diff options
79 files changed, 12272 insertions, 5866 deletions
diff --git a/gnu/usr.bin/cvs/FAQ b/gnu/usr.bin/cvs/FAQ index 38f8e15..84d4a3a 100644 --- a/gnu/usr.bin/cvs/FAQ +++ b/gnu/usr.bin/cvs/FAQ @@ -1,7 +1,9 @@ Archive-name: cvs-faq -$Revision: 1.1 $ <<== Include this in your comments -$Date: 1994/10/07 06:17:45 $ +Hand Revision: 3.5 <<== Include this in your comments +Last Updated: 1995/03/09 +$Revision: 1.4 $ +$Date: 1995/10/02 23:13:07 $ =========================================================================== == Frequently Asked Questions about CVS (The Concurrent Versions System) == @@ -15,15 +17,15 @@ $Date: 1994/10/07 06:17:45 $ Disclaimer: - Though every attempt has been made to ensure the veracity of the + Although an attempt has been made to ensure the veracity of the following material, no responsibility is assumed for any use, or for any consequences resulting from any use, of the information contained herein. No guarantee of suitability for any purpose - is offered or implied. Nothing in this document may be assumed + is offered or implied. Nothing in this document may be assumed to represent the employers of its contributors. I also might have slipped in a whopper or two to see if you are - paying attention. ;-) In other words, don't bet the house on + paying attention. ;-) In other words, don't bet the house on anything you read here unless you have checked it out yourself. @@ -34,11 +36,13 @@ $Date: 1994/10/07 06:17:45 $ to the author, who wrote all unattributed text: (Does it always feel strange to refer to oneself in the third person?) - David G. Grubbs <dgg@think.com> + David G. Grubbs <dgg@world.std.com> - To help readers of previous versions of this document, I will annotate - each question with a change marker + Major revisions contain enough alterations to render change markers + meaningless. (Major revisions are those with a final digit of '0', + such as 2.0 or 3.0.) To help readers of previous versions of this + document, minor revisions will be annotated: Change markers: Column 1 will contain a: @@ -48,28 +52,25 @@ $Date: 1994/10/07 06:17:45 $ '+' for a newly added Question and Answer. - The markers indicate significant changes in content between major - revision numbers. Trivial changes, such as question reordering or - spelling and grammar corrections are not marked. If I need to delete - a question, I'll move it to a "Deleted" section for a few revisions. - Deletions will arise when new versions of CVS are released. In the - long run, any question that can be answered by "get the latest - release" will be deleted. - - The minor revision number will change frequently. If a minor revision - change is large enough, I'll add a change marker. At major revision - changes the markers will be cleared and set again, based on the latest - minor revision of the previous major revision. - - + Trivial changes, such as question reordering or spelling and grammar + corrections are not marked. Deleted questions will simply disappear, + as will any question that can be answered by "get the latest release". Editorial comments are delimited by pairs of "[[" & "]]". They contain either references to the (usually unfinished) nature of the - FAQ entry itself or version-specific comments to be removed (or - altered) when new revisions of CVS are released. + FAQ entry itself, version-specific comments to be removed (or + altered) when new revisions of CVS are released or snide remarks from + the editor. + + If you plan to do anything with this document other than: + + - Read it. + - Redistribute the whole document along with the date and revision. + - Post sections as answers to CVS questions (as long as you + identify it as coming from the FAQ.) + + talk to the author first. - You may redistribute this as long as you don't take statements out - of context. Keep it together along with the revision number. ============================================ @@ -109,7 +110,7 @@ within each sub-section, though they are in no particular order. A. What is CVS? What's it for? Why CVS? B. Where do I find it? Where can I find Help? C. How does CVS differ from other similar software? - D. What do you mean by . . .? (Definitions) + D. What do you mean by . . .? (Definitions) 2. User Tasks A. Getting Started @@ -123,16 +124,18 @@ within each sub-section, though they are in no particular order. 4. Advanced Topics A. Installing CVS B. Setting up and Managing the Repository - C. Branching + C. Branching and Merging D. Tricks of the Trade - E. Weirdness + E. Internal errors F. Related Software - G. Other Systems + G. Engineering + H. Other Systems 5. Past & Future A. Contributors. B. Bugs and Patches C. Development + D. Professional Support 6. Table of Contents @@ -140,7 +143,8 @@ within each sub-section, though they are in no particular order. Final note: Except for the "Past & Future" section, all answers in this - document refer to the latest released version of CVS: 1.3. + document refer to CVS version 1.4. The latest released version is + 1.5. ============================================ @@ -156,9 +160,8 @@ Final note: 1A.1 What does CVS stand for? Can you describe it in one sentence? 1A.2 What is CVS for? What does it do for me? 1A.3 How does CVS work? -=1A.4 What is CVS useful for? -=1A.5 What is CVS *not* useful for? -=1A.6 Why isn't it called OSCO (Online Source COntrol)? + 1A.4 What is CVS useful for? + 1A.5 What is CVS *not* useful for? **** Answers: @@ -168,7 +171,7 @@ Final note: "CVS" is an acronym for the "Concurrent Versions System". CVS is a "Source Control" or "Revision Control" tool - designed to keep track of changes to files made by groups of + designed to keep track of source changes made by groups of developers working on the same files, allowing them to stay in sync with each other as each individual chooses. @@ -176,7 +179,7 @@ Final note: 1A.2 What is CVS for? What does it do for me? CVS is used to keep track of collections of files in a shared - directory, called "The Repository". Each collection of files + directory called "The Repository". Each collection of files can be given a "module" name, which is used to "checkout" that collection. @@ -193,21 +196,25 @@ Final note: 1A.3 How does CVS work? - CVS stores its files in a directory hierarchy, called the - Repository, which is separate from the user's working directory. + CVS saves its version-control information in RCS files stored in a + directory hierarchy, called the Repository, which is separate from + the user's working directory. Files in the Repository are stored in a format dictated by the RCS commands CVS uses to do much of its real work. RCS files are standard byte-stream files with an internal format described by keywords stored in the files themselves. - To begin work, you execute the "checkout" command, handing it - a module or directory you want to work on. CVS copies each file + To begin work, you execute a "checkout" command, handing it a + module name or directory path (relative to the $CVSROOT variable) + you want to work on. CVS copies the latest revision of each file in the specified module or directory out of the Repository and - into a sub-directory created in your current directory. + into a directory tree created in your current directory. You may + specify a particular branch to work on by symbolic name if you + don't want to work on the default (main or trunk) branch. - You may then modify files in the new sub-directory, building them - into output files and testing the results. When you want to make + You may then modify files in the new directory tree, build them + into output files and test the results. When you want to make your changes available to other developers, you "commit" them back into the Repository. @@ -219,37 +226,36 @@ Final note: require locks on the source files. At any time, usually at some milestone, you can "tag" the - committed files, producing a symbolic name that can be handed - to a future "checkout" command. A special form of "tag" - can produce a branch in development, as usually happens at - "release" time. + committed files, producing a symbolic name that can be handed to a + future "checkout" command. A special form of "tag" produces a + branch in development, as usually happens at "release" time. When you no longer plan to modify or refer to your local copy of the files, they can be removed. -=1A.4 What is CVS useful for? + 1A.4 What is CVS useful for? - CVS is intended to be useful for three major activities: + CVS is intended to handle source control for files in three major + situations: - 1. Multiple developers + 1. Multiple developers working on the same files. - The major advantage of using CVS over the older and simpler - tools like RCS or SCCS is that it allows multiple developers - to work on the same sources at the same time. + The major advantage of using CVS over the simpler tools like + RCS or SCCS is that it allows multiple developers to work on + the same sources at the same time. - The shared Repository provides a rendezvous point for - committed sources that allows developers a fair amount of - flexibility in how often to publish (via the "commit" - command) changes or include work committed by others (via the - "update" command). + The shared Repository provides a rendezvous for committed + sources that allows developers a fair amount of flexibility in + how often to publish (via the "commit" command) changes or + include work committed by others (via the "update" command). - 2. Vendor releases + 2. Tracking a stream of releases from a source vendor. - If you are making changes to a product distributed by someone - else, the CVS feature, called the Vendor Branch, allows you - to combine local modifications with vendor releases. + If you are making changes to sources distributed by someone + else, the CVS feature, called the Vendor Branch, allows you to + combine local modifications with repeated vendor releases. I have found this most useful when dealing with sources from three major classes of source vendor: @@ -259,46 +265,47 @@ Final note: b. Public Domain software which *always* requires work. - c. Pseudo-Public sources which require medium amounts of - work. (e.g. GNU programs, X, etc.) + c. Pseudo-Public sources which may require work. + (e.g. GNU programs, X, CVS itself, etc.) - 3. Branches + 3. Branching development. Aside from the "Vendor Branch", there are three kinds of "branches in development" that CVS can support: a. Your working directory can be treated as a private branch. - b. A Development branch can be shared by one or more developers. + b. A Development branch can be shared by one or more developers. c. At release time, a branch is usually created for bug fixes. (See 1D.9 and Section 4C for more info on branches.) - Although, at this writing, CVS's branch support is a bit - primitive, CVS was designed to allow you to create branches, - work on them for while and merge them back into the main - line of development. Arbitrary sharing and merging between - branches is not currently supported. + CVS's branch support is a bit primitive, but it was designed to + allow you to create branches, work on them for while and merge + them back into the main line of development. You should also + be able to merge work performed on the main branch into the + branch you are working on. Arbitrary sharing and merging + between branches is not currently supported. -=1A.5 What is CVS *not* useful for? + 1A.5 What is CVS *not* useful for? CVS is not a build system. Though the structure of your Repository and modules file - interact with your build system (e.g. Makefiles), they are - essentially independent. + interact with your build system (e.g. a tree of Makefiles), + they are essentially independent. CVS does not dictate how you build anything. It merely stores files for retrieval in a tree structure you devise. - CVS does not dictate how to use disk space in the checked - out working directories. If you write your Makefiles or - scripts in every directory so they have to know the relative - positions of everything else, you wind up requiring the entire - Repository to be checked out. That's simply bad planning. + CVS does not dictate how to use disk space in the checked out + working directories. If you require your Makefiles or build + procedures to know the relative positions of everything else, + you wind up requiring the entire Repository to be checked out. + That's simply bad planning. If you modularize your work, and construct a build system that will share files (via links, mounts, VPATH in Makefiles, @@ -309,16 +316,18 @@ Final note: issues involved. You must use your brain and a collection of other tools to provide a build scheme to match your plans. - Of course, you should place the tools created to support such - a build system (scripts, Makefiles, etc) under CVS. + Of course, you should use CVS to maintain the tools created to + support such a build system (scripts, Makefiles, etc). CVS is not a substitute for management. - Your managers and project leaders are expected to talk to - you frequently enough to make certain you are aware of - schedules, merge points, branch names and release dates. If - they don't, CVS can't help. + You and your project leaders are expected to plan what you are + doing. Everyone involved must be aware of schedules, merge + points, branch names, release dates and the range of + procedures needed to build products. (If you produce it and + someone else uses it, it is a product.) CVS can't cover for a + failure to manage your project. CVS is an instrument for making sources dance to your tune. But you are the piper and the composer. No instrument plays @@ -335,17 +344,16 @@ Final note: CVS cannot determine when simultaneous changes within a single file, or across a whole collection of files, will logically - conflict with one another. Its concept of a "conflict" is + conflict with one another. Its concept of a "conflict" is purely textual, arising when two changes to the same base file - are near enough to spook the merge (i.e. "diff3") command. - - CVS does not claim to help at all in figuring out non-textual - or distributed conflicts in program logic. + are near enough to spook the merge command into dropping + conflict markers into the merged file. - For example: Say you change the arguments to function X - defined in file A. At the same time, someone edits file B, - adding new calls to function X using the old arguments. You - are outside the realm of CVS's competence. + CVS is not capable of figuring out distributed conflicts in + program logic. For example, if you change the arguments to + function X defined in file A and, at the same time, edit file + B, adding new calls to function X using the old arguments. + You are outside the realm of CVS's competence. Acquire the habit of reading specs and talking to your peers. @@ -374,13 +382,6 @@ Final note: CVS provides only the first. -=1A.6 Why isn't it called OSCO (Online Source COntrol)? - - Better discount? CVS is shorter? (The international audience - requires an explanation: Both CVS and OSCO are the names of large - chains of Pharmacies (drug stores) in the U.S.) - - ---------------- -- Section 1B -- Where do I find CVS? Where can I find Help? @@ -390,75 +391,86 @@ Final note: 1B.1 How do I get more information about CVS? 1B.2 Is there an archive of CVS material? - 1B.3 How do I get a copy of the latest version of CVS? - 1B.4 Is there any other documentation? How about tutorials? - 1B.5 Is there a mailing list devoted to CVS? How do I get on it? - 1B.6 What prayers are appropriate for each of the major denominations - (e.g. 20's, 50's, 100's) when issuing complex CVS commands? -+1B.7 How do I get files out of the archive if I don't have FTP? + 1B.3 How do I get files out of the archive if I don't have FTP? + 1B.4 How do I get a copy of the latest version of CVS? + 1B.5 Is there a mailing list devoted to CVS? How do I find it? + 1B.6 What happened to the CVS Usenet newsgroup I heard about? **** Answers: -=1B.1 How do I get more information about CVS? - - 1. The first thing you should do is read the man page. + 1B.1 How do I get more information about CVS? - 2. Type "cvs -H" for general help or "cvs -H command" for - command-specific help. + 1. The first thing I would do is to read the Info file that comes + with the CVS sources under "doc". You can format and read the + cvs.texinfo file in two ways: 1. Use TeX to format it and a + "dvips" command to print it and 2. Install the cvs.info files + that are created by the Makefile and read them online using the + Emacs "info-mode" or a stand-alone "info" reader. - 3. Read the original CVS paper (in the source tree, under "doc"). - It describes the purpose of CVS and some of its workings. Note - that some of the emphasis (especially on multiple vendors - providing the same sources) is out of date. + 2. Then I'd run "cvsinit" to set up a Repository and read the man + page while trying out the commands. - 4. Read the man pages for RCS. + Type "cvs -H" for general help or "cvs -H command" for + command-specific help. - 5. Read the source code. + 3. For background, you can read the original CVS paper (in the + source tree, under "doc"). It describes the purpose of CVS and + some of how it was designed. Note that the emphasis of the + document (especially on multiple vendors providing the same + sources) is somewhat out of date. - 6. Look in the "doc" directory in the FTP archive described - below. + 4. For more detailed information about "internals", read the man + pages for RCS. If you are a programmer, you can also read the + source code to CVS. - 7. Read the gnu.cvs.info newsgroup. + 5. Other information and tutorials may be available in the "doc" + directory of the FTP archive described below. - 8. If you don't get the newsgroup, you can join the info-cvs - mailing list, described below. + 6. For current information, and a fair amount of detail, join the + info-cvs mailing list described below. 1B.2 Is there an archive of CVS material? - An anonymous FTP area has been set up. It contains many of the - CVS files you might want, including documentation, patches and - the latest release. + An anonymous FTP area has been set up. It contains many of the + CVS files you might want, including extra documentation, patches + and a copy of the latest release. - ftp think.com + ftp ftp.delos.com >>> User: anonymous >>> Passwd: <Your Internet address> cd /pub/cvs get README get Index - The README has more (and more up-to-date) information. The Index + The README has more (and more up-to-date) information. The Index contains a terse list of what is in the archive. + A WWW home page is also available at http://www.delos.com/cvs. -+1B.3 How do I get files out of the archive if I don't have FTP? + + 1B.3 How do I get files out of the archive if I don't have FTP? Use one of the FTP<->Email servers. These are the ones I've been told about: - 1. To use DEC's ftpmail service, type + 1. FTPMAIL service is available from the same host as the FTP + server described above. Send mail to "ftpmail@delos.com" + containing "help" in the body of the message. For example, + on most Unix systems, you can type: - echo 'send help' | mail ftpmail@decwrl.dec.com - - which will send you a message telling you how to use Email to - retrieve files from FTP archives. + echo help | Mail ftpmail@delos.com + The FTPMAIL server will respond with a document describing how + to use the server. If the "Mail" command doesn't exist on your + system, try "mailx", "/usr/ucb/mail" or "/bin/mail". - 2. If you are on BITNET, use Princeton's BITFTP server. Type - echo 'send help' | mail bitftp@pucc.princeton.edu + 2. If you are on BITNET, use Princeton's BITFTP server. Type + + echo 'send help' | Mail bitftp@pucc.princeton.edu (It is likely that only BITNET addresses can use this one.) @@ -466,6 +478,7 @@ Final note: 3. Other possibilities I've heard of from the net: (Try the one closest to you.) + ftpmail@decwrl.dec.com ftpmail@sunsite.unc.edu ftpmail@cs.arizona.edu ftpmail@cs.uow.edu.au @@ -476,78 +489,39 @@ Final note: The latest released version of CVS and all the programs it depends on should be available through anonymous FTP on any FSF - archive. The main FSF archive is at "prep.ai.mit.edu". There is - another archive at UUNET and other large Internet sites. + archive. The main FSF archive is at "prep.ai.mit.edu". There are + mirrors of the FSF archive on UUNET and other large Internet sites. - Program(s) Latest revision + Program(s) Suggested revision ----------- ----------------------- - CVS 1.3 - RCS 5.6.0.1 - GNU diff 2.3 + CVS 1.5 + RCS 5.7 (latest version available today) + GNU diff 2.7 (or later) [contained in diffutils-2.7] + GDBM 1.5 (or later) [optional] The GNU version of diff is suggested by both the RCS and CVS configuration instructions because it works better than the - standard version. If you plan to use dbm in the modules file, you - might also want to pick up the GNU dbm library. + standard version. It is a good idea not to accept the versions of CVS, RCS or diff you find lying on your system unless you have checked out their provenance. Using inconsistent collections of tools can cause you - more trouble than you probably want. + more trouble than you can probably afford. The FTP archive mentioned above should contain the latest official release of CVS, some official and unofficial patches and possibly complete patched versions of CVS in use somewhere. - 1B.5 Is there any other documentation? How about tutorials? - - Take a look at the "doc" sub-directory in the FTP archive. - You should find a growing collection of additional CVS - documentation there. - - Other sources: - - 1. Per Cederqvist's Texinfo manual. - - The latest version of the Texinfo manual written by Per - Cederqvist is included in the "doc" area mentioned - above. - - - 2. Gray Watson's cvs_tutorial. - - There is a version of this document in the "doc" area. - - 3. Apparently there is at least one project at O'Reilly (on - RCS/SCCS) that will include some info about CVS. - - - [[Anything else?]] - - -#1B.6 Is there a mailing list or Usenet newsgroup devoted to CVS? - How do I find them? + 1B.5 Is there a mailing list devoted to CVS? How do I find it? An Internet mailing list named "info-cvs" grew out of the private - mailing list used by the CVS 1.3 alpha testers in early 1992. An - associated Usenet newsgroup named "gnu.cvs.info" was created in - August, 1993. - - The newsgroup and the mailing list are bidirectionally gatewayed, - meaning that you only need access to one of them. Anything sent - to the mailing list will be automatically posted to "gnu.cvs.info" - and anything posted to the newsgroup will be automatically mailed - to "info-cvs". - - First try the newsgroup, since it is generally easier to read (and - manage) than a mailing list. Ask your system administrator - whether you get the "gnu" hierarchy. If so, select a newsreader - and dive in. - - If you don't get any form of Usenet News (or don't get the "gnu" - hierarchy), you can add yourself to the mailing list by sending an - Email message to: + mailing list used by the CVS 1.3 alpha testers in early 1992. + Throughout 1994, the list received an average of 100 messages per + month. + + You can add yourself to the mailing list by sending an Email + message to: info-cvs-request@prep.ai.mit.edu @@ -558,58 +532,69 @@ Final note: info-cvs@prep.ai.mit.edu - An archive of the mailing list and the newsgroup might appear - someday in the CVS FTP archive. + An archive of the mailing list is maintained in the FTP archive + mentioned above. + + + 1B.6 What happened to the CVS Usenet newsgroup I heard about? - 1B.7 What prayers are appropriate for each of the major denominations - (e.g. 20's, 50's, 100's) when issuing complex CVS commands? + A Usenet newsgroup named "gnu.cvs.info" was announced in April + 1993, with an expected creation date of August, 1993. - Only what the traffic will allow, in small, unmarked bills - delivered to me, Ralph Icebag, in a plain brown wrapper, by a - brown-shoed square, in the dead of night. (Apologies to - Firesign Theater.) + As of this writing (October, 1994) it hasn't appeared. + If the newsgroup is ever created, it and the mailing list should + be bidirectionally gatewayed, meaning that you only need access to + one of them. Anything sent to the mailing list would be + automatically posted to "gnu.cvs.info" and anything posted to the + newsgroup would be automatically mailed to "info-cvs". + + A newsgroup would be easier to use than a mailing list. If the + CVS newsgroup ever shows up, ask your system administrator whether + you get the "gnu" hierarchy. If so, select a news reader and dive + in. ---------------- --- Section 1C -- How does CVS differ from other similar software? +-- Section 1C -- How does CVS differ from other, similar software? ---------------- This section attempts to list programs purporting to cover some of the -same territory as CVS. [[These are very sparsely documented here. If you +same territory as CVS. [[These are very sparsely documented here. If you know something about one of these tools, how about trying to flesh out an entry or two?]] **** Questions: -=1C.1 How does CVS differ from RCS? + 1C.1 How does CVS differ from RCS? 1C.2 How does CVS differ from SCCS? -=1C.3 How does CVS differ from ClearCase? -=1C.4 How does CVS differ from TeamWare? - 1C.5 How does CVS differ from SunPro? - 1C.6 How does CVS differ from Aegis? - 1C.7 How does CVS differ from Shapetools? -+1C.8 How does CVS differ from TeamNet? -+1C.9 How does CVS differ from ProFrame? -+1C.10 How does CVS differ from CaseWare/CM? -+1C.11 How does CVS differ from Sublime? + 1C.3 How does CVS differ from ClearCase? +#1C.4 How does CVS differ from TeamWare/SparcWorks? + 1C.5 How does CVS differ from Aegis? + 1C.6 How does CVS differ from Shapetools? + 1C.7 How does CVS differ from TeamNet? + 1C.8 How does CVS differ from ProFrame? + 1C.9 How does CVS differ from CaseWare/CM? + 1C.10 How does CVS differ from Sublime? + 1C.11 How does CVS differ from PVCS? + 1C.12 How does CVS differ from CMVC? **** Answers: -=1C.1 How does CVS differ from RCS? + 1C.1 How does CVS differ from RCS? CVS uses RCS to do much of its work and absolutely all the work of changing the underlying RCS files in the Repository. RCS comprises a set of programs designed to keep track of changes to individual files. Of course, it also allows you to refer to - whole sets of files on the command line, but groups are - manipulated by iterating over those files. There is no pretense - of combined interaction between the files. + multiple files on the command line, but they are handled by + iterating over individual files. There is no pretense of + coordinated interaction among groups of files. CVS's main intent is to provide a set of grouping functions that allow you to treat a collection of RCS files as a single object. @@ -637,14 +622,13 @@ entry or two?]] 3. Serialization of commits. CVS requires you to merge all changes committed (via "update") since you checked out - your working copy of the file. Although it is still + your working copy of the file. Although it is still possible to commit a file filled with old data, it is less likely than when using raw RCS. 4. Relatively easy merging of releases from external Vendors. - 1C.2 How does CVS differ from SCCS? SCCS is much closer to RCS than to CVS, so some of the previous @@ -656,52 +640,56 @@ entry or two?]] [[More info here?]] -=1C.3 How does CVS differ from ClearCase? - - ClearCase is a client-server CASE tool for version management, - configuration management, and process management. ClearCase - is an evolution of the popular DSEE tools, formerly available - on HP/Apollo platforms. ClearCase includes an X/Motif GUI, - command-line interface, and C programmer API, and is currently - available on Sun, HP, and SGI platforms. - - ClearCase uses a special Unix filesystem type, called "mfs" - for "multi-version file system". Conceptually, mfs adds - another dimension to the regular Unix filesystem. The new - axis is used to store the different versions of files. Each - user makes a "view" into the file database by creating a - special mfs mountpoint on their machine. Each view has a set - of flexible selection rules that specify the particular - version of each file to make visible in that view. You can - think of a "view" as a workarea in CVS, except that the files - don't really exist on your local disk until you modify them. - This type of filesystem is sometimes called "copy-on-write" - and conserves disk space for files that are read-only. - Another advantage is that a view is "tranparent" in the sense - that all of the files in a "view" appear to be regular Unix - files to other tools and Unix system calls. An extended - naming convention allows access to particular versions of a - file directly: "test.cc@@/main/bugfix/3" identifies the third - version of test.c on the bugfix branch. - - ClearCase supports both the copy-modify-merge model of CVS and - the checkin/checkout development model with file locking. - Directories are versionable objects as well as files. A - graphical n-way merge tool is provided. Like CVS, ClearCase - supports branches, symbolic tags, and delta compression. - ASCII as well as binary files are supported, and converters - from RCS, SCCS, DSEE formats are also included. + 1C.3 How does CVS differ from ClearCase? + + ClearCase is a distributed client-server version control system. + ClearCase is a variant DSEE tools, formerly available on Apollo + platforms. The ClearCase tool set includes a few X-based + interface tools, a command-line interface, and C programmer API. + It is currently available on Sun, HP, SGI and OSF/1 platforms. + + ClearCase uses a special Unix filesystem type, called "mvfs" + for "multi-version file system". Conceptually, mvfs adds + another dimension to a regular Unix filesystem. The new + axis is used to store the different versions of files and to + provide a tree-hierarchical view of a collection of objects that + might be scattered across any number of separate hosts on your + local network. + + Each user acquires a "view" into the file database by creating a + special mvfs mount point on their machine. Each view has a + "configuration spec" containing a set of selection rules that + specify the particular version of each file to make visible in + that view. You can think of a "view" as a work area in CVS, except + that the files don't really exist on your local disk until you + modify them. This technique conserves disk space because it + doesn't keep private copies of read-only files. + + Another advantage is that a view is "transparent" in the sense that + all of the files in a "view" appear to be regular Unix files to + other tools and Unix system calls. An extended naming convention + allows access to particular versions of a file directly: + "test.cc@@/main/bugfix/3" identifies the third version of test.c + on the bugfix branch. + + ClearCase supports both the copy-modify-merge model of CVS (by + using what are called "unreserved checkouts" and the + checkin/checkout development model with file locking. Directories + are version-controlled objects as well as files. A graphical merge + tool is provided. Like RCS, ClearCase supports branches, symbolic + tags, and delta compression. ASCII as well as binary files are + supported, and converters from RCS, SCCS, DSEE formats are also + included. A make-compatible build facility is provided that can identify common object code and share it among developers. A build auditing feature automatically records file dependencies by tracking every file that is opened when producing a derived - object, thus making explicit Makefiles unnecessary. Pre- and - post-event triggers are available for most ClearCase - operations to invoke user programs or shell scripts. - User-defined attributes can be assigned to any version or - object. Hyperlinks between versioned objects can record their - relationship. + object, thus making explicit dependency lists unnecessary. Pre- + and post-event triggers are available for most ClearCase + operations to invoke user programs or shell scripts. User-defined + attributes can be assigned to any version or object. Hyper-links + between version controlled objects can record their relationship. For more information, contact: @@ -713,14 +701,38 @@ entry or two?]] (508) 650-1193 (phone) (508) 650-1196 (fax) - Contributed by Steve Turner - [extracted from the ClearCase 1.1.1 documentation] + Originally contributed by Steve Turner + Edited by the author of this FAQ. + +#1C.4 How does CVS differ from TeamWare/SparcWorks? -=1C.4 How does CVS differ from TeamWare? + TeamWare is a configuration management tool from Sun Microsystems, + a part of SparcWorks. It uses the same copy and merge model as + CVS. The central abstraction is a workspace, which corresponds to + either a CVS branch or a checked out module. TeamWare allows you + to manipulate workspaces directly, including moving and merging + code between workspaces. You can put your workspace on tape and + continue to work with it at home, just like you can with CVS. + TeamWare is built upon and compatible with SCCS. + + TeamWare provides both a command line interface and a graphical + interface. The CodeManager tool will display the project as a + tree of workspaces, and allows you to manipulate them with drag + and drop. The other tools are VersionTool that displays and + manipulates a dag with a version history of a single file, + CheckPoint that will create symbolic tags, MakeTool, a make + compatible tool with a GUI, and FileMerge which will interactively + merge files when needed (like emerge for emacs). If you have a + sun, you can try /usr/old/mergetool for an old SunView version of + FileMerge. + + Email: sunprosig@sun.com + + Originally extracted from TeamWare + Marketing literature by Per Abrahamsen. + Edited by the author of this FAQ. - TeamWare is a configuration management tool from Sun - Microsystems. For more information, contact: @@ -730,14 +742,7 @@ entry or two?]] (800)873-7869 - 1C.5 How does CVS differ from SunPro? - - SunPro is advertised as the successor to "SCCS". - - [[Need more info here.]] - - - 1C.6 How does CVS differ from Aegis? + 1C.5 How does CVS differ from Aegis? Aegis appears to be a policy-setting tool that allows you to use other sub-programs (make, RCS, etc.) to implement pieces of the @@ -751,17 +756,17 @@ entry or two?]] [[Need more info here.]] - 1C.7 How does CVS differ from Shapetools? + 1C.6 How does CVS differ from Shapetools? Shapetools includes a build mechanism (called Shape, not surprisingly) that is aware of the version mechanism, and some dependency tracking. It is based on a file system extension called Attributed File System, which allows arbitrary-sized - "attributes" to be associated with a file. Files are versioned in - a manner similar to RCS. Configurations are managed through the - Shapefile, an extension of the Makefile syntax and functionality. - Shape includes version selection rules to allow sophisticated - selection of component versions in a build. + "attributes" to be associated with a file. Files are version + controlled in a manner similar to RCS. Configurations are managed + through the Shapefile, an extension of the Makefile syntax and + functionality. Shape includes version selection rules to allow + sophisticated selection of component versions in a build. Shapetools' concurrency control is pessimistic, in contrast to that of CVS. Also, there's very limited support for branching and @@ -771,7 +776,7 @@ entry or two?]] Contributed by Don Dwiggins -+1C.8 How does CVS differ from TeamNet? + 1C.7 How does CVS differ from TeamNet? TeamNet is a configuration management tool from TeamOne. @@ -785,7 +790,7 @@ entry or two?]] Contributed by Steve Turner -+1C.9 How does CVS differ from ProFrame? + 1C.8 How does CVS differ from ProFrame? ProFrame is a new system integration framework from IBM. ProFrame is compliant with the CFI (CAD Framework Initiative) @@ -800,13 +805,13 @@ entry or two?]] The Design Data Manager(2) is probably the appropriate component to compare to CVS. The Design Data Manager provides version control with checkin/checkout capability, - configuration management, and data dependency tracking. A - graphical data selection interface is provided. Using this + configuration management, and data dependency tracking. A + graphical data selection interface is provided. Using this interface, you may create and manipulate objects and hierarchy structures, view the revision history for an object, and view and assign attributes to a design object. - The ProFrame server currently runs only on RS600, but clients + The ProFrame server currently runs only on RS6000, but clients may be a wide variety of Unix platforms. Contact IBM for the latest platform information. @@ -820,10 +825,10 @@ entry or two?]] Contributed by Steve Turner - [extracted from the ProFrame 1.1.0 datasheet] + [extracted from the ProFrame 1.1.0 datasheet] -+1C.10 How does CVS differ from CaseWare/CM? + 1C.9 How does CVS differ from CaseWare/CM? CaseWare/CM is a software configuration management product from CaseWare, Inc. CaseWare/CM may be customized to support @@ -831,9 +836,9 @@ entry or two?]] the software lifecycle, and different access rights for users. A GUI is provided to view version histories and - configurations. A merge tools is also included. CaseWare + configurations. A merge tools is also included. CaseWare supports type-specific lifecycles, which allows different types - of files to move through different lifecycles. Also provided + of files to move through different lifecycles. Also provided is a build facility to support automatic dependency analysis, parallel, distributed, and remote builds, and variant releases. @@ -847,7 +852,7 @@ entry or two?]] Multiple vendors and operating systems are supported. For more information, contact: - + CaseWare, Inc. 108 Pacifica, 2nd Floor Irvine, CA 92718-3332 @@ -858,102 +863,184 @@ entry or two?]] [extracted from the CaseWare/CM data sheet] -+1C.11 How does CVS differ from Sublime? + 1C.10 How does CVS differ from Sublime? Produced by AT&T. [[Need more info here.]] + 1C.11 How does CVS differ from PVCS? + + PVCS works on single files like RCS and SCCS, CVS works on + complete subsystems. PVCS has a make utility (called a + configuration builder), CVS does not. PVCS has a GUI interface + for Unix, DOS, OS/2, and MS Windows. + + Intersolv, Inc. + 1700 NW 167th Place + OR 97006 + + Contributed by Per Abrahamsen + [Extracted from Intersolv Marketing literature.] + + + 1C.12 How does CVS differ from CMVC? + + CMVC is an IBM Configuration Management and Version Control + system. (Though I'm not certain that's the right acronym + expansion.) It runs on Suns, HPs, RS6000s, OS/2 and Windows. + + Other than revision control, it apparently has features to manage + releases, bug tracking and the connection between alterations and + reported bugs and feature requests. It is a client/server system, + based on a choice of commercial Relational Database systems, and + it provides a Motif or command line interface. + + Unlike CVS, it uses a strict locking protocol to serialize source + code alterations. + + ---------------- -- Section 1D -- What do you mean by . . .? (Definitions) ---------------- **** Questions: -#1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? + 1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? 1D.2 What is an RCS file? 1D.3 What is a working file? 1D.4 What is a working directory (or working area)? 1D.5 What is "checking out"? -=1D.6 What is a revision? + 1D.6 What is a revision? 1D.7 What is a "Tag"? -=1D.8 What are "HEAD" and "BASE"? -=1D.9 What is a Branch? -=1D.10 What is "the trunk"? -=1D.11 What is a module? -+1D.12 What does "merge" mean? + 1D.8 What are "HEAD" and "BASE"? + 1D.9 What is a Branch? + 1D.10 What is "the trunk"? + 1D.11 What is a module? + 1D.12 What does "merge" mean? **** Answers: -#1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? + 1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? The Repository is a directory tree containing the CVS administrative files and all the RCS files that constitute - "imported" or "committed" work. The Repository is kept in a + "imported" or "committed" work. The Repository is kept in a shared area, separate from the working areas of all developers. Users of CVS must set their "CVSROOT" environment variable to the absolute pathname of the head of the Repository. Most command line interpreters replace an instance of "$CVSROOT" with the value - of the "CVSROOT" environment variable. By analogy, in this + of the "CVSROOT" environment variable. By analogy, in this document "$CVSROOT" is used as shorthand for "the absolute pathname of the directory at the head of the Repository". One of the things found in $CVSROOT is a directory named CVSROOT. It contains all the "state", the administrative files, that CVS - needs during execution. The "modules", "history", "commitinfo", - "loginfo" and other files can be found there. + needs during execution. The "modules", "history", "commitinfo", + "loginfo" and other files can be found there. See 4B.2 for more + information about CVSROOT files. 1D.2 What is an RCS file? - A file, usually ending in ",v", containing the source text and - the revision history for all committed revisions of a source - file. It is stored separately from the working files, in a - directory hierarchy, called the Repository. + An RCS file is a text file containing the source text and the + revision history for all committed revisions of a source file. It + is stored separately from the working files, in a directory + hierarchy, called the Repository. RCS is the "Revision Control System" that CVS uses to manage - individual files. + individual files. RCS file names normally end in ",v", but + that can be altered (via the RCS -x option) to conform to file + naming standards on platforms with unusual filename limitations. 1D.3 What is a working file? - A disk file containing a checked-out copy of a source file that - earlier had been placed under CVS. If the working file has been - edited, the changes since the last committed revision are - invisible to other users of CVS. + A working file is a disk file containing a checked-out copy of a + source file that earlier had been placed under CVS. If the + working file has been edited, the changes since the last committed + revision are invisible to other users of CVS. 1D.4 What is a working directory (or working area)? - The "checkout" command creates a tree of working directories, - filling them with working files. A working directory always - contains a sub-directory named ./CVS containing information - about working files and the location of the directory within the - Repository that was used to create the working directory. - A working directory is the place where you work and the place from which you "commit" files. + The "checkout" command creates a tree of working directories, + filling them with working files. Each working directory contains + a sub-directory named ./CVS containing three administrative files, + which are created by "checkout" and are always present: + + ./CVS/Entries + contains information about working files. + + ./CVS/Repository + contains the location of the directory within the + Repository that was used to create the working directory. + + ./CVS/Root + contains the value of $CVSROOT at the time you created + the working directory. + + Other files may also appear in ./CVS depending on the state of + your working directory: + + ./CVS/Tag + contains the "sticky tag" associated with the whole + directory. See 3A.2 for its main purpose. + [Created by "checkout" or "update" when using "-r <tag>".] + [Deleted by "checkout" or "update" when using '-A'.] + + ./CVS/Entries.Static + contains a fixed list of working files. If this file + exists, an "update" doesn't automatically bring newly + added files out of the Repository. + [Created and maintained by hand.] + + ./CVS/Checkin.prog + contains a program to run whenever anything in the + working directory is committed. + [Created by checkout if "-i <prog>" appears in the + modules file for the checked-out module.] + + ./CVS/Update.prog + contains a program to run whenever anything in the + working directory is updated. + [Created by checkout if "-u <prog>" appears in the + modules file for the checked-out module.] + + ./CVS/<file>,p + ./CVS/<file>,t + contain (possibly zero-length) state information about an + "add" that has not been committed. + [Created by "add".] + [Deleted by "commit" or "remove".] + 1D.5 What is "checking out"? "Checking out" is the act of using the "checkout" command to copy a particular revision from a set of RCS files into your - working area. See the "checkout" command in Section 3C. + working area. You normally execute "checkout" only once per + working directory (or tree of working directories), maintaining + them thereafter with the "update" command. + See section 3C on the "checkout" command. -=1D.6 What is a revision? - A "revision" is a version of a file that was "committed" (or - "checked in", in RCS terms) some time in the past. CVS (and + 1D.6 What is a revision? + + A "revision" is a version of a file that was "committed" + ("checked in", in RCS terms) some time in the past. CVS (and RCS) can retrieve any file that was committed by specifying its - revision number or its "tag" (or symbolic name, in RCS terms). + revision number or its "tag" ("symbolic name", in RCS terms). - In CVS, a "tag" is more useful than a revision number. It usually + In CVS, a "tag" is more useful than a revision number. It usually marks a milestone in development represented by different revision numbers in different files, all available as one "tagged" collection. @@ -970,10 +1057,14 @@ entry or two?]] places the same "Tag" on all files in a working directory, allowing you to retrieve those files by name in the future. + The CVS "Tag" is implemented by applying RCS "symbols" to each + individual file. The Tags on a file (or collection of files) may + be displayed using the "log" command. + -=1D.8 What are "HEAD" and "BASE"? + 1D.8 What are "HEAD" and "BASE"? - BASE and HEAD are built-in tags that don't show up in the "log" + HEAD and BASE are built-in tags that don't show up in the "log" or "status" listings. They are interpreted directly by CVS. "HEAD" refers to the latest revision on the current branch in the @@ -986,7 +1077,7 @@ entry or two?]] your working file, "BASE" is the committed revision matching it. Most of the time BASE and HEAD refer to the same revision. They - become different for two reasons: + can become different in two ways: 1. Someone else changed HEAD by committing a new revision of your file to the Repository. You can pull BASE up to equal HEAD by @@ -996,20 +1087,20 @@ entry or two?]] with the option "-r <rev/tag>" or "-D <date>". CVS records a sticky tag and moves your files to the specified earlier revision. You can clear the sticky tag and pull BASE up to - equal HEAD by executing "update -A". + equal HEAD again by executing "update -A". -=1D.9 What is a Branch? + 1D.9 What is a Branch? - Any mechanism that allows one or more developers to modify a - physically separate copy of a file without affecting anyone other - than those working on the branch. + In general, a branch is any mechanism that allows one or more + developers to modify a file without affecting anyone other than + those working on the same branch. - There are four kinds of branches CVS deals with: + There are four kinds of "branch" CVS can manage: 1. The Vendor Branch. - A single vendor branch is supported. The "import" command + A single vendor branch is supported. The "import" command takes a sequence of releases from a source code vendor (called a "vendor" even if no money is involved), placing them on a special "Vendor" branch. The Vendor branch is considered part @@ -1039,8 +1130,9 @@ entry or two?]] You can also create a private branch of this type, allowing an individual to commit (and tag) intermediate revisions without changing the Main line. It should be managed exactly like a - Development Branch -- collapsed into the Main line and - forgotten when the work is done. + Development Branch -- collapsed into the Main line (or its + parent branch, if that is not the Main Branch) and forgotten + when the work is done. 4. A Release branch. @@ -1055,12 +1147,12 @@ entry or two?]] the release. Although the internal format of this type of branch (branch tag - and RCS branches) is the same as in a development branch, the + and RCS branches) is the same as in a development branch, its purpose and the way it is managed are different. The major - difference is that the branch is Permanent. Once you let a - release out the door to customers, or to the next stage of - whatever process you are using, you should retain forever the - branch marking the release. + difference is that a Release branch is normally Permanent. + Once you let a release out the door to customers, or to the + next stage of whatever process you are using, you should retain + forever the branch marking that release. Since the branch is permanent, you cannot incorporate the branch fixes into the Main line by "collapsing" (merging and @@ -1076,7 +1168,7 @@ entry or two?]] See 1D.12 (merges) and Section 4C, on Branching for more info. -=1D.10 What is "the trunk"? + 1D.10 What is "the trunk"? Another name for the RCS Main Branch. The RCS Main Branch is related, but not equivalent, to both the CVS Main branch and what @@ -1084,7 +1176,7 @@ entry or two?]] See 3H.3 and Section 4C on Branching. -=1D.11 What is a module? + 1D.11 What is a module? In essence, a module is a name you hand to the "checkout" command to retrieve one or more files to work on. It was originally @@ -1116,7 +1208,7 @@ entry or two?]] Example: - emacs gnu/emacs + emacs gnu/emacs 2. A name for a subset of the files within such a directory. @@ -1125,11 +1217,12 @@ entry or two?]] ls unix/bin Makefile ls.c + The 2nd through Nth strings in the above can be files, + directories or module substitutions. No relative paths. - The 2nd through Nth strings in the above must be *files*. - No directories, no relative pathnames. To checkout more - than one directory by a single name, use an alias as - described in #5 below. + A module substitution occurs when you use a '&module-name' + reference. The module-name referred to is logically + substituted for the '&module-name' string. 3. A relative pathname to a directory within the Repository @@ -1138,12 +1231,13 @@ entry or two?]] Example: - gnu/emacs -o /emacs.helper gnu/emacs + gnu/emacs -o /bin/emacs.helper gnu/emacs - The files checked out are exactly the same as the files you - would get if the path weren't even in the modules file. The - only reason to put this kind of relative pathname into the - modules file is to hook one of the helper functions onto it. + The files checked out are exactly the same as the files + "checkout" would retrieve if the path weren't even in the + modules file. The only reason to put this kind of relative + pathname into the modules file is to hook one of the helper + functions onto it. 4. A relative pathname to a single file within the Repository @@ -1153,7 +1247,7 @@ entry or two?]] Example: - gnu/emacs/Makefile -o /emacs.helper gnu/emacs Makefile + gnu/emacs/Makefile -o /bin/emacs.helper gnu/emacs Makefile The file checked out is the same as what you would get if you handed the relative pathname to the "checkout" command. @@ -1162,11 +1256,15 @@ entry or two?]] 5. An alias consisting of a list of any of the above, including - other aliases. + other aliases, plus exceptions. Example: - my_work -a emacs gnu/bison unix/bin/ls.c + my_work -a emacs !emacs/tests gnu/bison unix/bin/ls.c + + + The exception "!emacs/test" above is functionally equivalent + to specifying "!emacs/tests" on the "checkout" command line. Another way to look at it is that the modules file is simply @@ -1174,51 +1272,20 @@ entry or two?]] structure provides another. You should use whatever turns out to be simplest for your development group. - - As an example, say you want to keep track of three programs, and - want to be allowed to check out any combination of one, two or all - three at a time. Here are most of the combinations I can think - of. Experiment and choose what you want -- you won't need every - possibility. - - - # All directories in "world", even ones added later. - world world - - # All three programs by name. They checkout into local dir. - prog123 -a prog1 prog2 prog3 - - # All three programs by name. They checkout into "world" subdir. - wprog123 -a wprog1 wprog2 wprog3 - - # Individual progs checkout into dirs named "prog1", etc. - prog1 world/prog1 - prog2 world/prog2 - prog3 world/prog3 - - # Individual progs checkout into dirs named "world/prog1", etc. - wprog1 -a world/prog1 - wprog2 -a world/prog2 - wprog3 -a world/prog3 - - # Pairs that checkout into local dir. - prog12 -a prog1 prog2 - prog13 -a prog1 prog3 - prog23 -a prog2 prog3 - - # Pairs that checkout into world subdir. - # Instead of using the wprog aliases, we could use "world/prog9" - wprog12 -a wprog1 wprog2 - wprog13 -a wprog1 wprog3 - wprog23 -a wprog2 wprog3 - + See 4G.2 for some specific ideas about how to use the modules file. + -+1D.12 What does "merge" mean? + 1D.12 What does "merge" mean? A merge is a way of combining changes made in two independent - copies of the same "base" file. There are always three files - involved in a merge: the original, or "base", file and two - copies of that base file modified in different ways. + copies of a common starting file. Checking out an RCS revision + produces a file, so for the purposes of a merge "file" and + "revision" are equivalent. So, we can say there are always three + "files" involved in a merge: + + 1. The original, starting, "base" or "branch point" file. + 2. A copy of the base file modified in one way. + 3. Another copy of the base file modified in a different way. Humans aren't very good at handling three things at once, so the terminology dealing with merges can become strained. One way to @@ -1234,22 +1301,24 @@ entry or two?]] by others into your working file. In this case, the three files involved in the merge are: - Base: The revision you originally checked out. - Later: A revision committed onto the current branch + Base: The revision you originally checked out. + Later: A revision committed onto the current branch after you checked out the Base revision. Working: Your working file. The one lying in the working directory containing changes you have made. - 2. The "update -j <branch_tag>" command merges a whole branch into - your working file, which is presumed to be on the Main line of - development. + 2. The "update -j <branch_tag> {optional files}" command merges + changes made on the given branch into your working files, which + is presumed to be on the Main line of development. See 4C.6 - 3. The "update -j <rev> -j <rev>" merges the difference between - two specific revisions on some other branch (though the two - revisions are usually on the same branch) into your working - directory. + 3. The "update -j <rev> -j <rev> {optional files}" command merges + the difference between two specified revisions into files in + your working directory. The two revisions <rev> are usually on + the same branch and, when updating multiple files, they are + most useful when they are Tag names rather than numeric + revisions. See 4C.7 @@ -1268,7 +1337,7 @@ entry or two?]] 2A.1 What is the first thing I have to know? 2A.2 Where do I work? -=2A.3 What does CVS use from my environment? + 2A.3 What does CVS use from my environment? 2A.4 OK, I've been told that CVS is set up, my module is named "ralph" and I have to start editing. What do I type? 2A.5 I have been using RCS for a while. Can I convert to CVS without @@ -1279,10 +1348,10 @@ entry or two?]] 2A.1 What is the first thing I have to know? - Your organization has assigned one or more persons to understand, - baby-sit and administer both the CVS programs and the data - Repository. I call these persons Repository Administrators. - They will have set up a Repository and "imported" files into it. + Your organization has most likely assigned one or more persons to + understand, baby-sit and administer the CVS programs and the data + Repository. I call these persons Repository Administrators. They + should have set up a Repository and "imported" files into it. If you don't believe anyone has this responsibility, or you are just testing CVS, then *you* are the Repository Administrator. @@ -1295,12 +1364,12 @@ entry or two?]] If you *are* the Repository Administrator, you will want to read everything you can get your hands on, including this FAQ. Source control issues can be difficult, especially when you get to - branches and release planning. Expect to feel stupid for a few + branches and release planning. Expect to feel stupid for a few days/weeks. No tool in the universe avoids the need for intelligent organization. In other words, there are all sorts of related - issues you will probably have to learn. Don't expect to dive in + issues you will probably have to learn. Don't expect to dive in without any preparation, stuff your 300 Megabytes of sources into CVS and expect to start working. If you don't prepare first, you will probably spend a few sleepless nights. @@ -1317,9 +1386,9 @@ entry or two?]] Ask your peers. -=2A.3 What does CVS use from my environment? + 2A.3 What does CVS use from my environment? - You must set two environment variables. Some shells share these + You must set two environment variables. Some shells share these variables with local shell variables using a different syntax. You'll have to learn how your shell handles them. @@ -1328,11 +1397,13 @@ entry or two?]] CVSROOT Absolute pathname of the head of your Repository. PATH Normally set to a list of ':'-separated directory - pathnames searched to find executables. You must + pathnames searched to find executables. You must make sure "cvs" is in one of the directories. - If your CVS installation set the RCSBIN directory - to null (""), then the RCS commands also must be - somewhere in your PATH. + + If your CVS was built with the RCSBIN directory set + to null (""), and you don't set the RCSBIN + variable mentioned below, then the RCS commands + also must be somewhere in your PATH. Optional variables: (Used if set, but ignored otherwise.) @@ -1343,8 +1414,6 @@ entry or two?]] program. You'll be kicked into your editor to supply revision comments if you don't specify them via -m "Log message" on the command line. - [Note: This is not in 1.3 -- It should appear in - the next release.] EDITOR Used if CVSEDITOR doesn't exist. If EDITOR doesn't exist, CVS uses a configured constant, @@ -1369,10 +1438,16 @@ entry or two?]] Otherwise LOGNAME/USER/getuid() are used to find your home directory from the passwd file. + TMPDIR Used during import. It might also be used if your + platform's version of mktemp(3) is unusual, or + you have changed the source to use tmpnam(3). + + 2A.4 OK, I've been told that CVS is set up, my module is named "ralph" and I have to start editing. What do I type? + cd <where you have some space to work> cvs checkout ralph cd ralph @@ -1383,7 +1458,7 @@ entry or two?]] losing my revision history? How about converting from SCCS? If you are asking such questions, you are not a mere user of CVS, - but one of its Administrators! You should take a look at Section + but one of its Administrators! You should take a look at Section 4A, "Installing CVS" and Section 4B, "Setting up and Managing the Repository". @@ -1395,7 +1470,7 @@ entry or two?]] What I consider a "common user task" generally involves combinations of the following commands: - add, checkout, commit, diff, log, status, tag, update + checkout, update, commit, diff, log, status, tag, add Conventions in this section: @@ -1418,12 +1493,12 @@ Conventions in this section: **** Questions: -#2B.1 What is the absolute minimum I have to do to edit a file? -=2B.2 If I edit multiple files, must I type "commit" for each one? - 2B.3 How do I get rid of the directory that "checkout" created? -=2B.4 How do I find out what has changed? -=2B.5 I just created a new file. How do I add it to the Repository? -=2B.6 How do I merge changes made by others into my working directory? + 2B.1 What is the absolute minimum I have to do to edit a file? + 2B.2 If I edit multiple files, must I type "commit" for each one? + 2B.3 How do I get rid of the <module> directory that "checkout" created? + 2B.4 How do I find out what has changed since my last update? + 2B.5 I just created a new file. How do I add it to the Repository? + 2B.6 How do I merge changes made by others into my working directory? 2B.7 How do I label a set of revisions so I can retrieve them later? 2B.8 How do I checkout an old release of a module, directory or file? 2B.9 What do I have to remember to do periodically? @@ -1432,54 +1507,62 @@ Conventions in this section: **** Answers: -#2B.1 What is the absolute minimum I have to do to edit a file? + 2B.1 What is the absolute minimum I have to do to edit a file? Tell your Repository Administrator to create a module covering the - directory or files you care about. You'll find out that the - module name is named <module>. Then type: + directory or files you care about. You will be told that your + module name is <module>. Then type: cvs checkout <module> cd <module> - emacs <file> # Isn't Emacs a synonym for editor? + emacs <file> # Isn't Emacs a synonym for edit? cvs commit <file> If you don't use modules (in my opinion, a mistake), you can check out a directory by substituting its relative path within the Repository for <module> in the example above. - To check out a single file, you'll have to change the "cd - <module>" to "cd to the parent of the file named in <module>". + To work on a single file, you'll have to change "cd <module>" to + "cd `dirname <module>`". -=2B.2 If I edit multiple files, must I type "commit" for each one? + 2B.2 If I edit multiple files, must I type "commit" for each one? - No. You can do them all at once by name or by directory. - See 3D.2. + No. You can commit a list of files and directories, including + relative paths into multiple directories. You can also commit + every modified file in the current directory or in all directories + and subdirectories from your current directory downward. See 3D.2. - 2B.3 How do I get rid of the directory that "checkout" created? + 2B.3 How do I get rid of the <module> directory that "checkout" created? Change your directory to be the same as when you executed the - "checkout" command and type: + "checkout" command that created <module>. - cvs release -d <module> + If you want to get rid of the CVS control information, but leave + the files and directories, type: + + cvs release <module> + If you want to obliterate the entire directory, type: - The current version of CVS does not detect foreign directories - (i.e. ones that weren't created by CVS) in your working - directory and will destroy them. + cvs release -d <module> + + ("release -d" searches through the output of "cvs -n update" and + refuses to continue if the "update" command finds any modified + files or non-ignored foreign files. Foreign directories too.) - If you don't care about keeping "history", and you don't care to - plan ahead to a more completely implemented "release" command, you - can just remove it. That's "rm -rf <module>" under Unix. + If you don't care about keeping "history", or checking for + modified and foreign files, you can just remove the whole + directory. That's "rm -rf <module>" under Unix. -=2B.4 How do I find out what has changed? + 2B.4 How do I find out what has changed since my last update? There are many ways to answer this. To find out what you've changed in your current working directory - since your last commit, type: + since your last checkout, update or commit, type: cvs diff @@ -1494,10 +1577,10 @@ Conventions in this section: You can also use "history" to trace a wide variety of events. -=2B.5 I just created a new file. How do I add it to the Repository? + 2B.5 I just created a new file. How do I add it to the Repository? - The "update" command will display files CVS doesn't know about in - your working directory marked with a '?' indicator. + The "update" command will mark files CVS doesn't know about in + your working directory with a '?' indicator. ? <file> @@ -1506,8 +1589,10 @@ Conventions in this section: cvs add <file> cvs commit <file> + See 3A.[2-5] and 4C.8 for branch and merge considerations. -=2B.6 How do I merge changes made by others into my working directory? + + 2B.6 How do I merge changes made by others into my working directory? If you are asking about other branches, see Section 4C on "Branching". You will have to use the "update -j" command. @@ -1518,32 +1603,44 @@ Conventions in this section: by others since you last executed "checkout", "update" or "commit" into your working files. - For a single file, there are five possible results when you type + For a single file, there are six possible results when you type the "update" command: - 1. If neither you nor others have made changes to <file>, "update" - will print nothing. + 1. If the file is lying in your working directory, but is not + under CVS, it will do nothing but print: + + ? <file> + + 2. If neither you nor anyone else has committed changes to <file>, + since your last "checkout", "update" or "commit", "update" + will print nothing and do nothing. - 2. If you have made no changes to a file, but others have, CVS - will replace your working file with a copy of the latest - revision of that file in the Repository. You will see: + 3. If you have made no changes to a working file, but you or + others have committed changes to the Repository since your last + "checkout", "update" or "commit" of this working file, CVS will + remove your working file and replace it with a copy of the + latest revision of that file in the Repository. It will print: U <file> You might want to examine the changes (using the CVS "diff" command) to see if they mesh with your own in related files. - 3. If you have made changes, but others have not, you will see: + 4. If you have made changes to a working file, but no one has + changed your BASE revision (the revision you retrieved from the + Repository in your last "checkout", "update" or "commit"), + "update" will print: M <file> - Nothing happened except you were told that you have a modified + Nothing changes. You were told that you have a modified file in your directory. - 4. If both you and others have made changes to a file, but in - different sections of the file, CVS will merge the changes - stored in the Repository since your last "checkout", "update" - or "commit" into your working file. You will see: + 5. If you have made changes to your working file and you or others + have committed changes to the Repository, but in different + sections of the file, CVS will merge the changes stored in the + Repository since your last "checkout", "update" or "commit" + into your working file. "update" will print: RCS file: /Repository/module/<file> retrieving revision 1.X @@ -1552,14 +1649,15 @@ Conventions in this section: M <file> If you execute "diff" before and after this step, you should - see the same output. This is one of the few times the + see the same output, since both the base file and your working + file changed in parallel. This is one of the few times the otherwise nonsensical phrase "same difference" means something. - - 5. If both you and others have made changes to the same section of - a file, CVS will merge the changes into your file as in #4 - above, but it will leave conflict indicators in the file. - You will see: + 6. If both you and those who committed files (since your last + checkout, update or commit) have made changes to the same + section of a file, CVS will merge the changes into your file as + in #5 above, but it will leave conflict indicators in the file. + "update" will print: RCS file: /Repository/module/<file> retrieving revision 1.X @@ -1569,12 +1667,14 @@ Conventions in this section: cvs update: conflicts found in <file> C <file> - This is a "conflict". The file will contain strange-looking - text marking the overlapping text. - - You must examine the overlaps with care and resolve the - problem without removing all previous work. + This is a "conflict". The file will contain markers + surrounding the overlapping text. The 'C' conflict indicator + is sticky -- subsequent "update" commands will continue to show + a 'C' until you edit the file. + You must examine the overlaps with care and resolve the problem + by analyzing how to retain the features of both changes. See + 2D.7 and 3P.6 for more details on conflict resolution. 2B.7 How do I label a set of revisions so I can retrieve them later? @@ -1598,7 +1698,7 @@ Conventions in this section: 2B.8 How do I checkout an old release of a module, directory or file? Module names and directories are simply ways to name sets of - files. Once the names are determined, there are 6 ways to specify + files. Once the names are determined, there are 6 ways to specify which revision of a particular file to check out: 1. By tag or symbolic name, via the "-r <tag>" option. @@ -1611,13 +1711,14 @@ Conventions in this section: 4. By date within a branch, via the "-r <branch_tag>:<date>" option. - 5. By an explicit branch revision number, which refers to the - latest revision on the branch. This isn't really an "old" - revision, from the branch's perspective, but from the user's - perspective the branch might have been abandoned in the past. + 5. By an explicit branch revision number ("-r <rev>"), which + refers to the latest revision on the branch. This isn't really + an "old" revision, from the branch's perspective, but from the + user's perspective the whole branch might have been abandoned + in the past. - 6. An explicit revision number. Though this works, it is almost - useless for more than one file. + 6. An explicit revision number: "-r <rev>" Though this works, it + is almost useless for more than one file. You type: @@ -1634,7 +1735,8 @@ Conventions in this section: Unless you are purposely delaying the inclusion of others' work, you should execute "update" once in a while and resolve the - conflicts. It is not good to get too far out of sync. + conflicts. It is not good to get too far out of sync with the + rest of the developers working on your branch. It is assumed that your system administrators have arranged for editor backup and Unix temp files (#* and .#*) to be deleted after @@ -1642,7 +1744,8 @@ Conventions in this section: that is ignored or hidden. Try "cvs -n update -I !" to see all the ignored files. - If you are the Repository Administrator, see 4B.17. + If you are the Repository Administrator, see 4B.16 on + Administrator responsibilities. ---------------- @@ -1657,29 +1760,33 @@ more of the following commands: **** Questions: - 2C.1 Can I create sub-directories in my working directory? + 2C.1 Can I create non-CVS sub-directories in my working directory? 2C.2 How do I add new sub-directories to the Repository? 2C.3 How do I remove a file I don't need? -=2C.4 How do I rename a file? + 2C.4 How do I rename a file? 2C.5 How do I make sure that all the files and directories in my working directory are really in the Repository? -=2C.6 How do I create a branch? -=2C.7 How do I modify the modules file? How about the other files in + 2C.6 How do I create a branch? + 2C.7 How do I modify the modules file? How about the other files in the CVSROOT administrative area? -+2C.8 How do I split a file into pieces, retaining revision histories? + 2C.8 How do I split a file into pieces, retaining revision histories? **** Answers: - 2C.1 Can I create sub-directories in my working directory? + 2C.1 Can I create non-CVS sub-directories in my working directory? - Yes, but the "update" command will traverse them, wasting a lot - of time. You can't currently ignore directories but if a - directory has no ./CVS administrative directory, nothing will - happen to it during an "update". + Yes. Unless the directory exists in the Repository, "update" will + skip over them and print a '?' the way it does for files you + forgot to add. You can avoid seeing the '?' by adding the name + of the foreign directory to the ./.cvsignore file, just ask you + can do with files. - On the other hand "release -d" will delete it without warning. + If you explicitly mention a foreign directory on the "update" + command line, it will traverse the directory and waste a bit of + time, but if any directory or sub-directory lacks the ./CVS + administrative directory, CVS will print an error and abort. 2C.2 How do I add new sub-directories to the Repository? @@ -1691,11 +1798,10 @@ more of the following commands: It will respond: - Add directory /Repos/<dir> to the Repository (y/n) [n] ? + Directory /Repos/<dir> added to the repository - If you type a 'y', you will create both a directory in the - Repository and a ./CVS administrative directory within the local - <dir> directory. + and will create both a matching directory in the Repository and a + ./CVS administrative directory within the local <dir> directory. 2C.3 How do I remove a file I don't need? @@ -1721,34 +1827,37 @@ more of the following commands: files from the Attic with that tag, date or revision. -=2C.4 How do I rename a file? + 2C.4 How do I rename a file? CVS does not offer a way to rename a file in a way that CVS can track later. See Section 4B for more information. - Here is the best way to get the effect of renaming, while - preserving the change log: + Here is the best (to some, the only acceptable) way to get the + effect of renaming, while preserving the change log: 1. Copy the RCS (",v") file directly in the Repository. cp $CVSROOT/<odir>/<ofile>,v $CVSROOT/<ndir>/<nfile>,v - 2. Remove the old file using CVS. - By duplicating the file, you will preserve the change history and the ability to retrieve earlier revisions of the old file via the "-r <tag/rev>" or "-D <date>" options to "checkout" and "update". + 2. Remove the old file using CVS. + cd <working-dir>/<odir> rm <ofile> cvs remove <ofile> cvs commit <ofile> - 3. Retrieve <newfile> and remove all the Tags from it. + This will move the <ofile> to the Attic associated with + <odir>. - By stripping off all the old Tags, the "checkout -r" and - "update -r" commands won't retrieve revisions Tagged before + 3. Retrieve <nfile> and remove all the Tags from it. + + By stripping off all the old Tags, "checkout -r" and + "update -r" won't retrieve revisions Tagged before the renaming. cd <working-dir>/<ndir> @@ -1764,24 +1873,30 @@ more of the following commands: directories too, as long as you apply the above to each file and don't delete the old directory. - Of course, you have to change the build system (e.g. Makefile) in + Of course, you have to change your build system (e.g. Makefile) in your <working-dir> to know about the name change. + Warning: Stripping the old tags from the copied file will allow + "-r <tag>" to do the right thing, but you will still have problems + with "-D <date>" because there is no place to store the "deletion + time". See 5B.3 for more details. + 2C.5 How do I make sure that all the files and directories in my working directory are really in the Repository? - Normally, you only need to be notified of files you forgot to - "add". A simple "update", or "cvs -n update" (which won't modify - your working directory) will display non-added files preceded by a - '?' indicator. To recover, "add" and "commit" them. + A "cvs update", or "cvs -n update" (which won't modify your + working directory) will display foreign elements, which have no + counterpart in the Repository, preceded by a '?'. To register + foreign directories, you can use "cvs add". To register foreign + files, you can use "cvs add" followed by "cvs commit". - To verify that all your directories are in the Repository, you - have to go look. Though CVS traverses all directories, it - produces no output for directories not backed up by a Repository - directory. + You could also checkout your module, or the Repository directory + associated with your working directory, a second time into another + work area and compare it to your working directory using the + (non-CVS) "diff -r" command. - By default many patterns of files are ignored. If you create a + By default many patterns of files are ignored. If you create a file named "core" or a file ending in ".o", it is usually ignored. If you really want to see all the files that aren't in the Repository, you can use a special "ignore" pattern to say @@ -1790,16 +1905,16 @@ more of the following commands: cvs -n update -I ! - The above command will display not only the normal 'M'odified, - 'U'pdate and 'C'onflict indicators on files within the - Repository, but it will also display each file not in the - Repository preceded by a '?' character. + The above command will display not only the normal modified, + update and conflict indicators ('M', 'U', and 'C' respectively) on + files within the Repository, but it will also display each file + not in the Repository preceded by a '?' character. The '-n' option will not allow "update" to alter your working directory. -=2C.6 How do I create a branch? + 2C.6 How do I create a branch? Type this in your working directory: @@ -1820,7 +1935,7 @@ more of the following commands: See Section 4C, on Branching. -=2C.7 How do I modify the modules file? How about the other files in + 2C.7 How do I modify the modules file? How about the other files in the CVSROOT administrative area? A module named "modules" has been provided in the default modules @@ -1839,22 +1954,21 @@ more of the following commands: cvs commit - If you use the provided template for the "modules" file, both the - CVSROOT and the "modules" module will have the "mkmodules" program - as a "commit helper". - - After a file is committed in these modules the "mkmodules" - command will convert all the files CVSROOT directory within the - Repository into a form that is usable by CVS. + If you start with the provided template for the "modules" file, + the CVSROOT and the "modules" module will have the "mkmodules" + program as a "commit helper". After a file is committed to such a + module, "mkmodules" will convert a number of standard files (See + 4B.2) in the CVSROOT directory inside the Repository into a form + that is usable by CVS. -+2C.8 How do I split a file into pieces, retaining revision histories? + 2C.8 How do I split a file into pieces, retaining revision histories? If you and a coworker find yourselves repeatedly committing the same file, but never for changes in the same area of the file, you might want to split the file into two or more pieces. If you are both changing the same section of code, splitting the file is of - no use. You should talk to each other instead. + no use. You should talk to each other instead. If you decide to split the file, here's a suggestion. In many ways, it is similar to multiple "renamings" as described in @@ -1864,7 +1978,8 @@ more of the following commands: into three pieces, <fileA>, <fileB> and <fileC>. 1. Copy the RCS (",v") files directly in the Repository, - creating all the new files. + creating the new files, then bring readable copies of the + new files into the working directory via "update". cp $CVSROOT/<path>/<fileA>,v $CVSROOT/<path>/<fileB>,v cp $CVSROOT/<path>/<fileA>,v $CVSROOT/<path>/<fileC>,v @@ -1877,12 +1992,14 @@ more of the following commands: cvs tag -d <tag2> <fileB> <fileC> . . . - 3. Edit and commit all three copies of the same file into three - distinct files. This is a hand-editing job, not something - CVS can handle. [From experience, I'd suggest making sure - that only one copy of each line of code exists among the - three files, except for "include" statements, which must be - duplicated. And make sure the code compiles.] + 3. Edit each file until it has the data you want in it. + This is a hand-editing job, not something CVS can handle. + Then commit all the files. + + [From experience, I'd suggest making sure that only one copy + of each line of code exists among the three files, except + for "include" statements, which must be duplicated. And + make sure the code compiles.] emacs <fileA> <fileB> <fileC> cvs commit <fileA> <fileB> <fileC> @@ -1891,11 +2008,8 @@ more of the following commands: As in the "rename" case, by duplicating the files, you'll preserve the change history and the ability to retrieve earlier revisions. - Also, as in the "rename" case, you can apply this idea to - directories too, by changing <path> to <pathA>, <pathB> and - <pathC> in the example. - - Of course, you have to change your build system (e.g. Makefile). + Of course, you have to alter your build system (e.g. Makefiles) to + take the new names and the change in contents into account. @@ -1905,20 +2019,20 @@ more of the following commands: **** Questions: -=2D.1 How do I see what CVS is trying to do? + 2D.1 How do I see what CVS is trying to do? 2D.2 If I work with multiple modules, should I check them all out and commit them occasionally? Is it OK to leave modules checked out? 2D.3 What is a "sticky" tag? What makes it sticky? How do I loosen it? 2D.4 How do I get an old revision without updating the "sticky tag"? -=2D.5 What operations disregard sticky tags? -=2D.6 Is there a way to avoid reverting my Emacs buffer after + 2D.5 What operations disregard sticky tags? + 2D.6 Is there a way to avoid reverting my Emacs buffer after committing a file? Is there a "cvs-mode" for Emacs? 2D.7 How does conflict resolution work? What *really* happens if two of us change the same file? 2D.8 How can I tell who has a module checked out? -#2D.9 Where did the .#<file>.1.3 file in my working directory come from? - 2D.10 What is this "ignore" stuff? - 2D.11 Why does .cvsignore not ignore directories? + 2D.9 Where did the .#<file>.1.3 file in my working directory come from? + 2D.10 What is this "ignore" business? What is it ignoring? + 2D.11 Is there a way to set user-specific configuration options? 2D.12 Is it safe to interrupt CVS using Control-C? 2D.13 How do I turn off the "admin" command? 2D.14 How do I turn off the ability to disable history via "cvs -l"? @@ -1928,25 +2042,26 @@ more of the following commands: **** Answers: -=2D.1 How do I see what CVS is trying to do? + 2D.1 How do I see what CVS is trying to do? The '-t' option on the main "cvs" command will display every external command (mostly RCS commands and file deletions) it executes. When combined with the '-n' option, which prevents the execution of any command that might modify a file, you can see - what it will do before you let it fly. The '-t' option will *not* + what it will do before you let it fly. The '-t' option will *not* display every internal action, only calls to external programs. To see a harmless example, try typing: cvs -nt update - Some systems offer a "trace" command that will display all system - calls as they happen. This is a *very* low-level interface, but - it can be useful. + Some systems offer a "trace" or "truss" command that will display + all system calls as they happen. This is a *very* low-level + interface that does not normally follow the execution of external + commands, but it can be useful. The most complete answer is to read the source, compile it - with the '-g' option and execute it under a debugger. + with the '-g' option and step through it under a debugger. 2D.2 If I work with multiple modules, should I check them all out and @@ -1991,20 +2106,20 @@ more of the following commands: 2D.4 How do I get an old revision without updating the "sticky tag"? - Use the '-p' option to "pipe" data to standard output. The + Use the '-p' option to "pipe" data to standard output. The command "update -p -r <tag/rev>" sends the selected revision to your standard output (usually the terminal, unless redirected). The '-p' affects no disk files, leaving a "sticky tag" unaltered and avoiding all other side-effects of a normal "update". If you want to save the result, you can redirect "stdout" to a - file using your shell's redirection capability. In most shells + file using your shell's redirection capability. In most shells the following command works: cvs update -p -r <tag/rev> filename > diskfile -=2D.5 What operations disregard sticky tags? + 2D.5 What operations disregard sticky tags? The functions that routinely disregard sticky tags are: @@ -2021,15 +2136,15 @@ more of the following commands: 3. The "release" command itself ignores sticky tags, but it calls "cvs -n update" (which *does* pay attention to a sticky tag) to figure out what inconsistencies exist in - the working directory. If no discrepancies exist between + the working directory. If no discrepancies exist between the files you originally checked out (possibly marked by a sticky tag) and what is there now, "release -d" will delete them all. - 4. The "tag" command, which works on the revision lying in - the working directory however it got there. That the - revision lying there might happen to have a sticky tag - attached to it is not the "tag" command's concern. + 4. The "tag" command works on the revision lying in the + working directory however it got there. That the revision + lying there might happen to have a sticky tag attached to + it is not the "tag" command's concern. The main function that *does* read and write sticky tags is the @@ -2042,23 +2157,23 @@ more of the following commands: an existing directory. The "diff" and "commit" commands use the sticky tags, unless - overridden on the command line. They do not set sticky tags. (In - the future, "commit" might set a sticky branch tag on a newly - added file.) Note that you can only "commit" to a file checked - out with a sticky tag, if the tag identifies a branch. + overridden on the command line. They do not set sticky tags. + Note that you can only "commit" to a file checked out with a + sticky tag, if the tag identifies a branch. There are really two types of sticky tags, one attached to individual files (in the ./CVS/Entries file) and one attached to - each directory (in the ./CVS/Tag file). They can differ. + each directory (in the ./CVS/Tag file). They can differ. - The "add" command doesn't pay attention to anything -- it just - registers the desire to add a new file. When a newly added file - is `committed", CVS *should* use the "directory tag" to determine - what branch to commit it to and set the corresponding sticky tag. - Unfortunately, it doesn't work correctly in CVS 1.3. See 4C.8. + The "add" command registers the desire to add a new file. If the + "directory tag" (./CVS/Tag) file exists at the time of the "add", + the value stored in ./CVS/Tag becomes the "sticky tag" on the new + file. The file doesn't exist in the Repository until you "commit" + it, but the ./CVS/Entries file holds the sticky tag name from the + time of the "add" forward. -=2D.6 Is there a way to avoid reverting my Emacs buffer after + 2D.6 Is there a way to avoid reverting my Emacs buffer after committing a file? Is there a "cvs-mode" for Emacs? See Section 4F.1 @@ -2068,13 +2183,14 @@ more of the following commands: of us change the same file? While editing files, there is no conflict. You are working on - separate virtual branches of development contained in your working - directories. When one of you decides to commit the file, the - other may not commit the same file until "update" has merged the - two together. + separate copies of the file stored in the virtual "branch" + represented by your working directories. After one of you commits + a file, the other may not commit the same file until "update" has + merged the earlier committed changes into the later working file. - Say you both check out rev 1.2 of <file>. Your coworker commits - revision 1.3. When you try to commit your file, CVS says: + For example, say you both check out rev 1.2 of <file> and make + change to your working files. Your coworker commits revision 1.3. + When you try to commit your file, CVS says: cvs commit: Up-to-date check failed for `<file>' @@ -2085,27 +2201,42 @@ more of the following commands: which will produce the output described in 2B.6. - After you resolve any overlaps caused by the merging process, you - may then commit the file. + If a conflict occurs, the filename will be shown with a status of + 'C'. After you resolve any overlaps caused by the merging + process, you may then commit the file. See 3P.6 for info on + "sticky conflicts". + + Even if you get a simple 'M', you should examine the differences + before committing the file. A smooth, error-free text merge is + still no indication that the file is in proper shape. Compile and + test it at least. + + The answer to two obvious questions is "Yes". - Yes, the first one who commits can cause the other some work. + Yes, the first one who commits avoids the merge. Later developers + have to merge the earlier changes into their working files before + committing the merged result. Depending on how difficult the merge + is and how important the contending projects are, the order of + commits and updates might have to be carefully staged. - Yes, between the time you execute "update" and "commit", someone - else may have committed a later revision of <file>. You will have - to execute "update" again to merge the new work before - committing. Most organizations don't have this problem. If you - do, you might consider splitting the file. + And yes, between the time you execute "update" and "commit" (while + you are fixing conflicts and testing the results) someone else may + commit another revision of <file>. You will have to execute + "update" again to merge the new work before committing. Most + organizations don't have this problem. If you do, you might + consider splitting the file. Or hiring a manager. 2D.8 How can I tell who has a module checked out? If you "checkout" module names (not relative pathnames) and you - use the release command, the "history" command will display who - has what checked out. It is advisory only; it can be circumvented - by using the '-l' option on the main "cvs" command. + use the release command, the "history" command will display active + checkouts, who has them and where they were checked out. It is + advisory only; it can be circumvented by using the '-l' option on + the main "cvs" command. -#2D.9 Where did the .#<file>.1.3 file in my working directory come from? + 2D.9 Where did the .#<file>.1.3 file in my working directory come from? It was created during an "update" when CVS merged changes from the Repository into your modified working file. @@ -2129,20 +2260,20 @@ more of the following commands: In the case where your working file was not modified, #1 and #3 will be the same, as will #2 and #4. In this degenerate case, - there is no need to create #5. The following assumes that your + there is no need to create #5. The following assumes that your working file was modified. - If the merge executed by the "update" caused no overlaps, #4 - and #5 will be the same. But you might then make changes before - committing, so the difference between #4 and #5 might be more - than just the correction of overlaps. In general, though, you - don't need #4 after a commit. + If the merge executed by the "update" caused no overlaps, and you + commit the file immediately, #4 and #5 will be the same. But you + can make arbitrary changes before committing, so the difference + between #4 and #5 might be more than just the correction of + overlaps. In general, though, you don't need #4 after a commit. But #3 (which is the one saved as ".#<file>.1.3") holds all of your work, independent of B's work. It could represent a major effort that you couldn't afford to lose. If you don't save it somewhere, the merge makes #3 *disappear* under a potential - rat's nest of conflicts caused by overlapping changes. + blizzard of conflicts caused by overlapping changes. I have been saved a few times, and others I support have been saved hundreds of times, by the ability to "diff <original file> @@ -2175,19 +2306,22 @@ more of the following commands: whole file (with his "undo" size set low so he couldn't undo it) and decide that it would be less work to play with the uppercased file than to blow it away and start over. I've even - seen committed files with conflict markers still in them. + seen committed files with conflict markers still in them, a sure + sign of carelessness. There are all sorts of scenarios where having #3 is incredibly - useful. You can move it back into place and try again. + useful. You can move it back into place and try again. - 2D.10 What is this "ignore" stuff? + 2D.10 What is this "ignore" business? What is it ignoring? The "update" and "import" commands use collections of Unix - wildcards to skip over files matching any of those patterns. + wildcards to skip over files and directories matching any of those + patterns. - You may add to the built-in ignore list by adding wildcards to - the following places: (They are read in this order.) + You may add to the built-in ignore list by adding lines of + whitespace-separated wildcards to the following places: (They are + read in this order.) 1. In a file named "cvsignore" in $CVSROOT/CVSROOT. @@ -2200,8 +2334,9 @@ more of the following commands: your default junk file prefix, you can put "__*" in your .cvsignore file. - People who play around in the X tree might want to put - "Makefile" in their ignore list, since they are all + People who play around exclusively in directory trees where the + Makefiles are generated by "imake" or "configure" might want to + put "Makefile" in their ignore list, since they are all generated and usually don't end up in the Repository. 3. In the CVSIGNORE environment variable. @@ -2217,9 +2352,9 @@ more of the following commands: The contents of a ".cvsignore" file in each directory is temporarily added to the ignore list. This way you can ignore files that are peculiar to that directory, such as executables - and other files without known suffix patterns. + and other generated files without known wildcard patterns. - In any of the 5 places listed above, a single '!' character nulls + In any of the places listed above, a single '!' character nulls out the ignore list. A Repository administrator can use this to override, rather than enhance, the built-in ignore list. A user can choose to override the system-wide ignore list. For example, @@ -2227,21 +2362,72 @@ more of the following commands: files, plus any files a local-directory .cvsignore file, are ignored. + A variant of the ignore-file scheme is used internally during + checkout. "Module names" found in the modules file (or on the + "checkout" command line) that begin with a '!' are ignored during + checkout. This is useful to permanently ignore (if the '!' path + is in the modules file) or temporarily ignore (if the '!' path is + on the command line) a sub-directory within a Repository + hierarchy. For example: + + cvs checkout !gnu/emacs/tests gnu/emacs + + would checkout the module (or relative path within $CVSROOT) named + "gnu/emacs", but ignore the "tests" directory within it. + + + 2D.11 Is there a way to set user-specific configuration options? + + User-specific configuration is available through use of a ".cvsrc" + file in your home directory. + + CVS searches the first column of your ~/.cvsrc file for the cvs + command name you invoked. If the command is found, the rest of + the line is treated like a set of command line options, stuffed + into the command line before the arguments you actually typed. + + For example, if you always want to see context diffs and you never + want to have to delete a file before you run "cvs remove", then + you should create a .cvsrc file containing the following: + + diff -c + remove -f + + which will add the given options to every invocation of the given + commands. + + [[The rest of this will be removed someday, when CVS changes.]] + + I would like to stop here with a comment that the command name to + use is the full, canonical one. But the command that the cvsrc + support uses is the string you typed on the command line, not the + proper command. So to get the full effect of the above example, + you should also add all the alternate command names: - 2D.11 Why does .cvsignore not ignore directories? + di -c + dif -c + rm -f + delete -f - Ignore lists are intended to be per-directory wildcards matching - various patterns. They are matched against file names, not - directory names or relative paths ('/' is an invalid character in - an ignore list). I suppose it could be extended, but as it - stands, it only works on files. + There are two other limitations that will probably be fixed when + CVS sprouts long option names: - This might change in the future. + 1. It only affects options made available on the command line. + + There is a limited number of short options. With long option + names, there is no problem. You can have as many long options + as you like, affecting anything that looks malleable. + + 2. The existing command line options do not come in on/off pairs, + so there is no easy way to override your ~/.cvsrc configuration + for a single invocation of a command. + + Choosing a good set of long option pairs would fix this. 2D.12 Is it safe to interrupt CVS using Control-C? - It depends on what you mean by "safe". ("Ah," said Arthur, + It depends on what you mean by "safe". ("Ah," said Arthur, "this is obviously some strange usage of the word *safe* that I wasn't previously aware of." -- Hitchhiker's Guide to the Galaxy) @@ -2266,13 +2452,13 @@ more of the following commands: to the files is a different matter. Since CVS is not a full-fledged database, with what database - people call "commit points", merely stopping the process will - not place you back in the starting blocks. CVS has no concept of - an "atomic" transaction or of "backtracking", which means that - a command can be half-executed. + people call "commit points", merely stopping the process will not + back out the "transaction" and place you back in the starting + blocks. CVS has no concept of an "atomic" transaction or of + "backtracking", which means that a command can be half-executed. - First, you will usually leave lock files that you have to go clean - up in the Repository. + Hitting Control-C will usually leave lock files that you have to + go clean up in the Repository. Example1: @@ -2308,19 +2494,25 @@ more of the following commands: Normally, you have little choice but to re-execute the command and allow it to tag everything consistently. - You might be able to recover by applying a raw "rcs -n" to + You might be able to recover by carefully re-applying the + tags via the "cvs admin -N" command, but you'll still have + to dig up from outside sources the information you use to + determine what tag was on what revision in what file. the Repository, or by using the equivalent: "cvs admin". - Halting a new "checkout" should cause no harm. If you don't want + Halting a new "checkout" should cause no harm. If you don't want it, "release" (or rm -rf) it. If you do want it, re-execute the - command. - - Halting "update" half-way will give you some strange collection - of files and revisions. You'll have to examine the output from - the command and take a look at each file that was modified. Good - Luck. + command. A repeated "checkout" from above a directory acts like a + repeated "update -d" within it. + Halting "update" half-way will give you an unpredictable + collection of files and revisions. To continue, you can rerun the + update and it should move you forward into in a known state. To + back out, you'll have to examine the output from the first + "update" command, take a look at each file that was modified and + reconstruct the previous state by editing the ./CVS/Entries file + and by using "cvs admin". Good Luck. 2D.13 How do I turn off the "admin" command? @@ -2340,8 +2532,8 @@ more of the following commands: If you only want to limit "commit" commands, you can write a program to put in the "commitinfo" file. In the "contrib" - directory, there is a script called "cvs_acls.pl" that implements - a form of access control. + directory, there are a few scripts that might help you out. + ======================================== @@ -2354,7 +2546,7 @@ single command, usually of the form: "Why does the 'xyz' command do this?" Questions about "missing" features and side-effects not attributable to a particular command are in Section 2D, "General Questions". -I won't provide patches here that are longer than a few lines. Patches +I won't provide patches here that are longer than a few lines. Patches referred to in this section are available in the FTP archive described toward the beginning of this document. @@ -2367,9 +2559,13 @@ toward the beginning of this document. 3A.1 What is "add" for? 3A.2 How do I add a new file to the branch I'm working on? - 3A.3 Why did my newly added file end up in the Attic? - 3A.4 How do I put a new file on the Main Branch and branch off from - there onto my default branch? + 3A.3 Why did my new file end up in the Attic? + 3A.4 Now that it's in the Attic, how do I connect it to the Main branch? + 3A.5 How do I avoid the hassle of reconnecting an Attic-only file to + the Main Branch? + 3A.6 How do I cancel an "add"? + 3A.7 What are the ./CVS/file,p and ./CVS/file,t files for? + 3A.8 How do I "add" a binary file? **** Answers: @@ -2379,31 +2575,164 @@ toward the beginning of this document. To add a new directory to the Repository or to register the desire to add a new file to the Repository. - The directory is created immediately, after verification, while - the desire to add the file is recorded in the local ./CVS - administrative directory. To really add the file to the - Repository, you must then "commit" it. + The directory is created immediately, while the desire to add the + file is recorded in the local ./CVS administrative directory. To + really add the file to the Repository, you must then "commit" it. 3A.2 How do I add a new file to the branch I'm working on? - See 4C.8 + The user actions for adding a file to any branch, including the + Main Branch, are exactly the same. + + You are in a directory checked out (or updated) with the '-A' + option (to place you on the Main Branch) or the "-r <branch_tag>" + option (to place you on a branch tagged with <branch_tag>). To + add <file> to the branch you are on, you type: + + cvs add <file> + cvs commit <file> + + If no ./CVS/Tag file exists (the '-A' option deletes it), the + file will be added to the Main Branch. If a ./CVS/Tag file exists + (the "-r <branch_tag>" option creates it), the file will be added + to the branch named (i.e. tagged with) <branch_tag>. + + Unless you took steps to first add the file to the Main Branch, + your new file ends up in the Attic. + + + 3A.3 Why did my new file end up in the Attic? + + The file is thrown into the Attic to keep it from being visible + when you check out the Main Branch, since it was never committed + to the Main Branch. + + 3A.4 Now that it's in the Attic, how do I connect it to the Main branch? - 3A.3 Why did my newly added file end up in the Attic? + That can be considered a kind of "merge". See 4C.8 - Your new file is placed in the Attic if it is added onto a side - branch without ever showing up on the trunk. - If the file were in the main Repository area, it would show up - when the Main branch is checked out. You didn't commit it onto - the Main branch -- only onto the side branch. + 3A.5 How do I avoid the hassle of reconnecting an Attic-only file to + the Main Branch? + You create it on the Main Branch first, then branch it. - 3A.4 How do I put a new file on the Main Branch and branch off from - there onto my default branch? + If you haven't yet added the file or if you decided to delete the + new Attic file and start over, then do the following: + (If you added the file (or worse, the 157 files) to the Attic and + don't want to start over, try the procedure in 4C.8.) - See 4C.8 + + 1. Temporarily remove the sticky branch information. Either: + + A. Move the whole directory back to the Main Branch. + [This might not be a good idea if you have modified files, + since it will require a merge in each direction.] + + cvs update -A + + *or* + + B. Move the ./CVS/Tag file out of the way. + + mv ./CVS/Tag HOLD_Tag + + 2. Add and branch the file "normally": + + cvs add <file> + cvs commit <file> + cvs tag -b <branch_tag> <file> + + [<branch_tag> is the same Branch Tag as you used on all + the other files. Look at ./CVS/Entries or the output + from "cvs stat" for sticky tags.] + + 3. Clean up the temporary step. + + A. If you moved the ./CVS/Tag file, put it back. Then + move the new file onto the branch where you are working. + + mv HOLD_Tag ./CVS/Tag + cvs update -r <branch_tag> <file> + + B. If you ran "update -A" rather than moving the ./CVS/Tag + file, move the whole directory (including the new file) back + onto the branch where you were working: + + cvs update -r <branch_tag> + + + 3A.6 How do I cancel an "add"? + + If you want to remove the file entirely and cancel the "add" at + the same time, type: + + cvs remove -f <file> + + If you want to cancel the "add", but leave the file as it was + before you typed "cvs add", then you have to fake it: + + mv <file> <file>.hold + cvs remove <file> + mv <file>.hold <file> + + + 3A.7 What are the ./CVS/file,p and ./CVS/file,t files for? + + The ./CVS/file,p and ./CVS/file,t files are created by the "add" + command to hold command line options and message text between the + time of the "add" command and the expected "commit". + + The ./CVS/file,p file is always null, since its function was + absorbed by the "options" field in the ./CVS/Entries file. If you + put something in this file it will be used as arguments to the RCS + "ci" command that commit uses to check the file in, but CVS itself + doesn't put anything there. + + The ./CVS/file,t file is null unless you specify an initial + message in an "add -m 'message'" command. The text is handed to + "rcs -i -t./CVS/file,t" to create the initial RCS file container. + + Both files must exist to commit a newly added file. If the + ./CVS/file,p file doesn't exist, CVS prints an error and aborts + the commit. If the ./CVS/file,t file doesn't exist, RCS prints an + error and CVS gets confused, but does no harm. + + To recover from missing ,p and ,t files, just create two + zero-length files and rerun the "commit". + + + 3A.8 How do I "add" a binary file? + + If you configured CVS to use the GNU version of "diff" and + "diff3", you only need to turn off RCS keyword expansion. + + First you turn off RCS keyword expansion for the initial checkin + by using "add -ko". It works like "update -ko" in creating a + "sticky" option only for the copy of the file in the current + working directory. + + cvs add -ko <file> + + Commit the file normally. The sticky -ko option will be used. + + cvs commit <file> + + Then mark the RCS file in the Repository so that keyword + expansion is turned off for all checked out versions of the file. + + cvs admin -ko <file> + + Since "admin -ko" records the keyword substitution value in the + Repository's RCS file, you no longer need the sticky option. You + can turn it off with the "update -A" command, but if you were on a + branch, you'll have to follow it "update -r <branch_tag>" to put + yourself back on the branch. + + + Managing that binary file is another problem. See 4D.1. @@ -2415,11 +2744,11 @@ toward the beginning of this document. 3B.1 What is "admin" for? 3B.2 Wow! Isn't that dangerous? -=3B.3 What would I normally use "admin" for? -=3B.4 What should I avoid when using "admin"? --3B.5 How do I restrict the "admin" command? The -i flag in the modules + 3B.3 What would I normally use "admin" for? + 3B.4 What should I avoid when using "admin"? + 3B.5 How do I restrict the "admin" command? The -i flag in the modules file can restrict commits. What's the equivalent for "admin"? -+3B.6 I backed out a revision with "admin -o" and committed a + 3B.6 I backed out a revision with "admin -o" and committed a replacement. Why doesn't "update" retrieve the new revision? @@ -2428,8 +2757,9 @@ toward the beginning of this document. 3B.1 What is "admin" for? - To provide direct access to the underlying "rcs" command, which - is not documented in this FAQ + To provide direct access to the underlying "rcs" command (which + is not documented in this FAQ) bypassing all safeguards and CVS + assumptions. 3B.2 Wow! Isn't that dangerous? @@ -2445,18 +2775,63 @@ toward the beginning of this document. before blasting any CVS files. -=3B.3 What would I normally use "admin" for? + 3B.3 What would I normally use "admin" for? Normally, you wouldn't use admin at all. In unusual circumstances, experts can use it to set up or restore the internal RCS state that CVS requires. - You can also use the '-o' (for "outdate") option to remove - revisions you don't care about. This has its own problems, such - as leaving dangling Tags and confusing the "update" command. + You can use "admin -o" (for "outdate") to remove revisions + you don't care about. This has its own problems, such as leaving + dangling Tags and confusing the "update" command. + There is some feeling among manipulators of binary files that + "admin -l" should be used to serialize access. See 3C.8. -=3B.4 What should I avoid when using "admin"? + An interesting use for "admin" came up while maintaining CVS + itself. I import versions of CVS onto the Vendor branch of my + copy of CVS, make changes to some files and ship the diffs + (created by "cvs diff -c -r TO_BRIAN") off to Brian Berliner. + After creating the diff, I retag ("cvs tag -F TO_BRIAN") the + working directory, which is then ready to produce the next patch. + + I'll use "add.c" as an example (only because the name is short). + + When the next release came out, I discovered that the released + "add.c" (version 1.1.1.3 on the Vendor branch) was exactly the + same as my modified file (version 1.3). I didn't care about the + changelog on versions 1.2 and 1.3 (or the evidence of having done + the work), so I decided to revert the file to the state where it + looked like I had not touched the file -- where I was just using + the latest on the vendor branch after a sequence of imports. + + To do that, I removed all the revisions on the main branch, except + for the original 1.1 from which the Vendor branch sprouts: + + cvs admin -o1.2: add.c + + Then I set the RCS "default branch" back to the Vendor branch, the + way import would have created it: + + cvs admin -b1.1.1 add.c + + And I moved the "TO_BRIAN" Tag to the latest revision on the + Vendor branch, since that is the base from which further patches + would be created (if I made any): + + cvs admin -NTO_BRIAN:1.1.1.3 add.c + + Instead of 1.1.1.3, I could have used one of the "Release Tags" + last applied by "import" (3rd through Nth arguments). + + Suggestion: Practice on non-essential files. + + + + 3B.4 What should I avoid when using "admin"? + + If you know exactly what you are doing, hack away. But under + normal circumstances: Never use "admin" to alter branches (using the '-b' option), which CVS takes very seriously. If you change the default branch, CVS @@ -2464,8 +2839,8 @@ toward the beginning of this document. using the "tag -b" command, you may not be able to treat them as CVS branches. - Don't try to use the '-l' option, which will lock RCS files. - See 4D.7 for a cautionary scenario. + See 3C.8 for a short discussion of how to use "admin -l" for + serializing access to binary files. The "admin -o <file>" allows you to delete revisions, usually a bad idea. You should commit a correction rather than back out a @@ -2492,7 +2867,7 @@ toward the beginning of this document. connected branch revision 1.1.1.1), then the default branch must be set to the Vendor branch as it was when you first imported the file. Outdating back through 1.2 doesn't restore - the branch setting. Despite the above admonition against it, + the branch setting. Despite the above admonition against it, "admin -b" is the only way to recover: cvs admin -b1.1.1 <file> @@ -2505,7 +2880,7 @@ toward the beginning of this document. 5. If you "outdate" a tagged revision, you will invalidate all uses of the <tag>, not just the one on <file>. A tag is supposed to be attached to a consistent set of files, usually a - set built as a unit. By discarding one of the files in the + set built as a unit. By discarding one of the files in the set, you have destroyed the utility of the <tag>. And it leaves a dangling tag, which points to nothing. @@ -2515,7 +2890,7 @@ toward the beginning of this document. and you "outdate" the 1.3 revision, <tag> will point to a nonexistent revision. Although this is annoying, it is nowhere near as much trouble as the problem that will occur when you - commit to this file again, recreating revision 1.3. The old + commit to this file again, recreating revision 1.3. The old tag will point to the new revision, a file that was not in existence when the <tag> was applied. And the discrepancy is nearly undetectable. @@ -2525,16 +2900,16 @@ toward the beginning of this document. command at all. --3B.5 How do I restrict the "admin" command? The -i flag in the modules + 3B.5 How do I restrict the "admin" command? The -i flag in the modules file can restrict commits. What's the equivalent for "admin"? At this writing, to disable the "admin" command, you will have to change the program source code, recompile and reinstall. -+3B.6 I backed out a revision with "admin -o" and committed a + 3B.6 I backed out a revision with "admin -o" and committed a replacement. Why doesn't "update" retrieve the new revision? - + CVS is confused because the revision in the ./CVS/Entries file matches the latest revision in the Repository *and* the timestamp in the ./CVS/Entries file matches your working file. CVS believes @@ -2573,8 +2948,8 @@ toward the beginning of this document. 3C.6 How do I avoid dealing with those long relative pathnames? 3C.7 Can I move a checked-out directory? Does CVS remember where it was checked out? -#3C.8 How can I lock files on checkout the way RCS does? -+3C.9 What is "checkout -s"? How is it different from "checkout -c"? + 3C.8 How can I lock files while I'm working on them the way RCS does? + 3C.9 What is "checkout -s"? How is it different from "checkout -c"? **** Answers: @@ -2604,7 +2979,7 @@ toward the beginning of this document. Differences include: - 1. CVS does not lock the files. Others may access them at the + 1. CVS does not lock the files. Others may access them at the same time. 2. CVS works best when you provide a name for a collection of @@ -2615,43 +2990,42 @@ toward the beginning of this document. you are on, simplifying later commands. - 3C.4 What's the difference between "update" and "checkout"? - The "checkout" and "update" differ in the following ways: + The "checkout" and "update" commands are nearly equivalent in how + they treat individual files. They differ in the following ways: 1. The "checkout" command always creates a directory, moves into it, then becomes equivalent to "update -d". - 2. The "update" does not create directories unless you add the - '-d' option. + 2. The "update" command does not create directories unless you add + the '-d' option. 3. "Update" is intended to be executed within a working directory - created by "checkout". It doesn't take a "module" or - "directory" argument, but figures out what Repository files to - look at by reading the ./CVS administrative directory. + created by "checkout". It doesn't take a module or directory + argument, but figures out what Repository files to look at by + reading the files in the ./CVS administrative directory. 4. The two commands generate completely different types of records in the "history" file. - The two commands are equivalent in nearly all other respects. - 3C.5 Why can't I check out a file from within my working directory? - You normally check out a module or directory, not a file. And you - normally do it only once at the beginning of a project. + Though you *can* check out a file, you normally check out a module + or directory. And you normally do it only once at the beginning + of a project. After the initial "checkout", you can use the "update" command to retrieve any file you want within the checked-out directory. There is no need for further "checkout" commands. If you want to retrieve another module or directory to work on, - you must provide names for both where to find it in the Repository - and where to put it on disk. The "modules" file and your - current directory supply two pieces of naming information. While - inside a checked-out working directory, the CVS administrative - information provides most of the rest. + you must provide two pathnames: where to find it in the Repository + and where to put it on disk. The "modules" file and your current + directory supply two pieces of naming information. While inside a + checked-out working directory, the CVS administrative information + provides most of the rest. You should be careful not to confuse CVS with RCS and use "checkout" in the RCS sense. An RCS "checkout" (which is @@ -2670,9 +3044,9 @@ toward the beginning of this document. This type of question occurs only among groups of people who - decide not to use "modules". The answer is to use "module". + decide not to use "modules". The answer is to use "modules". - When you hand the "checkout" command a relative pathname, rather + When you hand the "checkout" command a relative pathname rather than a module name, all directories in the path are created, maintaining the same directory hierarchy as in the Repository. The same kind of environment results if you specify a "module" @@ -2689,10 +3063,6 @@ toward the beginning of this document. responsibility of the Repository Administrators to set up a modules file that describes the software within the Repository. - I consider it unfortunate that CVS sprouted the ability to check - out relative pathnames without more extensive and flexible - support for "modules." - 3C.7 Can I move a checked-out directory? Does CVS remember where it was checked out? @@ -2705,7 +3075,7 @@ toward the beginning of this document. $CVSROOT, depending on how you configured CVS. When you move a checked-out directory, the CVS administrative - files will move along with it. As long as you don't move the + files will move along with it. As long as you don't move the Repository itself, or alter your $CVSROOT variable, the moved directory will continue to be usable. @@ -2715,19 +3085,105 @@ toward the beginning of this document. "history" command. -#3C.8 How can I lock files on checkout the way RCS does? + 3C.8 How can I lock files while I'm working on them the way RCS does? + + Until the day arrives of the all-powerful merge tool, there are + still files that must be accessed serially. For those instances, + here's a potential solution: + + 1. Install a pre-commit program in the "commitinfo" file to check + for RCS locks. The program "rcslock.pl" performs this + function. It can be found in the contrib directory of the CVS + source distribution. + + 2. When you want to make a change to a file you know can't be + merged, first use "cvs admin -l" to lock the file. If you + can't acquire the lock, use the standard "locked out" protocol: + go talk to the person holding the lock. + + 3. Make sure the pre-commit program prints a message and exits + with a non-zero status if someone besides the user running + "commit" has the file locked. This non-zero exist status will + cause the "commit" to fail cleanly. + + 4. Make sure the pre-commit program exits with a zero status if + the file is either unlocked or locked by the user running + "commit". The "cvs commit" command that kicked off the + pre-commit program will take a zero exist status as an OK and + checkin the file, which has the side-effect of unlocking it. + + + ===> The following is opinion and context. Don't read it if you + are looking for a quick fix. + + The topic of locking CVS files resurfaces on the network every so + often, producing the same results each time: + + The Big Endians: + + CVS was designed to avoid locks, using a copy-modify-merge + model. Locking is not necessary and you should take the time + to learn the CVS model which many people find workable. So why + not get with the program and learn how to think the CVS way? + + The Little Endians: + + The users determine how a tool is to be used, not the + designers. We, the users, have always used locking, our bosses + demand locking, locking is good, locking is God. I don't want + to hear any more lectures on the CVS model. Make locking work. + + Any organization making active changes to a source base will + eventually face the need to do parallel development. Parallel + development implies merges. (If you plan to keep separate copies + of everything and never merge, good luck. Tell me who you work + for so I can buy stock in your disk suppliers this year and sell + your stock short next year.) - Think about why you want that ability. RCS locking is there to - keep people from breaking individual files. CVS does the same - task a different way. If you are only looking for the consistency - aspect, then you should just forget about locking. For normal - development, there is no need for CVS to lock anything. + Merges will never go away. CVS chose to make "merges" stand + front and center as an important, common occurrence in + development. It is one way of looking at things. - If you want to restrict access to parts of the Repository, see - the question in Section 4B on "Limiting Access". + For free-format text, the merge paradigm gives you a considerable + amount of freedom. It does take a bit of management, but any + project should be ready to deal with it. + On the other hand, there are many files that can't be merged using + text merge techniques. Straight text merge programs like "diff3" + are guaranteed to fail on executables (with relative branch + statements), files with self-referential counts stored in the file + (such as TAGS files), or files with relative motion statements in + them (such as Frame MIF files, many postscript files). They + aren't all binary files. -+3C.9 What is "checkout -s"? How is it different from "checkout -c"? + For these types of files, and many others, there are only two + solutions: + + 1. Complex merge tools that are intimately aware of the contents + of the files to be merged. (ClearCase, and probably others, + allow you to define your own "files types" with associated + "merge tools".) + + 2. Serialization of access to the file. The only technical + solution to the problem of serialization is "locking". + + + Since you can call a program that offers: + + "Which one do you want? A/B?" + + a "merge tool", more and more merge tools will appear which can be + hooked into a merge-intensive program like CVS. Think of a bitmap + "merge" tool that displays the bitmaps on the screen and offers a + "paint" interface to allow you to cut and paste, overlay, invert + or fuse the two images such that the result is a "merged" file. + + My conclusion is that the need for locking is temporary, awaiting + better technology. For large development groups, locking is not + an alternative to merging for text files. + + + 3C.9 What is "checkout -s"? How is it different from "checkout -c"? The '-c' and '-s' options to "checkout" both cause the modules file to appear on standard output, but formatted differently. @@ -2739,17 +3195,13 @@ toward the beginning of this document. "checkout -s" lists the modules file sorted by "status" field, then by module name. The status field was intended to allow you to mark modules with strings of your choice to get a quick sorted - report based on the data you chose to put in the status fields. I + report based on the data you chose to put in the status fields. I have used it for priority ("Showstopper", etc as tied into a bug database), for porting status ("Ported", "Compiled", etc. when porting a large collection of modules), for "assignee" (the person responsible for maintenance), and for "test suite" (which automatic test procedure to run for a particular module). - [[CVS 1.3 fails to handle all the flags you can put into the - modules file. The '-l' switch in particular causes "checkout -c" - to dump core on some systems.]] - ---------------- -- Section 3D -- "commit", "ci", "com" @@ -2758,13 +3210,13 @@ toward the beginning of this document. **** Questions: 3D.1 What is "commit" for? -=3D.2 If I edit ten files, do I have to type "commit" ten times? + 3D.2 If I edit ten files, do I have to type "commit" ten times? 3D.3 Explain: cvs commit: Up-to-date check failed for `<file>' 3D.4 What happens if two people try to "commit" conflicting changes? 3D.5 I committed something and I don't like it. How do I remove it? -=3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch -=3D.7 Why does "commit -r <branch_tag>" put new files in the attic? -+3D.8 Why does "commit -r <rev>" ignore <rev> on an added file? + 3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch + 3D.7 Why does "commit -r <tag/rev>" put newly added files in the Attic? + 3D.8 Why would a "commit" of a newly added file not produce rev 1.1? **** Answers: @@ -2775,11 +3227,12 @@ toward the beginning of this document. to other users. -=3D.2 If I edit ten files, do I have to type "commit" ten times? + 3D.2 If I edit ten files, do I have to type "commit" ten times? - No. The "commit" command will take multiple filenames on the - command line and commit them all with the same log message. - If the file is unchanged, CVS will skip it. + No. The "commit" command will take multiple filenames, directory + names and relative pathnames on the command line and commit them + all with the same log message. If a file is unchanged, even if it + is explicitly listed on the command line, CVS will skip it. Like all CVS commands, "commit" will work on the whole directory by default. Just type "cvs commit" to tell CVS to commit all @@ -2798,10 +3251,10 @@ toward the beginning of this document. In other words, someone committed a revision since you last executed "checkout", "update" or "commit". You must now execute "update" to merge the other person's changes into your working - file before "commit" will work. You are thus protected (somewhat) + file before "commit" will work. You are thus protected (somewhat) from a common form of race condition in source control systems, - where a second checkin of minor changes from the same base file - obliterates the changes made in the first. + where a checkin of a minor alteration of a second copy of the same + base file obliterates the changes made in the first. Normally, the "update" command's auto-merge should be followed by another round of building and testing before the "commit". @@ -2838,22 +3291,22 @@ toward the beginning of this document. independently. In the real world you have to talk/argue, read code, test and debug until the combined changes work again. - Welcome to simultaneous development. + Welcome to the world of parallel development. 3D.5 I committed something and I don't like it. How do I remove it? Though you *can* use the "admin -o" (synonym: "rcs -o") command to delete revisions, unless the file you committed is so embarrassing - that the need to eradicate it overrides the need for being - careful, you should just grab an old version of the file ("update - -p -r <previous-rev>" might help here) and commit it on top of the + that the need to eradicate it overrides the need to be careful, + you should just grab an old version of the file ("update -p -r + <previous-rev>" might help here) and commit it on top of the offending revision. See Section 3B on "admin". -=3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch + 3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch The message implies two things: @@ -2863,10 +3316,10 @@ toward the beginning of this document. 2. The tag named V3 is not a branch tag. - CVS remembers any "-r <tag/rev>" arguments handed to the - "checkout" or "update" commands. This is the "sticky" part. The - <tag/rev> is recorded as the CVS working branch, which is the - branch to which "commit" will add a new revision. + CVS records (i.e. makes "sticky") any "-r <tag/rev>" argument + handed to the "checkout" or "update" commands. The <tag/rev> is + recorded as the CVS working branch, which is the branch to which + "commit" will add a new revision. Branch tags are created when you use the -b switch on the "tag" or "rtag" commands. Branch tags are magic tags that don't create a @@ -2889,7 +3342,11 @@ toward the beginning of this document. If you don't want a branch and were just looking at an old revision, then you can move back to the Main Branch by typing: - cvs update -A {optional files, default is whole directory} + cvs update -A {files or dirs, default is '.'} + + or you can move to the branch named <branch_tag> by: + + cvs update -r <branch_tag> {files or dirs, default is '.'} Scenario2: @@ -2901,7 +3358,7 @@ toward the beginning of this document. cvs rtag -b -r <oldtag> <newtag> <module> - (It was not a big mistake. Branch-point tags can be useful. + (It was not a big mistake. Branch-point tags can be useful. But the <newtag> must have a different name.) If you don't know the <module> name or don't use "modules", @@ -2927,10 +3384,15 @@ toward the beginning of this document. Scenario3: - If you made the same mistake as in Scenario2, but really want + If you made the same mistake as in Scenario2 (of placing a + non-branch tag where you wanted a branch tag), but really want <oldtag> to be the name of your branch, you can execute a slightly different series of commands to rename it and move - your working directory onto the branch: + your working directory onto the branch. + + Warning: This is not a way to rename a branch tag. It is a way + to turn a non-branch tag into a branch tag with the + same name. cvs rtag -r <oldtag> <branch_point_tag> <module> cvs rtag -d <oldtag> <module> @@ -2941,63 +3403,44 @@ toward the beginning of this document. cvs rtag -d <branch_point_tag> <module> + Note: The unwieldy mixture of "tag" and "rtag" is mostly because you can't specify a revision (-r <tag>) to the "tag" command. - See 4C.3 for more details. + See 4C.3 for more info on creating a branch. -=3D.7 Why does "commit -r <branch_tag>" put new files in the attic? + 3D.7 Why does "commit -r <tag/rev>" put newly added files in the Attic? - This was a design choice. The Attic is a way to keep track of - files that are no longer on the Main Branch, or ones that were - *never* on the Main Branch. + If you specify "-r <rev>" (where <rev> is a dotted numeric number + like 2.4), it correctly sets the initial revision to <rev>, but it + also attaches the numeric <rev> as a sticky tag and throws the + file into the Attic. This is a bug. The obvious solution is to + move the file out of the Attic into the associated Repository + directory and "update -A" the file. There are no Tags to clean up. - If the file doesn't already exist on the Main branch, committing - it directly to the BRANCH will stuff it into the Attic. Such - files are skipped over when checking out the Main Branch because - the file isn't on that branch. + If you specify "-r <tag>" to commit a newly added file, the <tag> + is treated like a <branch_tag>, which becomes a symbolic RCS label + pointing to the string '1', which can be considered to be the + "Main branch number" when the main branch is still at revision + 1.N. The file is also thrown into the Attic. See 4C.8 for a way + to recover from this. - If it didn't go into the Attic, you would be committing the new - file to the Main branch in addition to the Branch you are working - on. This is an undesirable side-effect. - - The file can be retrieved by using the "-r <branch_tag>" option on - a "checkout" or "update" command. + In fact, a plain "commit" without the "-r" will throw a newly + added file into the Attic if you added it to a directory checked + out on a branch. See 3A.[2-5]. See Section 4C, on Branching, for many more details. -+3D.8 Why does "commit -r <rev>" ignore <rev> on an added file? - - The sequence + 3D.8 Why would a "commit" of a newly added file not produce rev 1.1? - cvs add <file> - cvs commit -r <rev> <file> - - does not commit the new file <file> with revision <rev> as you - might expect. For newly added files (for which "update" would - display an 'A') the '-r' option is assumed to be a branch tag. If - <rev> is numeric, it is ignored. This might or might not be - changed in future revisions of CVS, but for now, the following - commands will allow you to set the revision of the file: (with - some restrictions) - - cvs add <file> - cvs commit <file> - cvs commit -r <rev> <file> - - The first commit causes CVS to look for the highest main branch - major number in all files in the directory. Normally it is '1', - but if you have a file of revision 3.27 in your directory, CVS - will find the '3' and create revision 3.1 for the first rev of - <file>. Normally, the first revision is 1.1. - - As long as <rev> is higher than the initial (calculated as in the - above) revision, the second commit will work as expected and force - a second commit even if the file hasn't changed, setting the file - revision to <rev>. + When committing a newly added file CVS looks for the highest main + branch major number in all files in the ./CVS/Entries file. + Normally it is '1', but if you have a file of revision 3.27 in + your directory, CVS will find the '3' and create revision 3.1 for + the first rev of <file>. Normally, the first revision is 1.1. ---------------- @@ -3007,40 +3450,43 @@ toward the beginning of this document. **** Questions: 3E.1 What is "diff" for? -=3E.2 Why did "diff" display nothing when I know there are later + 3E.2 Why did "diff" display nothing when I know there are later committed revisions in the Repository? -#3E.3 How do I display what changed in the Repository since I last + 3E.3 How do I display what changed in the Repository since I last executed "checkout", "update" or "commit"? -=3E.4 How do I display the difference between my working file and what + 3E.4 How do I display the difference between my working file and what I checked in last Thursday? -=3E.5 Why can't I pass the --unified option to "diff"? + 3E.5 Why can't I pass long options, like --unified, to "diff"? **** Answers: 3E.1 What is "diff" for? - To display the difference between your working file and a - committed revision: + 1. To display the difference between a working file and its BASE + revision (the revision last checked out, updated or committed): + + cvs diff <file> + + 2. To display the difference between a working file and a + committed revision of the same file: cvs diff -r <tag/rev> <file> - or between two committed revisions: + 3. To display the difference between two committed revisions of + the same file: cvs diff -r <tag1/rev1> -r <tag2/rev2> <file> - Without explicit file names, it "diffs" the whole directory. - - Without explicit revision numbers, it "diffs" your working file - against the BASE revision, which is the one last checked out, - updated or committed. + You can specify any number of <file> arguments. Without any + <file> arguments, it compares the whole directory. In the examples above, "-D <date>" may be substituted wherever - "-r <tag/rev>" appears. The revision a <date> refers to is the + "-r <tag/rev>" appears. The revision a <date> refers to is the revision that existed on that date. -=3E.2 Why did "diff" display nothing when I know there are later + 3E.2 Why did "diff" display nothing when I know there are later committed revisions in the Repository? By default, "diff" displays the difference between your working @@ -3054,7 +3500,7 @@ toward the beginning of this document. cvs diff -r HEAD <file> -#3E.3 How do I display what changed in the Repository since I last + 3E.3 How do I display what changed in the Repository since I last executed "checkout", "update" or "commit"? A special tag (interpreted by CVS -- it does not appear in the Tag @@ -3067,33 +3513,28 @@ toward the beginning of this document. cvs diff -r BASE -r HEAD <file> -=3E.4 How do I display the difference between my working file and what + 3E.4 How do I display the difference between my working file and what I checked in last Thursday? cvs diff -D "last Thursday" <file> - where "last Thursday" is a date string. To be more precise, the + where "last Thursday" is a date string. To be more precise, the argument to the '-D' option is a timestamp. Many formats are accepted. See the man page under "-D date_spec" for details. -=3E.5 Why can't I pass the --unified option to "diff"? - - There are a few reasons: - - 1. CVS passes through only arguments it knows about, because a few - arguments are captured and interpreted. + 3E.5 Why can't I pass long options, like --unified, to "diff"? - 2. CVS only parses single character '-X' arguments, not the FSF - long options. - - 3. If you didn't configure RCS and CVS to use the GNU version of - diff, long options wouldn't work even if future versions of CVS - acquire the ability to pass them through. + CVS only handles single character '-X' arguments, not the FSF long + options. CVS also passes through only arguments it knows about, + because a few arguments are captured and interpreted by CVS. + If you didn't configure RCS and CVS to use the GNU version of + diff, long options wouldn't work even if future versions of CVS + acquire the ability to pass them through. Most of the long options have equivalent single-character options, - which do work. The "--unified" option is equivalent to '-u' in + which do work. The "--unified" option is equivalent to '-u' in revisions of GNU diff since 1.15. @@ -3105,10 +3546,10 @@ toward the beginning of this document. **** Questions: 3F.1 What is "export" for? -=3F.2 Why does it remove the RCS keywords so I can't use the "ident" + 3F.2 Why does it remove the RCS keywords so I can't use the "ident" command on the source files? -=3F.3 Can I override the '-kv' flag CVS passes to RCS? -=3F.4 Why the hell not? + 3F.3 Can I override the '-kv' flag CVS passes to RCS? + 3F.4 Why doesn't "export" have a '-k' flag like "import" does? 3F.5 Why does "export -D" check out every file in the Attic? @@ -3123,16 +3564,16 @@ toward the beginning of this document. RCS keywords from the files. -=3F.2 Why does it remove the RCS keywords so I can't use the "ident" + 3F.2 Why does it remove the RCS keywords so I can't use the "ident" command on the source files? It removes the RCS keywords, so that if the recipient of the exported sources checks them into another set of RCS files (with or without CVS), and then makes modifications through RCS or CVS commands, the revision numbers that they had when you exported - them will be preserved. (That ident no longer works is just an + them will be preserved. (That ident no longer works is just an unfortunate side effect.) - + The theory is that you are exporting the sources to someone else who will make independent changes, and at some point you or they will want to know what revisions from your Repository they started @@ -3140,7 +3581,7 @@ toward the beginning of this document. merge changes). A better way to handle this situation would be to give them their - own branch of your Repository. They would need to remember to + own branch of your Repository. They would need to remember to checkin the exported sources with RCS IDs intact (ci -k) so that their changes would get revision numbers from the branch, rather than starting at 1.1 again. Perhaps a future version of CVS will @@ -3149,22 +3590,20 @@ toward the beginning of this document. Contributed by Dan Franklin -=3F.3 Can I override the '-kv' flag CVS passes to RCS? + 3F.3 Can I override the '-kv' flag CVS passes to RCS? - Not in CVS 1.3. Maybe later. + Not as of CVS version 1.4. -=3F.4 Why the hell not? + 3F.4 Why doesn't "export" have a '-k' flag like "import" does? Export is intended for a specific purpose -- to remove all trace - of revision control on the way *out* of CVS. Maybe in the future - CVS will allow the -kv default to be overridden. + of revision control on the way *out* of CVS. 3F.5 Why does "export -D" check out every file in the Attic? - See the explanation of the same problem with "update -D" - contained in section 5B. + See 5B.3 for an explanation of the same problem with "update". @@ -3178,16 +3617,17 @@ toward the beginning of this document. 3G.2 Of what use is it? 3G.3 What is this, Big Brother? 3G.4 I deleted my working directory and "history" still says I have - it checked out. How do I fix it? + it checked out. How do I fix it? 3G.5 So I *can* edit the History file? 3G.6 Why does the history file grow so quickly? 3G.7 What is the difference between "cvs history -r <tag/rev>" and "cvs history -t <tag>"? 3G.8 Why does "cvs history -c -t <tag>" fail to print anything? 3G.9 "cvs history -a -o" only printed one line for each checked-out - module. Shouldn't it print all the directories where the + module. Shouldn't it print all the directories where the modules are checked out? -=3G.10 I can't figure out "history", can you give me concrete examples? + 3G.10 I can't figure out "history", can you give me concrete examples? + 3G.11 Can we merge history files when we merge Repositories? **** Answers: @@ -3241,12 +3681,13 @@ toward the beginning of this document. 3G.4 I deleted my working directory and "history" still says I have - it checked out. How do I fix it? + it checked out. How do I fix it? - In later versions of CVS, you can use the '-f' option which - forcibly adds a "release" record to the history file. If your - version of "release" doesn't have the '-f' option, you have - to edit the $CVSROOT/CVSROOT/history file. + You can use "release -f" to forcibly add a "release" record to the + history file for a working directory associated with a "module". + If your version of "release" doesn't have the '-f' option, or you + checked out the directory using a relative path, you have to edit + the $CVSROOT/CVSROOT/history file. You can remove the last 'O' line in the history file referring to the module in question or add an 'F' record. @@ -3265,7 +3706,7 @@ toward the beginning of this document. It stores 'U' records, which come in handy sometimes when you are tracking whether people have updated each other's code - before testing. There should (and probably will sometime) be a + before testing. There should (and probably will sometime) be a way to choose what kinds of events go into the history file. The contributed "cln_hist.pl" script will remove all the 'U' @@ -3281,7 +3722,7 @@ toward the beginning of this document. of the given name was added. The '-r' option was intended to search all files looking for the - <tag> in the RCS files. It takes forever and needs to be + <tag> in the RCS files. It takes forever and needs to be rewritten. @@ -3289,11 +3730,20 @@ toward the beginning of this document. You have been using "tag" instead of "rtag". The "tag" command currently doesn't store a history record. This is another remnant - of CVS's earlier firm belief in "modules". + of CVS's earlier firm belief in "modules". But it also has a + basis in how "rtag" and "tag" were originally used. + + "rtag" was intended for large-scale tagging of large chunks of the + Repository, an event work recording. "tag" was intended for + adding and updating tags on a few files or directories, though it + could also be used to tag the entire checked-out working tree when + there is no module defined to match the tree or when the working + tree is the only place where the right collection of revisions to + tag can be found. 3G.9 "cvs history -a -o" only printed one line for each checked-out - module. Shouldn't it print all the directories where the + module. Shouldn't it print all the directories where the modules are checked out? Not as designed. @@ -3311,7 +3761,7 @@ toward the beginning of this document. the default history report. -=3G.10 I can't figure out "history", can you give me concrete examples? + 3G.10 I can't figure out "history", can you give me concrete examples? Default output selects records only for the user who executes the "history" command. To see records for other users, add one or @@ -3332,39 +3782,60 @@ toward the beginning of this document. * Module report on "module": -m module + 3G.11 Can we merge history files when we merge Repositories? + + Assuming that the two Repositories have different sets of + pathnames, it should be possible to merge two history files by + sorting them together by the timestamp fields. + + You should be able to run: + + sort +0.1 ${dir1}/history ${dir2}/history > history + + + If you "diff" a standard history file before and after such a + sort, you might see other differences caused by garbage (split + lines, nulls, etc) in the file. If your Repository is mounted + through NFS onto multiple machines you will also see a few + differences caused by different clocks on different machines. + (Especially if you don't use NTP to keep the clocks in sync.) + + + ---------------- -- Section 3H -- "import", "im", "imp" ---------------- **** Questions: -=3H.1 What is "import" for? -=3H.2 How am I supposed to use "import"? -=3H.3 Why does import put files on a branch? Why can't you put it on - the Main Trunk and let me work on a branch? + 3H.1 What is "import" for? + 3H.2 How am I supposed to use "import"? + 3H.3 Why does import put files on a branch? Why can't I work on the + main trunk instead of a Vendor branch? 3H.4 Is there any way to import binary files? -=3H.5 Why does "import" corrupt some binary files? - 3H.6 How do I keep "import" from expanding all the $\Revision$ strings - to be 1.1.1.1? -#3H.7 I imported some files for the Yarg compiler that compiles files + 3H.5 Why does "import" corrupt some binary files? + 3H.6 How do I retain the original $\Revision$ strings in the sources? +=3H.7 I imported some files for the Yarg compiler that compiles files with a suffix of ".yarg" and whose comment prefix is "YARG> ". When I check them out, they will no longer compile because they - have this junk in them. Why? + have this junk in them. Why? 3H.8 How do I make "import" save the timestamps on the original files? - 3H.9 Why didn't "import" ignore the directories I told it to? - 3H.10 Why can't I "import" 3 releases on different branches? - 3H.11 What do I do if the Vendor adds or deletes files between releases? - 3H.12 What about if the Vendor changes the names of files or + 3H.9 Why can't I "import" 3 releases on different branches? + 3H.10 What do I do if the Vendor adds or deletes files between releases? + 3H.11 What about if the Vendor changes the names of files or directories, or rearranges the whole structure between releases? - 3H.13 I thought "import" was for Vendor releases, why would I use it + 3H.12 I thought "import" was for Vendor releases, why would I use it for code of my own? Do I have to use import? -=3H.14 How do I import a large Vendor release? -+3H.15 Explain: ERROR: cannot create link to <file>: Permission denied + 3H.13 How do I import a large Vendor release? + 3H.14 Explain: ERROR: cannot create link to <file>: Permission denied + 3H.15 Where does the -m <message> go when the file doesn't change? + 3H.16 How do I "import" just the files ignored by a previous "import"? + 3H.17 Why did "import" ignore all the symlinks? **** Answers: -=3H.1 What is "import" for? + 3H.1 What is "import" for? The "import" command is a fast way to insert a whole tree of files into CVS. @@ -3372,7 +3843,7 @@ toward the beginning of this document. The first "import" to a particular file within the Repository creates an RCS file with a single revision on the "Vendor branch." Subsequent "import"s of the same file within the Repository append - a new revision onto the Vendor branch. It does not, as some seem + a new revision onto the Vendor branch. It does not, as some seem to believe, create a new branch for each "import". All "imports" are appended to the single Vendor branch. @@ -3387,36 +3858,59 @@ toward the beginning of this document. See 4C.6 and 4C.15 -=3H.2 How am I supposed to use "import"? + 3H.2 How am I supposed to use "import"? Create a source directory containing only the files you want to - import. Make sure you clean up any cruft left over from previous + import. Make sure you clean up any cruft left over from previous builds or editing. You want to make sure that the directory contains only what you want to call "source" from which everything else is built. + If this is not the first import from this "Vendor", you should + also compare the output of "find . ! -name CVS -print | sort" + executed both at the head of a checked out working directory and + at the head of the sources to be imported. If you find any + deleted or renamed files, you have to deal with them by hand. + (See 4B.8 on renaming.) + "cd" into your source directory and type: cvs import -m "Message" <repos> <Vendor-Tag> <Release-Tag> + where <repos> is the relative directory pathname within the + Repository that corresponds to the sources you are importing. - where <repos> is a relative directory pathname within the - Repository. + You might also consider using the "-I !" option to avoid ignoring + anything. It is easier to remove bogus files from the Repository + than to create a sparse tree of the ignored files and rerun + "import". For example, if the FSF, CVS, Make and I are still active in the year 2015, I'll import version 89.53 of GNU make this way: cvs import -m "GNUmake V89.53" gnu/make GNU GNUMAKE_89_53 - See 3H.14 for more details. + See 3H.13 for more details. + + 3H.3 Why does import put files on a branch? Why can't I work on the + main trunk instead of a Vendor branch? -=3H.3 Why does import put files on a branch? Why can't you put it on - the Main Trunk and let me work on a branch? + This was a Design choice. The Vendor branch is the way "import" + deals with a Vendor release. It is a solution to the Engineering + problem of how to merge multiple external releases of + Vendor-supplied sources into your ongoing work. The Vendor + releases are kept on a separate, special, "Vendor" branch and your + work is kept on the RCS trunk. New Vendor releases are imported + onto the Vendor branch and then merged into your work, if there is + any, on the trunk. - Design choice. If you don't like the Vendor branch, you can use - the RCS "ci" command to generate all the RCS (",v") files and move - them into the Repository directly. + This way, you can use CVS to find out not only about your work, + but you can also find out what the Vendor changed by diffing + between two of the Release Tags you handed to "import". + + CVS was designed to work this way. If you use CVS in some other + way, you should think carefully about what you are doing. Note that the CVS "Main Branch" and the RCS Main Trunk are not the same. Placing files on the Vendor Branch doesn't keep you from @@ -3425,22 +3919,54 @@ toward the beginning of this document. See Section 4C, on Branching. + If you are not working with 3rd party (i.e. Vendor) sources, you + can skip the "import" and avoid the Vendor branch entirely. It + works just as well to move pre-existing RCS files into Repository + directories. + + You can create a whole Repository tree by copying a directory + hierarchy of normal source files directly into the Repository and + applying CVS to it. Here's an idea you should *test* before using: + + cd <your source tree> + set source = `pwd` + set module = xyzzy <<== Your choice of directory name + mkdir $CVSROOT/$module + cd $CVSROOT/$module + (cd $source; tar cf - .) | tar xvpBf - + find . -type f -exec ci -t-Original. {} \; + + The RCS "ci" command, without -u or -l options, will turn your + source file into an RCS (",v") and delete the original source. + + 3H.4 Is there any way to import binary files? + If you configured CVS to use the GNU version of "diff" and + "diff3", then you can import any kind of file. + + Binary files with RCS keywords in them are a problem, since you + don't want them to expand. + + If the tree you are about to "import" is entirely filled with + binary files, you can use the '-ko' option on "import". + Otherwise, I would run the import normally, then fix the binary + files as described below in 3H.5. + See 4D.1 on Binary files. -=3H.5 Why does "import" corrupt some binary files? + 3H.5 Why does "import" corrupt some binary files? The RCS "co" command, when it is invoked by a CVS "checkout" or "update" (or after a "commit") command, searches for and expands a list of keywords within the file. They are documented in the RCS - "co" man page. Strings such as "$\Id$" (or "$\Id:"), or + "co" man page. Strings such as "$\Id$" (or "$\Id:"), or "$\Revision$" (or "$\Revision:") are altered to the include the indicated information. [[Note: The keywords should appear in the text without the '\' - character I have inserted to *avoid* expansion here. The only + character I have inserted to *avoid* expansion here. The only real RCS keywords in this document are at the top of the file, where I store the Revision and Date.]] @@ -3453,40 +3979,43 @@ toward the beginning of this document. rm <file> cvs update <file> + After an import that didn't use '-ko' (because the whole tree + wasn't of binary files) you should fix up the binary files as + described above before checking out any new copies of the files + and before updating any working directories you checked out + earlier. + See 4D.1 on Binary files. - 3H.6 How do I keep "import" from expanding all the $\Revision$ strings - to be 1.1.1.1? + 3H.6 How do I retain the original $\Revision$ strings in the sources? If you want to leave old RCS keywords as they are, you can use the - '-ko' trick described above. In the future, "import" might - sprout a '-ko' option of its own, so you don't have to execute two - commands. + '-ko' tricks described above. -#3H.7 I imported some files for the Yarg compiler that compiles files +=3H.7 I imported some files for the Yarg compiler that compiles files with a suffix of ".yarg" and whose comment prefix is "YARG> ". When I check them out, they will no longer compile because they - have this junk in them. Why? + have this junk in them. Why? YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG> YARG> $\Log: - # Revision 1.3 1998/03/03 00:16:16 bubba + # Revision 1.3 1998/03/03 00:16:16 bubba # What is 2+2 anyway? # - # Revision 1.2 1998/03/03 00:15:15 bubba + # Revision 1.2 1998/03/03 00:15:15 bubba # Added scorekeeping. YARG> YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG>YARG> - Well bubba, "Yarg" hasn't hit the big time yet. Neither RCS nor + Well bubba, "Yarg" hasn't hit the big time yet. Neither RCS nor CVS know about your suffix or your comment prefix. So you have two choices: 1. Check out the Yarg-less module, and tell all the files about - your comment prefix. Visit each directory and type: + your comment prefix. Visit each directory and type: cvs admin -c"YARG> " *.yarg @@ -3498,12 +4027,16 @@ toward the beginning of this document. Then save any changes you made, remove all the "*.yarg" files and grab new copies from the Repository: - rm *.yarg (or: find . -name '*.yarg' -exec rm {} ';') + rm *.yarg + (or: find . -name '*.yarg' -exec rm {} ';') + (or: find . -name '*.yarg' -print | xargs rm) + (or: find . -name '*.yarg' -print0 | xargs -0 rm + if you have spaces in filenames and the GNU find/xargs.) cvs update It might be faster to remove the whole directory and check it out again. - + 2. Change the import.c file in the CVS sources and add the .yarg suffix, along with the "YARG> " comment prefix to the "comtable" array. @@ -3513,24 +4046,18 @@ toward the beginning of this document. the table contained in the "rcsfnms.c" file. Then delete the imported files from the Repository and - re-"import" the sources. + re-"import" the sources. 3H.8 How do I make "import" save the timestamps on the original files? - In the released CVS 1.3, there is no way. In the coming patch - release, there will supposedly be a '-d' option to save the dates - of the files rather than the current date. - - See 4D.8 for more details. - - - 3H.9 Why didn't "import" ignore the directories I told it to? + Use "import -d" to save the current timestamps on the files as the + RCS revision times. - See 2D.11. + See 4D.8 for another aspect of file timestamps. - 3H.10 Why can't I "import" 3 releases on different branches? + 3H.9 Why can't I "import" 3 releases on different branches? I'll bet you typed something like this: @@ -3567,28 +4094,32 @@ toward the beginning of this document. cvs import VENDOR Version4 - 3H.11 What do I do if the Vendor adds or deletes files between releases? + 3H.10 What do I do if the Vendor adds or deletes files between releases? Added files show up with no extra effort. To handle "removed" files, you should always compare the tree structure of the new release against the one you have in your Repository. If the Vendor has removed files since the previous release, go into a working directory containing your current version of the sources - and "remove" (followed by "commit" to make it really take effect) - each file that is no longer in the latest release. + and "cvs remove" (followed by "cvs commit" to make it really take + effect) each file that is no longer in the latest release. Using this scheme will allow you to "checkout" any version of the vendor's code, with the correct revisions and files, by using "checkout -r Version[234]". + Renames are harder to find, since you have to compare file + contents to determine that one has occurred. If you notice one, + see 4B.8 on renaming files. - 3H.12 What about if the Vendor changes the names of files or + + 3H.11 What about if the Vendor changes the names of files or directories, or rearranges the whole structure between releases? Currently CVS can't handle this cleanly. It requires "renaming" a bunch of files or directories. - See 4B.9 on "renaming" for more details. + See 4B.8 on "renaming" for more details. What I generally do is to close the Repository for a while and make changes in both the Repository and in a copy of the vendor @@ -3598,18 +4129,22 @@ toward the beginning of this document. have to use the new, or completely different Makefiles. - 3H.13 I thought "import" was for Vendor releases, why would I use it + 3H.12 I thought "import" was for Vendor releases, why would I use it for code of my own? Do I have to use import? - For code you produce yourself, "import" is a convenience for - fast insertion. It is not necessary. You can just as easily - checkin all the files using the RCS "ci" command and move the - resulting ",v" files into the Repository. + For code you produce yourself, "import" is a convenience for fast + insertion of whole trees. It is not necessary. You can just as + easily create ",v" files using the RCS "ci" command and move + them directly into the Repository. + + Other than the CVSROOT directory, the Repository consists entirely + of directories of ",v" files. The Repository contains no other + state information. See Section 4B, on Setting up and Managing the Repository. -=3H.14 How do I import a large Vendor release? + 3H.13 How do I import a large Vendor release? When the sum of the changes made by the Vendor and the changes made by local developers is small, "import" is not a big @@ -3620,6 +4155,8 @@ toward the beginning of this document. questions in Section 4C dealing with branch merges and Vendor branch merges. + 0. If this is not the first import of this code, before starting, + rtag the whole directory you will be changing. 1. The first step is to make sure the structure of the new files matches the structure of the current Repository. @@ -3637,22 +4174,36 @@ toward the beginning of this document. "cvs remove". The command "comm -23 files.old files.new" will show you a list of files that need to be removed. - 4. If they renamed any files, see 4B.9 on renaming files. + You should examine the list first to see if any have been + renamed rather than simply deleted. + + 4. If they renamed any files, see 4B.8 on renaming files. + + 5. Remember to *SAVE* the output from the import command. - 5. When you have dealt with removed and renamed files, then you + 6. When you have dealt with removed and renamed files, then you can execute the import: cd <new source> - cvs import -m "Message" <repos> <VendorTag> <ReleaseTag> + cvs import -I ! -m "Message" <repos> <VendorTag> <ReleaseTag> + Where + "-I !" is an optional argument that keeps "import" from + ignoring files. The comparison of the "find" + commands above will probably avoid the need for + this, but it is easier to remove files from the + Repository than to run a subset "import" to catch + just the ignored files. + [You might have to quote or backwhack the '!'.] + Message is the log message to be stored in the RCS files. <repos> is a relative path to a directory within the Repository. The directory <new source> must be at the same relative level within the new sources as - the <repos> you give is within the Repository. (I + the <repos> you give is within the Repository. (I realize this is not obvious. Experiment first.) <VendorTag> is a Tag used to identify the Vendor who sent you @@ -3660,14 +4211,12 @@ toward the beginning of this document. the same <repos> *must* use the same VendorTag. You can find it later by using the "log" command. - <ReleaseTag> is a Tag used to identify the particular release + <ReleaseTag> is a Tag used to identify the particular release of the software you are importing. It must be unique and should be mnemonic -- at least include the revision number in it. (Note: you can't use '.' characters in a Tag. Substitute '_' or '-'.) - 6. *SAVE* the output from the import command. - 7. There will be six categories of files to deal with. (Actually there are eight, but you have already dealt with "removed" and "renamed" files.) @@ -3681,9 +4230,7 @@ toward the beginning of this document. CVS prints: I filename You'll need to examine it to see if it *should* have been - ignored. Alternatively, you can examine every file in the - "find" at the beginning and use the "import -I !" - option to avoid ignoring anything. + ignored. If you use "-I !", nothing will be ignored. b. Symbolic link. @@ -3700,7 +4247,8 @@ toward the beginning of this document. CVS creates a new file in the Repository. You don't have to do anything to the file, but you might have to - change Makefiles to refer to it. + change Makefiles to refer to it if this is really a new + file. d. A file unchanged by the Vendor since its last release. @@ -3733,17 +4281,138 @@ toward the beginning of this document. time), you must execute: cvs update -j <PreviousReleaseTag> -j <ReleaseTag> + or + cvs update -j <VendorTag:yesterday> -j <VendorTag> It will print either 'M' (if no overlaps) or 'C', if overlaps. If a 'C' shows up, you'll need to edit the file by hand. - Then, for every file, you'll need to execute"cvs commit". + Then, for every file, you'll need to execute "cvs commit". See the part of Section 4C dealing with branch merges. -+3H.15 Explain: ERROR: cannot create link to <file>: Permission denied + 8. If you are truly performing a large import, you will most + likely need help. Managing those people is another problem + area. + + Since the merge of the Vendor branch is just like any other + merge, you should read section 4C for more info about + performing and cleaning up merges. + + The larger the import, and the larger the group of people + involved, the more often you should use "tag" and "rtag" to + record even trivial milestones. See 4C.14, especially the + "paranoid" section. + + Before starting the import, you should install and test a + "commitinfo" procedure to record all commits in a file or via + Email to a mail archive. Along with the tags you placed on the + Repository before the import, this archive will help to track + what was changed, if problems occur + + There are four stages to the recovery: + + A. Parcel out the work -- Effective Emacs Engineering. + + As input to the assignment process, you might want to + examine the tree and record the last person who changed the + file. You can also research, if you don't already know, who + is expert in each area of the software. + + Examine the import log (you saved the output, right?), + estimate how much work is involved in each area and assign + groups of files to individual developers. Unless some + directory is immense, it is easier to manage if you assign + whole directories to one person. + + Keep a list. Suggest a completion date/time. Tell them to + "commit" the file when they are finished with the merge. + If you tagged the Repository before starting the import, you + should have no trouble figuring out what happened. + + If you can, find out (or tell them) which working directory + to use. You should verify that the working directory they + use is on the Main Branch ("update -A") and without modified + files. + + If you trust your crew, have them notify you by Email. Have + them send you the output from "cvs update" in their working + directory. You might have to poll some people until you are + certain they have finished, or have given up. (This is not + an invention. I've heard a false, "Yeah, sure. I finished + yesterday," more times that you'd believe.) + + When all reports are in, go on to the Source Verification + stage. + + B. Source Verification -- CVS and other Tools. + + If you didn't dictate which ones to use, find all working + directories and run "cvs -n update" in all of them. The + history command and the "commitinfo" log you set up might + help to find checked out working directories. + + Sticky conflict flags will help, but they can't recover from + sloppiness or incompetence. You might want to check + everything out into a tree and grep for the parts of the + merge conflict markers CVS doesn't look for. CVS looks for + the string '^>>>>>>> '. The merge operation also puts + '^<<<<<<< ' and '^======= ' markers in the file that + careless developers might leave there. + + If you find problems simply by looking at the source files + and working directories, start the flogging now. Resolving + the textual conflicts is the easy part. Weed the turkeys + out before reaching the next part of the cleanup -- the + resolution of logical conflicts. + + Then apply a set of post-commit tags. + + C. Logical Verification -- Diff and powerful eyeballs. + + No source control system can solve the problem of resolving + distributed conflicts in program logic. If you change the + argument template for function A (defined in file A.c) and + add new calls to function A from within function B (defined + in file B.c) using the old argument format, you are outside + the realm of CVS's competence. + + Assign someone to understand what the Vendor changed by + running "cvs diff -c -r <PreviousReleaseTag> <ReleaseTag>", + where the tags were those handed to the last two invocations + of "import". + + Then have the same person compare that output (logically or + you can actually diff the diffs) to the output of the + similar "cvs diff -c -r <pre-import-tag> <post-commit-tag>". + The two sets of differences should be almost identical. + They should both show only the work *you* have performed. + + D. Product Verification -- Build and Test. + + Don't let your help off the hook until you verify that the + merge actually produced something that can compile and pass + tests. Compiling should really be part of the logical + verification phase, but you should test the output of the + build system before declaring victory and releasing the + troops. + + + 9. After it is all built, apply another set of tags to mark the + end of the "import process". You can delete the intermediate + tags you added during source and logic testing, but keep the + "pre-import" and "post-import" tags forever. + + + Of course, experience can tell you when to skip a step. But I'd + start out by considering each one as necessary unless you can + prove otherwise. + + + + 3H.14 Explain: ERROR: cannot create link to <file>: Permission denied This error appears when you try to execute a second (or later) "import" into the same module from a directory to which you don't @@ -3756,45 +4425,141 @@ toward the beginning of this document. "import" is supposed to be executed only in writable directories. + 3H.15 Where does the -m <message> go when the file doesn't change? + + The <message> handed to import is used as an RCS log message, but + only if the imported file changed since the last version on the + Vendor branch. If the imported file hasn't changed, then no new + revision is created. The <ReleaseTag> is still applied, but to + the previous revision. So the Tags are still correct, but the + message is lost. + + Maybe it should be appended to the previous log message. But + currently it isn't. + + + 3H.16 How do I "import" just the files ignored by a previous "import"? + + A real answer follows, but first, an editorial: + + I am now convinced that you should always use the "-I !" + option. Removing a few extraneous files from the Repository + is a lot easier than the recovery step described below. + + + Let's assume your original import procedure was: + (We assume there is enough disk space in /tmp.) + + cd <head-of-vendor-tree> + cvs import -m 'xyz 1.3' gnu/xyz GNU GNUXYZ_1_3 | tee /tmp/IMP + + To import just the files ignored by "import", I would do this: + + 1. Create a list of the ignored files to import: + + cd <head-of-vendor-tree> + awk '/^I / {print $2}' /tmp/IMP | sed 's|^gnu/xyz/||' > /tmp/IG + [Edit the IG file to contain just the files you want.] + + 2. Then create a sparse directory by handing your list to the GNU + version of "tar", installed in many places as "gtar": + + mkdir /tmp/FIXUP + gtar -T /tmp/IG -c -f - . | (cd /tmp/FIXUP; gtar xvBf -) + + 3. Then rerun the import. Use the exact same command, but execute + it in the sparse directory tree you just created. And this + time, tell it not to ignore anything. + + cd /tmp/FIXUP + cvs import -I ! -m 'xyz 1.3' gnu/xyz GNU GNUXYZ_1_3 + + + 3H.17 Why did "import" ignore all the symlinks? + + This is another design choice. + + Like the Unix "tar" command, "import" could sprout an option to + follow symbolic links, but I don't think CVS will ever follow + symbolic links by default. + + Two possible future enhancements have been seriously discussed: + + 1. Treat symbolic links as data in its parent directory (the way + ClearCase does) in some sort of per-directory control file. + + 2. Treat symbolic links as version-controlled elements themselves, + whose data is the value of readlink(2). + + For now, they are simply ignored. + + If you want to save and reconstruct symlinks, you might want to + define a "checkout" or "update" program in the modules file which + could consult a file kept under CVS in your working directory and + make sure the specified links are in place. + + + ---------------- -- Section 3I -- "log", "lo", "rlog" ---------------- **** Questions: -=3I.1 What is "log" for? + 3I.1 What is "log" for? 3I.2 How do I extract the log entries between two revisions? -=3I.3 How do I extract the log entries on a whole branch? -=3I.4 How do I generate ChangeLogs from RCS logs? -=3I.5 Why does "log" tell me a file was committed exactly 5 hours later + 3I.3 How do I extract the log entries on a whole branch? + 3I.4 How do I generate ChangeLogs from RCS logs? + 3I.5 Why does "log" tell me a file was committed exactly 5 hours later than I know it was? **** Answers: -=3I.1 What is "log" for? + 3I.1 What is "log" for? To provide an interface to the RCS "rlog" command, which displays information about the underlying RCS files, including the revision - history and Tag (what RCS calls "symbol") list. + history and Tag (RCS calls it a "symbol") list. 3I.2 How do I extract the log entries between two revisions? If both <rev1> and <rev2> are on the same branch, you can get - what you are looking for with: + what you are looking for with: (If they aren't on the same branch + you'll either get an error or a display of the whole change log.) cvs log -r<rev1>:<rev2> <file> - If either <rev1> or <rev2> contain '-' characters, it will - complain and fail due to RCS's continued support of '-' as an - alternate range character. + If you want all the revisions on the branch from <rev1> to the end + of the branch <rev1> is on, you can use: + + cvs log -r<rev1>: <file> + + (If <rev1> is a numeric RCS symbol attached to a branch revision + with an even number of '.'s in it, you get the whole branch.) + + If you want all the revisions on the branch from the beginning of + the branch <rev2> is on up to revision <rev2>, you can use: + + cvs log -r:<rev2> <file> - <rev1> and <rev2> can be tag/symbol names, but they have to be - on the same branch, whether they are numeric or symbolic. + Note: Depending on whether <rev1> and <rev2> are: -=3I.3 How do I extract the log entries on a whole branch? + - numeric or symbolic + - in the file or not + - on the same branch or not + + the RCS "rlog" (and therefore the "cvs log") command will + display some combination of: + + - error messages + - (intuitively correct) partial log listings + - a display of the entire change log. + + + 3I.3 How do I extract the log entries on a whole branch? cvs log -r<rev> <file> @@ -3805,14 +4570,21 @@ toward the beginning of this document. within each file. Since these branch numbers are almost never the same in different files, this command is not all that useful. + The intuitive command (at least from the CVS perspective): + + cvs log -r<branch_tag> <file> + + does not work. + 3I.4 How do I generate ChangeLogs from RCS logs? A program called rcs2log is distributed as part of GNU Emacs 19. - This program should also appear in the CVS FTP archive. + A (possibly older) version of this program appears in the contrib + directory of the cvs source tree. -=3I.5 Why does "log" tell me a file was committed exactly 5 hours later + 3I.5 Why does "log" tell me a file was committed exactly 5 hours later than I know it was? I can tell by this question that you were working in a time zone @@ -3847,7 +4619,7 @@ toward the beginning of this document. 3J.1 What is "patch" for? To produce a "diff" between tagged releases to be handed to the - "patch" command at other sites. This is the standard way that + "patch" command at other sites. This is the standard way that source patches are distributed on the network. @@ -3869,9 +4641,10 @@ toward the beginning of this document. The patch command will be able to merge such a "diff" into the remote source files. - If the version of "diff" you are using supports the '-u' option, - to produce the more compact "Unidiff" format, the latest - revisions of the patch command understand that too. + If you configured CVS to use a version of "diff" that supports the + '-u' option, you can produce a more compact "patch" in "unidiff" + format. The latest revisions of the patch command can parse and + apply patches in "unidiff" format. @@ -3883,76 +4656,72 @@ toward the beginning of this document. **** Questions: 3K.1 What is "release" for? - 3K.2 Why does release -d delete directories within my directory that - weren't ever in the CVS Repository? - 3K.3 Why can't I reverse a "cvs checkout path/name/subdir" with a + 3K.2 Why can't I reverse a "cvs checkout path/name/subdir" with a "cvs release path/name/subdir" without an "unknown module name"? - 3K.4 Why can't I "release" portions of a checked out directory? I + 3K.3 Why can't I "release" portions of a checked out directory? I should be able to "release" any file or sub-directory within my working directory. - 3K.5 I removed the tree that I was about to start working on. How do I + 3K.4 I removed the tree that I was about to start working on. How do I tell cvs that I want to release it if I don't have it anymore? - 3K.6 Why doesn't "release -d module" reverse a "checkout module"? - 3K.7 Why can't I release a module renamed with "cvs checkout -d"? + 3K.5 Why doesn't "release -d module" reverse a "checkout module"? + 3K.6 Why can't I release a module renamed with "cvs checkout -d"? **** Answers: 3K.1 What is "release" for? - To register that a module is no longer in use. It is intended + To register that a module is no longer in use. It is intended to reverse the effects of a "checkout" by adding a record to the history file to balance the checkout record and by optionally allowing you to delete the checked-out directory associated with the module name. - 3K.2 Why does release -d delete directories within my directory that - weren't ever in the CVS Repository? + 3K.2 Why can't I reverse a "cvs checkout path/name/subdir" with a + "cvs release path/name/subdir" without an "unknown module name"? A simplistic implementation. (I can say this -- I wrote it.) - The "release" function was written under the assumptions that the - "module name" is a first class, unavoidable interface to the - Repository, allowing no way to retrieve anything other than by - module name and a module is a self-contained entity with no - foreign directories allowed. Though it is easier to program that + The "release" function was written for CVS 1.2 under the + assumption that the "module name" is a first class, unavoidable + interface to the Repository, allowing no way to retrieve anything + other than by module name. Though it is easier to program that way, many users of CVS believe the modules support to be too primitive to allow such a limitation. - Since "release" was written, other parts of CVS broke those - assumptions. It will be upgraded slightly in the next release and - rewritten in the future. + Since "release" was written, other parts of CVS broke that + assumption. It needs to be revised. - 3K.3 Why can't I reverse a "cvs checkout path/name/subdir" with a - "cvs release path/name/subdir" without an "unknown module name"? - - Again, "release" is too primitive. It believes, truly - *believes* in modules, not relative paths. I can't *believe* - how many times I've been asked this. It was a hack of the - moment with a particular use in mind. I had no idea it was - going to cause so much trouble. I'll *fix* it already! :-) - - - 3K.4 Why can't I "release" portions of a checked out directory? I + 3K.3 Why can't I "release" portions of a checked out directory? I should be able to "release" any file or sub-directory within my working directory. - Again, "release" believes in modules. Breaking it into bits - wasn't part of the plan. In the future, "release" might become - sophisticated enough to handle both the reversal of a "checkout" - and the deletion of random portions of the working directory, but - it isn't that way now. + This isn't really a limitation in "release", per se. CVS doesn't + try to keep track of which files in which directories are "checked + out" and which are just lying there. You can delete directories + and "update" will not bring them back unless you add a special + "-d" option. + In other words, CVS doesn't keep track of how you adjust the + partition between files you consider part of your working set and + files that were checked out because they are part of the same + module or directory. And neither does "release". - 3K.5 I removed the tree that I was about to start working on. How do I + In future CVS releases, "release" might become sophisticated + enough to handle both the reversal of a "checkout" and the + deletion of random portions of the working directory, but it isn't + that way now. + + + 3K.4 I removed the tree that I was about to start working on. How do I tell cvs that I want to release it if I don't have it anymore? See 3G.4. - 3K.6 Why doesn't "release -d module" reverse a "checkout module"? + 3K.5 Why doesn't "release -d module" reverse a "checkout module"? It does, if you are using "module" in a way that "release" expects: a non-alias string in the left column of the "modules" @@ -3966,7 +4735,7 @@ toward the beginning of this document. Future versions of "release" will probably fix most of these. - 3K.7 Why can't I release a module renamed with "cvs checkout -d"? + 3K.6 Why can't I release a module renamed with "cvs checkout -d"? The current version of "release" doesn't know how to track the renaming option ('-d') of the "checkout" command. It will @@ -3983,9 +4752,9 @@ toward the beginning of this document. 3L.1 What is "remove" for? 3L.2 Why doesn't "remove" work on directories when it appears to try? 3L.3 I don't like removing files. Is there another way to ignore them? - 3L.4 I just removed a file. How do I resurrect it? - 3L.5 Why doesn't "remove" delete the file? Instead, it prints: - cvs remove: no files removed; use `rm' to remove the file first + 3L.4 I just removed a file. How do I resurrect it? + 3L.5 Why doesn't "remove" delete the file? Instead, it prints an + error message and tells me to remove the file by hand. **** Answers: @@ -4017,9 +4786,9 @@ toward the beginning of this document. The CVS "ignore" concept can't ignore files already in CVS. - 3L.4 I just removed a file. How do I resurrect it? + 3L.4 I just removed a file. How do I resurrect it? - If you executed "remove", but haven't typed "commit" (you can + If you executed "remove", but haven't typed "commit" (you can tell this by the 'R' notation that "update" prints next to the file), you can execute "add" to reverse the "remove". @@ -4028,11 +4797,11 @@ toward the beginning of this document. I use something like this: (csh-like syntax) - set repos = `cat CVS/Repository` + set repos = `cat ./CVS/Repository` mv $repos/Attic/filename,v $repos/filename,v (If you use relative paths in your Repository files, that first - line becomes: set repos = $CVSROOT/`cat CVS/Repository`) + line becomes: set repos = $CVSROOT/`cat ./CVS/Repository`) While a file is in the Attic, you can't "add" another file by the same name. To add such a file you either have to move it by @@ -4042,33 +4811,37 @@ toward the beginning of this document. them. If you execute: "update -r <oldtag>", files with <oldtag> attached to some revision will be taken from the normal Repository area and from the Attic. That's why you can't "add" a file with - the same name. "remove" only moves a file off the main branch, it + the same name. "remove" only moves a file off the main branch, it doesn't obliterate it. - 3L.5 Why doesn't "remove" delete the file? Instead, it prints: - cvs remove: no files removed; use `rm' to remove the file first + 3L.5 Why doesn't "remove" delete the file? Instead, it prints an + error message and tells me to remove the file by hand. - Design choice. Unix software written within last decade, usually + Design choice. Unix software written within last decade, usually requires an extra verification step, such as answering a question or adding a flag on the command line. CVS currently requires that - you delete the file first. + you delete the file first unless you specify the '-f' (force) + option, which deletes the file before performing "cvs remove". - Future versions of CVS might contain a '-f' switch that deletes - the existing file without complaining. ---------------- -- Section 3M -- "rtag", "rt", "rfreeze" ---------------- -(See the "tag" section below for questions in common with "rtag".) +(See the "tag" section below for the general questions about Tagging, which + "tag" and "rtag" share in common.) **** Questions: 3M.1 What is "rtag" for? - 3M.2 Why would you use "rtag"? It assumes a static Repository. + 3M.2 Why use "rtag"? It assumes no one is changing the Repository. + 3M.3 What revision does "rtag -r <tag1> <tag2>" actually put the tag on? + 3M.4 What happens if the tags are the same in "rtag -r <tag> <tag>"? + 3M.5 Why doesn't "rtag -b -r <branch_tag1> <branch_tag2>" rename or + duplicate a magic branch tag? **** Answers: @@ -4079,7 +4852,7 @@ toward the beginning of this document. of a module directly in the Repository. - 3M.2 Why would you use "rtag"? It assumes a static Repository. + 3M.2 Why use "rtag"? It assumes no one is changing the Repository. Though the "tag" command is more useful in marking the revisions you have in a particular working directory, "rtag" is @@ -4087,6 +4860,164 @@ toward the beginning of this document. release boundaries. + 3M.3 What revision does "rtag -r <tag1> <tag2>" actually put the tag on? + + In short, the '-r' option is another way to select the revision to + tag. The revision is selected the same way for all commands that + accept a "-r <tag/rev>" option. + + Depending on whether <tag1> is a <branch_tag>, or a non-branch + <tag> and on whether you use the '-b' option to "rtag", you get + four different results: + + 1. rtag -r <tag1> <tag2> + + Adds the non-branch tag <tag2> to the same revision that the + non-branch tag <tag1> is attached to. + + Example: + <tag1> --> TT1 + <tag2> --> TT2 + <file> --> Symbols: TT1:1.4 + After --> Symbols: TT1:1.4,TT2:1.4 + + + 2. rtag -r <branch_tag1> <tag2> + + Adds the non-branch tag <tag2> to the HEAD of (the highest + revision number on) the branch labelled with tag <branch_tag1>. + + Example: + <branch_tag1> --> BR1 + <tag2> --> TT2 + <file> --> Symbols: BR1:1.2.0.2 (1.2.2.5 is HEAD) + After --> Symbols: BR1:1.2.0.2,TT2:1.2.2.5 + + If the branch tagged by <branch_tag1> has not been created, + then the tag shows up on the branch point revision: + + Example: + <branch_tag1> --> BR1 + <tag2> --> TT2 + <file> --> Symbols: BR1:1.2.0.2 (No 1.2.X exists.) + After --> Symbols: BR1:1.2.0.2,TT2:1.2 + + + 3. rtag -b -r <tag1> <branch_tag2> + + Adds the magic branch tag <branch_tag2> to the revision that + the non-branch tag <tag1> is attached to, preparing it to be a + branch point. + + Example: + <tag1> --> TT1 + <branch_tag2> --> BR2 + <file> --> Symbol: TT1:1.4 + After --> Symbol: TT1:1.4, BR2:1.4.0.2 + + + 4. rtag -b -r <branch_tag1> <branch_tag2> + + Adds the magic branch tag <branch_tag2> to the revision at the + HEAD of (the highest revision number on) the branch labelled + with <branch_tag1>, preparing it to be a branch point. + + Example: + <branch_tag1> --> BR1 + <branch_tag2> --> BR2 + <file> --> Symbol: BR1:1.2.0.2 (1.2.2.5 is HEAD) + After --> Symbol: BR1:1.2.0.2,BR2:1.2.2.5.0.2 + + If the branch tagged by <branch_tag1> has not been created, + then the tag shows up as a second branch off the same + branch point revision: + + Example: + <branch_tag1> --> BR1 + <tag2> --> TT2 + <file> --> Symbols: BR1:1.2.0.2 (No 1.2.X exists.) + After --> Symbols: BR1:1.2.0.2,TT2:1.2.0.4 + + + + In all four cases above, if <tag2> already exists on the file, you + get an error unless you specify the '-F' option. + + In all four cases, if <tag1> does not exist on the file, <tag2> is + not added unless you specify the '-f' option. + + + 3M.4 What happens if the tags are the same in "rtag -r <tag> <tag>"? + + Again, there are four cases depending on whether <tag> is a + branch tag, or a non-branch tag and on whether you use the + '-b' option to "rtag": + + 1. rtag -r <tag> <tag> + + Is a no-op. It does nothing even with '-F' specified. + + If you add the '-f' option ("rtag -f -r <tag> <tag>"), then + <tag> is attached to the latest revision on the Main Branch if + the file does *not* already have <tag> on some revision. + + If the <tag> is already on the file, using "rtag -f" is still + a no-op. + + + 2. rtag -r <branch_tag> <branch_tag> + + Produces an error, since the <branch_tag> is already on some + revision of the file. + + But, "rtag -F -r <branch_tag> <branch_tag>" turns the magic + branch tag into a non-branch tag. + + Symbols: BR1:1.4.0.2 + becomes + Symbols: BR1:1.4 + + + 3. rtag -b -r <tag> <tag> + + Produces an error, since the <tag> is already on the file. + + But, "rtag -F -b -r <tag> <tag>" turns the non-branch + tag into a magic branch tag. + + Symbols: BR1:1.4 + becomes + Symbols: BR1:1.4.0.2 + + + 4. rtag -b -r <branch_tag> <branch_tag> + + Produces an error, since the <branch_tag> is already on the + file. + + But, "rtag -F -b -r <branch_tag> <branch_tag>" increments the + branch number. It essentially removes the branch and creates a + new one by the same name. + + Symbols: BR1:1.2.0.4 + becomes + Symbols: BR1:1.2.0.6 + + + + 3M.5 Why doesn't "rtag -b -r <branch_tag1> <branch_tag2>" rename or + duplicate a magic branch tag? + + None of the "tag" or "rtag" options rename anything. They only + apply (or, with the '-F' option, move) tags to specific revisions + in the file. + + See 3M.[3-4] above for details of how it works. + + To rename a non-branch tag, see 3O.9. + To rename a magic branch tag, see 4D.5 + + ---------------- -- Section 3N -- "status", "st", "stat" @@ -4094,14 +5025,15 @@ toward the beginning of this document. **** Questions: -=3N.1 What is "status" for? + 3N.1 What is "status" for? 3N.2 Why does "status" limit the File: at the top to 17 characters? -+3N.3 Shouldn't the status "Needs Checkout" be "Needs Update"? + 3N.3 Why does it print "Sticky" lines when the values are "(none)"? + 3N.4 Shouldn't the status "Needs Checkout" be "Needs Update"? **** Answers: -=3N.1 What is "status" for? + 3N.1 What is "status" for? To display the status of files, including the revision and branch you are working on and the existence of "sticky" information. @@ -4114,9 +5046,16 @@ toward the beginning of this document. not limited in length. -+3N.3 Shouldn't the status "Needs Checkout" be "Needs Update"? + 3N.3 Why does it print "Sticky" lines when the values are "(none)"? + + Oversight. It should probably elide lines without information. + + + 3N.4 Shouldn't the status "Needs Checkout" be "Needs Update"? - Probably. Maybe in future revisions. + Probably. + + [[Did this show up in CVS 1.4?]] @@ -4127,15 +5066,16 @@ toward the beginning of this document. **** Questions: 3O.1 What is "tag" for? -=3O.2 What is the difference between "tag" and "rtag"? -=3O.3 Why does "tag -b" not put a tag on the Branch Point revision? + 3O.2 What is the difference between "tag" and "rtag"? + 3O.3 Why does "tag -b" not put a tag on the Branch Point revision? How do I refer to the Branch Point? --3O.4 So "tag" labels a bunch of files. What do you use a Tag for? + 3O.4 So "{r}tag" labels a bunch of files. What do you use a Tag for? 3O.5 How do I get "tag" and "rtag" to send mail the way "commit" does? 3O.6 Why can't "tag" handle the '-r' option that "rtag" takes? --3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout - -r <tag>" somewhere else produce copy of my current files? -#3O.8 Why doesn't "tag" write a history record the way "rtag" does? + 3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout + -r <tag>" somewhere else produce copies of my current files? + 3O.8 Why doesn't "tag" write a history record the way "rtag" does? + 3O.9 How do I rename a <tag>? **** Answers: @@ -4146,20 +5086,21 @@ toward the beginning of this document. out, updated or committed in a working directory. -=3O.2 What is the difference between "tag" and "rtag"? + 3O.2 What is the difference between "tag" and "rtag"? The end result of both commands is that a <tag>, or symbolic name, - is attached to a particular revision of a collection of files. + is attached to a single revision in each of a collection of files. The differences lie in: 1. The collection of files they work on. "rtag" works on the collection of files referred to by a - "module" name, as defined in the "modules" file. + "module" name as defined in the "modules" file, or a relative + path within the Repository. - "tag" works on files and directories in the current working - directory. + "tag" works on files and directories specified on the command + line within the user's working directory. (Default is '.') Both commands recursively follow directory hierarchies within the named files and directories. @@ -4172,7 +5113,8 @@ toward the beginning of this document. "tag" places a tag on the BASE (i.e. last checked out, updated or committed) revision of each file found in the working - directory. + directory. (The BASE revision of a file is the one stored in + the ./CVS/Entries file.) 3. A different set of command line options. @@ -4185,12 +5127,13 @@ toward the beginning of this document. "history" file, while "tag" does not. -=3O.3 Why does "tag -b" not put a tag on the Branch Point revision? + 3O.3 Why does "tag -b" not put a tag on the Branch Point revision? How do I refer to the Branch Point? - Design decision. If everything works perfectly, the "update -j" - command will do the merge you need and you don't need to check up - on it by playing with the branch point revision. + This is probably an oversight, or a disbelief in the need for it. + If everything works perfectly, the "update -j" command will do the + merge you need and you don't need to check up on it by playing + with the branch point revision. The '-b' option attaches a magic branch tag to allow CVS later to figure out the branch point. The actual revision that <tag> is @@ -4198,10 +5141,12 @@ toward the beginning of this document. equivalent to references to the latest revision on the branch. There is no way to refer to the branch point without adding a - non-branch tag. See 4C.3 on Creating a Branch. + non-branch tag. You might want to add non-branch tags as a + habit and add branch tags later, possibly immediate after adding + the non-branch tag. See 4C.3 on Creating a Branch. --3O.4 So "tag" labels a bunch of files. What do you use a Tag for? + 3O.4 So "{r}tag" labels a bunch of files. What do you use a Tag for? You use it to "checkout" the labeled collection of files as a single object, referring to it by name. @@ -4221,12 +5166,12 @@ toward the beginning of this document. 1.1 1.1 1.1 1.1 /--1.1* <-*- <tag> 1.2*- 1.2 1.2 -1.2*- - 1.3 \- 1.3*- 1.3 / 1.3 + 1.3 \- 1.3*- 1.3 / 1.3 1.4 \ 1.4 / 1.4 \-1.5*- 1.5 1.6 - At some time in the past, the '*' versions were tagged. Think + At some time in the past, the '*' versions were tagged. Think of the <tag> as a handle attached to the curve drawn through the tagged revisions. When you pull on the handle, you get all the tagged revisions. Another way to look at it is that you draw a @@ -4267,16 +5212,16 @@ toward the beginning of this document. Oversight. The answer is probably "Fixed in a Future Release." --3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout - -r <tag>" somewhere else produce copy of my current files? + 3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout + -r <tag>" somewhere else produce copies of my current files? The only reason this would fail, other than misspelling the <tag> string, is that you didn't "commit" your work before "tagging" it. - Only committed revisions may be tagged. Modified files are not + Only committed revisions may be tagged. Modified files are not marked for later tagging. -#3O.8 Why doesn't "tag" write a history record the way "rtag" does? + 3O.8 Why doesn't "tag" write a history record the way "rtag" does? The "rtag" command was originally intended to place major "release" tags onto modules. The "tag" functionality was @@ -4287,10 +5232,36 @@ toward the beginning of this document. The significant event was the "rtag", which was recorded in the "history" file for the "history -T" option to work. - It turns out that "tag" is more useful than "rtag", so the model - has changed. Future revisions of CVS will probably store both - kinds of tags in the history file. + It turns out that "tag" is generally more useful than "rtag", so + the model has changed. Future revisions of CVS will probably + store both kinds of tags in the history file. + + + 3O.9 How do I rename a <tag>? + + For a procedure to rename a branch tag, See section 4D.5 + The following covers only non-branch tags. + + First, pick a <newtag> that is not in use. You could reuse + (i.e. move) an existing tag to the new revisions using the '-F' + option, but that will confuse matters when both tags are not + already on a file. (It will probably confuse "rtag -f" too.) + + Use "rtag" to place <newtag> only on revisions attached to + <oldtag> in the whole Repository, then delete the old one. + + cvs rtag -r <oldtag> <newtag> world + cvs rtag -d <oldtag> world. + + You can also checkout or update your working directory to the + <oldtag> and "tag" rather than "rtag" the result. But that + will take longer and it has the chance of producing conflicts. + + cvs update -r <oldtag> + cvs tag <newtag> + cvs tag -d <oldtag> + cvs update -A (or cvs update -r <previous_tag>) ---------------- @@ -4300,20 +5271,16 @@ toward the beginning of this document. **** Questions: 3P.1 What is "update" for? -=3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they + 3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they different for "cvs -n update"? 3P.3 What's the difference between "update" and "checkout"? -=3P.4 Why don't I get new files when I execute "update"? -#3P.5 Why does "update" say 'M' both for plain modified files and for - successful (i.e. conflict-free) merges? Aren't they different? -=3P.6 After a merge ("update" or "update -j"), why doesn't CVS remember - the conflict and not allow you to commit the result until the - conflict is resolved? + 3P.4 Why don't I get new files when I execute "update"? + 3P.5 Why does "update" say 'M' both for plain modified files and for + successful (i.e. conflict-free) merges? Aren't they different? + 3P.6 What's a "sticky conflict"? How does it know a conflict occurred? 3P.7 Is there a feature to tell me what I have changed, added and removed without changing anything? -=3P.8 Why does "cvs update" not flag directories that are not in the - Repository as it does with new files? - 3P.9 Why are all my files deleted when I execute "update"? + 3P.8 Why were all my files deleted when I executed "update"? **** Answers: @@ -4334,17 +5301,17 @@ toward the beginning of this document. cvs -n update {optional list of files} - 2. To merge changes made to the branch you are working on into - your working files. + 2. To merge changes made by others to the branch you are working + on into your working files. Each working directory is attached to a branch, usually the - Main branch. To merge changes made on your working branch by - other people into your working files, type: + Main branch. To merge changes made on your working branch + since your last checkout, update or commit, type: cvs update {optional list of files} - 3. To merge changes made to another branch into the branch you are + 3. To merge changes made on another branch into the branch you are working on (your "working branch"). If you want to grab a whole branch, from the branch point, @@ -4356,13 +5323,13 @@ toward the beginning of this document. If you want to grab the changes made between two tags or revisions, you type: - cvs update -j <tag1/rev1> -j <tag2/rev2> {optional files} + cvs update -j <tag1> -j <tag2> {optional files} (If you are working with a single file, the Tags could also be - revisions numbers. Unless you take really unusual care to - match revision numbers across different files (a waste of time - given the way Tags work), using revision numbers in places of - the Tags for multiple files would be meaningless.) + revisions numbers. Unless you take great care to match + revision numbers across different files (a waste of time given + the way Tags work), using revision numbers in place of the + Tags for multiple files would be meaningless.) 4. To move your working directory to another branch. @@ -4380,12 +5347,20 @@ toward the beginning of this document. cvs update -A {optional files} + If you have modified files in your working directory, this is + not a clean move. CVS will attempt to merge the changes + necessary to make it look like you made the same changes to the + new branch as you made in the old one. But if you do this + twice without resolving the merge conflicts each time, you can + lose work. + 5. To retrieve old revisions of files. This option is similar to 4 above but you are not restricted to - using a <branch_tag>. You may specify any revision or Tag with - '-r' and get the specified revision or the tagged revision: + using a <branch_tag>. You may specify any revision or <tag> + with '-r' and get the specified revision or the tagged + revision: cvs update -r <tag/rev> {optional files} @@ -4398,12 +5373,12 @@ toward the beginning of this document. and changing the files. -=3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they + 3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they different for "cvs -n update"? "cvs update" merges changes made to the Repository, since your last "checkout", "update" or "commit", into your working files. - You can think of it as "changing your BASE revision." + You can think of it as changing your BASE revision. "cvs update" prints lines beginning with: @@ -4417,16 +5392,15 @@ toward the beginning of this document. 2. after a merge, if it detected no conflicts. - 'C' after a merge, if it detected conflicts. - - You will need to remove the conflicts by editing the file. - Conflicts are surrounded by <<<<< and >>>>> markers. - + 'C' after a merge, if it detected conflicts. See 2D.7 and + 3P.6 for more info on conflict resolution and "sticky + conflicts." "cvs -n update" shows what it *would* do, rather than doing it. Or, another way of looking at it, "cvs -n update" displays the - relationship between your current BASE revisions and the latest - revisions in the Repository. + relationship between your current BASE revisions (identified in + your ./CVS/Entries file) and the HEAD revisions (the latest + revisions in the Repository). "cvs -n update" prints lines beginning with: @@ -4441,7 +5415,7 @@ toward the beginning of this document. See 4C.6 for what the letters mean when merging in from another - branch. The output is almost the same for a normal update if you + branch. The output is almost the same for a normal update if you consider the Repository as the branch and your working directory as the "trunk". @@ -4451,7 +5425,7 @@ toward the beginning of this document. See 3C.4 above. -=3P.4 Why don't I get new files when I execute "update"? + 3P.4 Why don't I get new files when I execute "update"? There are six reasons for nothing to happen during an "update": @@ -4467,12 +5441,12 @@ toward the beginning of this document. working files you are trying to "update". At some time in the past you checked out or updated your - directory with the "-r <tag>" or "-D <date>" option. Until you + directory with the "-r <tag>" or "-D <date>" option. Until you do it again with a different tag or date, or go back to the Main Branch with "update -A", you will never again see any updates. - 3. The ./CVS/Entries.static file exists and you are expecting a + 3. The ./CVS/Entries.Static file exists and you are expecting a new file. If your ./CVS administrative directory contains a file named @@ -4487,55 +5461,94 @@ toward the beginning of this document. 5. You typed "update" instead of "cvs update". - And now your disk caches are furiously being flushed by - multiple update daemons, destroying performance and proving to - management that you need more CPU power. :-) + On most Unix systems, your disk caches are now furiously being + flushed by multiple update daemons, destroying performance and + proving to management that you need more CPU power. :-) - 6. Someone backed out the revision CVS thought you had in your - working directory, then committed a "replacement". CVS is now - confused because the revision in the Repository matches the - revision CVS thinks you already have. See 3B.6. + On HP systems you might be asked what package you want to + install from the "update server". + 6. Someone removed (using "admin -o") your BASE revision (the + revision CVS thought you had in your working directory), then + committed a "replacement". CVS is now confused because the + revision in the Repository matches your BASE revision when the + files themselves don't match. See 3B.6. -#3P.5 Why does "update" say 'M' both for plain modified files and for - successful (i.e. conflict-free) merges? Aren't they different? + + 3P.5 Why does "update" say 'M' both for plain modified files and for + successful (i.e. conflict-free) merges? Aren't they different? A design choice. Yes, they are different internally, but that shouldn't matter. Your files are in the same condition after the "update" as they were before -- a "diff" will display only your - modifications. And you are expected to continue onward with the - normal cycle of "emacs" (a synonym for "edit" in most of the - civilized world) and "commit". + modifications. And you are expected to continue onward with parts + two and three of the normal development cycle: "emacs" (a synonym + for "edit" in most of the civilized world) and "commit". -=3P.6 After a merge ("update" or "update -j"), why doesn't CVS remember - the conflict and not allow you to commit the result until the - conflict is resolved? + 3P.6 What's a "sticky conflict"? How does it know a conflict occurred? - Maybe in a future release. There is a "sticky_conflict" patch you - might want to look at in the CVS FTP archive. + When a "cvs update" (or an "update -j") creates a conflict, it + prints a 'C' and stores the timestamp of the file after the merge + in a special field in the ./CVS/Entries file. + This conflict indication implies that the merge command altered + your working file to contain conflict markers surrounding the + overlapping code segments. For example, say that - 3P.7 Is there a feature to tell me what I have changed, added and - removed without changing anything? + - Two developers acquire revision 1.2 of <file> via "checkout" or + "update". - The command "cvs -n update" will do exactly that. + - Developer A changes line 1 from "9999" to "5555", then commits + the file, creating revision 1.3. + + - Developer B changes line 1 from "9999" to "7777", then tries to + commit the file, but is blocked because the file is not up to + date. Developer B then runs "update" and sees the conflict + marker 'C'. The beginning of the file would look like this: + + <<<<<<< <file> The working <file> in question. + 7777 Change made to the working <file>. + ======= + 5555 Change made in the first commit (1.3) + >>>>>>> 1.3 The revision created by the first commit. + + The conflict is "sticky", which means that until the conflict is + cleared, the "update" command will continue to display the file's + status as 'C' and the "status" command will show the file's status + as "Unresolved Conflict". + + Until the conflict is cleared, "commit" is blocked for this file. + + The sticky conflict indicator can be cleared by: + + 1. Resolving the conflict by editing the file. Two things must + happen before the conflict is considered resolved: + + The timestamp of the file must change. + *and* + The file must contain no conflict markers. (The string + searched for in the file is the regexp: "^>>>>>>> ".) + + After clearing the sticky conflict indicator, you may then + commit the file normally. + 2. Removing the file and running "update". This throws away the + local changes and accepts the latest committed file on this + branch. No commit is needed. -=3P.8 Why does "cvs update" not flag directories that are not in the - Repository as it does with new files? + 3. Forcing the commit to happen by using "commit -f". This is + probably a mistake since there are few lines of real + text that begin with ">>>>>>> ". - Design choice or oversight, take your pick. Directories are - handled specially. You can aim "update" at any random directory - and "update" will traverse the whole directory tree beneath it, - looking for CVS administrative directories. When it finds one, - "update" will perform its "normal" function. - Maybe a future release will notice a directory not under CVS, - print a '?' and skip over it. + 3P.7 Is there a feature to tell me what I have changed, added and + removed without changing anything? + + The command "cvs -n update" will do exactly that. - 3P.9 Why are all my files deleted when I execute "update"? + 3P.8 Why were all my files deleted when I executed "update"? You probably executed "update -r <tag>" some time ago, then removed <tag> from the Repository files. "update -r <tag>" will @@ -4551,14 +5564,14 @@ toward the beginning of this document. cvs update -r <the_tag_you_want> - Another way to make files disappear is to execute "update -D + Another way to make a file disappear is to execute "update -D <date>" where <date> is before the date stamped onto the first revision in the RCS file. =============================================== -== Section 4 ==== Advanced Topics ==== +== Section 4 ==== Advanced Topics ==== =============================================== ---------------- @@ -4567,16 +5580,16 @@ toward the beginning of this document. **** Questions: -#4A.1 What do I have to do before I install CVS? + 4A.1 What do I have to do before I install CVS? 4A.2 How do I configure the CVS programs? -=4A.3 What do I have to install? --4A.4 How do I work around the merge problems in GNU diff version 2.1 + 4A.3 What do I have to install? + 4A.4 How do I work around the merge problems in GNU diff version 2.1 or later? **** Answers: -#4A.1 What do I have to do before I install CVS? + 4A.1 What do I have to do before I install CVS? 1. You must decide where to set up a Repository. @@ -4585,7 +5598,7 @@ toward the beginning of this document. real file across your entire organization. You may not "rdist" files and expect to edit both copies. - CVS does not support a truly distributed Repository. You can + CVS does not support a truly distributed Repository. You can have multiple Repositories, but each one must be mounted (not copied or "rdist"ed) from a single place onto all machines where it will be used. @@ -4594,19 +5607,22 @@ toward the beginning of this document. as the sources you want to put into it, plus a bit of overhead for the RCS files. - See Section 4B. For multiple Repositories, see 4D.14. + See Section 4B. For multiple Repositories, see 4G.3 2. You need a directory in everyone's $PATH variable where you can - install all the executables. /usr/local/bin is a common place. + install all the executables. /usr/local/bin is a common place. 3. You need some helper tools besides CVS such as "RCS" and a - good set of "diff" and "diff3" programs. See 1B.3 for + good set of "diff" and "diff3" programs. See 1B.4 for suggestions. - 4. Make sure you have versions of all the programs mentioned in - the "cvs/src/config.h" file. + 4. Read the README, INSTALL and ChangeLog files to see what you + are getting into. - 5. Though you can probably muddle along without it, you should + 5. Make sure you have versions of all the programs mentioned in + the "cvs/src/options.h" and "cvs/src/rcs.h" files. + + 6. Though you can probably muddle along without it, you should appoint one or more "Repository Administrators" who will be responsible for maintaining the Repository structure, administrative files and the "modules" interface. @@ -4618,40 +5634,54 @@ toward the beginning of this document. 4A.2 How do I configure the CVS programs? 1. You should certainly start by reading the README file, the - INSTALL files and possibly the Makefiles and "cvsinit" program. - - 2. Edit the "config.h" file in the "src" directory. + INSTALL files and possibly the ChangeLogs in each directory, + the Makefile.in files and the "cvsinit.sh" program. - Read it carefully. + 2. Edit the "options.h" file in the "src" directory. - You will need to specify a number of site-specific pieces of + You might need to specify a few site-specific pieces of information including the names of a number of functions. - Hint1: You really want to set the DIFF macro to use your + Hint1: You probably want to set the DIFF macro to use your version of the GNU diff program with the '-a' option. Ours is set to "gdiff -a". - Hint2: You want to use RCS 5.5 or greater and set the - "HAVE_RCS5" macro. + Hint2: You want to use RCS 5.6.0.1 or greater and set the + "HAVE_RCS5" macro. 3. Execute the ./configure command. 4. Type "make". + 5. After running "make" you might try running the "sanity.sh" + script: + ./src/sanity.sh `pwd`/src/cvs + + It writes into /tmp/cvs-sanity by default. + + 6. Finish reading the INSTALL file and test out the system. -=4A.3 What do I have to install? + + 4A.3 What do I have to install? 1. Install the "cvs" executable and "mkmodules" from the CVS - sources. (The man page is useful too.) + sources. The man page is useful too. If you plan to report + bugs, you should also install "cvsbug". 2. Make sure you have versions of all the programs mentioned in - the config.h file, most of which are included in a standard + the options.h file, most of which are included in a standard Unix system. 3. Unless you plan to reimplement RCS [:-)], you must install RCS. - 4. Create the Repository (which you planned out in 4A.1) with the - "cvsinit" command at the top of the CVS sources. + It is a very good idea to examine the RCS installation + instructions and make sure you are using the GNU versions of + "diff" and "diff3" or merges (an important part of CVS) will + not work as well as you'd like. + + 4. Set your $CVSROOT environment variable and create the + Repository (which you planned out in 4A.1) with the "cvsinit" + command at the top of the CVS sources. 5. You'll need to edit the Repository control files created by "cvsinit". @@ -4659,10 +5689,11 @@ toward the beginning of this document. 6. Install any helper programs mentioned in the modules file. --4A.4 How do I work around the merge problems in GNU diff version 2.1 + 4A.4 How do I work around the merge problems in GNU diff version 2.1 or later? - See 4D.11. + See 1B.4 If you use recent versions of RCS and "diff", you won't + run into the above. If you do, see 5B.8 ---------------- @@ -4671,34 +5702,33 @@ toward the beginning of this document. **** Questions: -=4B.1 What do I do first? How do I create a Repository? -=4B.2 What are those files in $CVSROOT/CVSROOT? + 4B.1 What do I do first? How do I create a Repository? + 4B.2 What are those files in $CVSROOT/CVSROOT? 4B.3 Is there any other state stored in the Repository besides in the $CVSROOT/CVSROOT directory? 4B.4 How do I put sources into the Repository? -=4B.5 What file permissions should I use on (and in) the Repository? -=4B.6 How do I structure my Repository? -=4B.7 How do I manage the modules file? - 4B.8 Why would anyone use "modules"? They are too restrictive. I + 4B.5 What file permissions should I use on (and in) the Repository? + 4B.6 How do I structure my Repository? + 4B.7 Why would anyone use "modules"? They are too restrictive. I want to be able to select just the files I want to edit. -=4B.9 How do I rename a file or directory? What are the consequences? -=4B.10 What are "Attic" directories? - 4B.11 Is it OK to remove anything from the Repository? - 4B.12 Can I convert to CVS from RCS without losing my revision history? -=4B.13 Can I move RCS files with branches in them into the Repository? -=4B.14 Can I use raw RCS commands on the Repository? - 4B.15 How do I convert from SCCS to RCS? -=4B.16 How do I limit access to the Repository? -=4B.17 What are the Repository Administrator's responsibilities? - 4B.18 How do I move the whole Repository? -+4B.19 How do I change permissions on a file in the Repository by using - a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") + 4B.8 How do I rename a file or directory? What are the consequences? + 4B.9 What are "Attic" directories? + 4B.10 Is it OK to remove anything from the Repository? + 4B.11 Can I convert to CVS from RCS without losing my revision history? + 4B.12 Can I move RCS files with branches in them into the Repository? + 4B.13 Can I use raw RCS commands on the Repository? + 4B.14 How do I convert from SCCS to RCS? + 4B.15 How do I limit access to the Repository? + 4B.16 What are the Repository Administrator's responsibilities? + 4B.17 How do I move the whole Repository? + 4B.18 How do I change permissions on a file in the Repository by using + a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") **** Answers: -=4B.1 What do I do first? How do I create a Repository? + 4B.1 What do I do first? How do I create a Repository? First, install all the programs. (See Section 4A.) @@ -4712,11 +5742,11 @@ toward the beginning of this document. with "cvsinit" entirely. I would advise executing it. The cvsinit program will create a short modules file containing - the module named "CVSROOT". Execute: + the module named "CVSROOT". To to your work directory and type: cvs checkout CVSROOT - and read the information stored in the files that are checked out. + Then read the files that are checked out. You will certainly want to add modules of your own. Edit the "modules" file and add lines to describe the items you want to @@ -4730,8 +5760,8 @@ toward the beginning of this document. cvs local/gnu/cvs public local/public - pdprog1 local/public/pdprog1 - pdprog2 local/public/pdprog2 + pdprog1 local/public/pdprog1 + pdprog2 local/public/pdprog2 test test junk test/junk @@ -4739,20 +5769,23 @@ toward the beginning of this document. When you are done editing, "commit" the modules file. If you configured CVS to use "dbm", you might have to edit and commit the - modules file twice, in order to change the pathname of the - mkmodules program in the modules file. + modules file twice to change the pathname of the mkmodules program + in the modules file. Try using the "import" command to insert the "junk" module and play around until you are comfortable. -=4B.2 What are those files in $CVSROOT/CVSROOT? + 4B.2 What are those files in $CVSROOT/CVSROOT? - There are seven Repository control (or "database") files of + There are eight Repository control (or "database") files of interest in the CVSROOT directory: - 1. commitinfo contains two columns: 1. a regular expression to + 1. modules contains the "modules" database. See 1D.11, 2C.7, + 4B.6 and 4B.7 for more details. + + 2. commitinfo contains two columns: 1. a regular expression to match against pathnames within the Repository and 2. a <command> to execute for matching pathnames. @@ -4767,7 +5800,7 @@ toward the beginning of this document. Every <command> associated with a pathname of "ALL" is executed separately. - 2. rcsinfo contains the same first column as commitinfo, but + 3. rcsinfo contains the same first column as commitinfo, but the second column is a template file for specifying the log entry you are required to enter for each commit. @@ -4775,13 +5808,13 @@ toward the beginning of this document. "DEFAULT" and "ALL" work the same as in the commitinfo file. - 3. editinfo contains the same two columns as commitinfo, but + 4. editinfo contains the same two columns as commitinfo, but the <command> in the second column is intended to do some consistency checking on the commit log. "DEFAULT" works as in commitinfo. - 4. loginfo contains the same two columns as commitinfo, but + 5. loginfo contains the same two columns as commitinfo, but the <command> is expected to read a log message from its standard input. The <command> can do anything it wants with the log information, but @@ -4790,23 +5823,25 @@ toward the beginning of this document. "DEFAULT" & "ALL" work the same as in commitinfo. - 5. cvsignore contains "ignore" patterns that are added to the + 6. cvsignore contains "ignore" patterns that are added to the built-in ignore list. See 2D.10. - 6. history contains a stream of text records, one for each + 7. checkoutlist contains a list of other files kept under RCS in + $CVSROOT/CVSROOT that should be checked out by + mkmodules to provide a readable copy. + + 8. history contains a stream of text records, one for each event that the "history" command is interested in. Though the contents of the history file can be read, it is intended to be read and displayed - by the "history" command. - - 7. modules contains the "modules" database. See 1D.11, 2C.7, - 4B.7 and 4B.8 for more details. + by the "history" command. This file is the only + one in the above list that is not under RCS. 4B.3 Is there any other state stored in the Repository besides in the $CVSROOT/CVSROOT directory? - Only in the RCS files. The Repository holds exactly two things: + Only in the RCS files. The Repository holds exactly two things: the tree of RCS files (each usually ending in ",v") and the CVSROOT directory described above. @@ -4824,15 +5859,17 @@ toward the beginning of this document. 2. Use "add" followed by "commit". This is how to add new files and directories to the Repository, - one at a time. + a few at a time. Directories don't need to be committed. 3. You can move RCS files directly into the Repository. - It would probably be a good idea to create directories to hold - them. + You should create a directory hierarchy to hold them, but you + can just move arbitrary ",v" files into the Repository. The + only "state" in the Repository other than within ",v" files is + in the required CVSROOT directory at the top of the Repository. -=4B.5 What file permissions should I use on (and in) the Repository? + 4B.5 What file permissions should I use on (and in) the Repository? If you run a completely open environment (which usually means that you don't have, or don't want to waste, the time to deal with it): @@ -4841,21 +5878,26 @@ toward the beginning of this document. - Have everyone set their umasks to 0. - (BTW, I don't suggest this. It am merely reporting it.) + (BTW, I don't suggest this. I am merely reporting it.) If you are a normal Unix shop and want to use groups effectively: - Set all the directory permissions in the Repository to 775. - (If you are using a system that handles both System V and BSD - filesystems, you might have to set the permissions to 2775.) + + If you are using a system that handles both System V and BSD + filesystems, you might have to set the permissions to 2775.) + + If you are using one of the many recent versions of Unix that + don't allow you to use the full octal mode, then you'll have + to type: chmod u=rwx,g=rwx,o=rx,g+s <dir> - Change all the groups on the directories to match the groups you want to write to various directories. - Make sure every user is in the appropriate groups. - - Have everyone set their umask to 002. + - Have everyone set their umask to 002, including root. If you don't want non-group members to even read the files, do the @@ -4867,8 +5909,8 @@ toward the beginning of this document. If you work in an environment where people can't be trusted to - set their "umask" to something reasonable, you might want to set - the umask for them: + set their "umask" to something reasonable, you might want to set + the umask for them: mv /usr/local/bin/cvs /usr/local/bin/cvs.real cat > /usr/local/bin/cvs @@ -4877,11 +5919,8 @@ toward the beginning of this document. exec /usr/local/bin/cvs.real ${1+"$@"} ^D - [[Future versions of CVS might sprout a "umask" configuration - variable.]] - -=4B.6 How do I structure my Repository? + 4B.6 How do I structure my Repository? The Repository holds your software. It can be all interrelated or it can be a bunch of separately managed directories. @@ -4901,8 +5940,8 @@ toward the beginning of this document. install and distribute your work. Common needs include the ability to: - - mount (or automount) directories from different - places in your organization. + - mount (or automount) directories from many places in your + organization. - check out just what you need and no more. - check out multiple sections in a fixed relation to each other. - check out large sections to match the assumptions built into @@ -4914,29 +5953,7 @@ toward the beginning of this document. sophisticated. -=4B.7 How do I manage the modules file? - - An equivalent question might be, "How do I structure my sources?" - This can be a difficult question in that it is not purely - technical. - - Generally you want to think about what pieces of your system need - to be checked out together, built as one system or tagged - consistently. You should certainly make module names that - correspond to complete, buildable collections that you would tag - and release as one "product". It is also convenient to create - module names for small sections containing files that will usually - be worked on at the same time by the same person. - - Once you have defined the structure of your work, you can usually - see how to lay it out in a Repository. After that the modules - file is easy. You set up module names and aliases to match what - you need to check out by name. If you like relative directories, - it is possible, but not recommended, to work completely without a - modules file. See 1D.11 and 2C.7. - - - 4B.8 Why would anyone use "modules"? They are too restrictive. I + 4B.7 Why would anyone use "modules"? They are too restrictive. I want to be able to select just the files I want to edit. Any form of structure is restrictive. If you believe that total @@ -4955,9 +5972,9 @@ toward the beginning of this document. and will smoothly interact with the rest of your environment. -=4B.9 How do I rename a file or directory? What are the consequences? + 4B.8 How do I rename a file or directory? What are the consequences? - In CVS 1.3 there is no single CVS "rename" command. + In CVS there is no single "rename" command. See 2C.4 for the suggested way to rename a file or directory. @@ -4969,7 +5986,7 @@ toward the beginning of this document. work across the renaming. But as it stands, you have to pick one of the following options: - 1. Use the technique described in 2C.4. (For each file, duplicate + 1. Use the technique described in 2C.4. (For each file, duplicate the file in the Repository, "remove" the old version so it winds up in the Attic and strip all Tags off the new version.) @@ -5004,7 +6021,7 @@ toward the beginning of this document. - Important Note: If you rename a directory, you must rename the corresponding directory in every checked-out working - directory. At the same time, you must edit the pathname + directory. At the same time, you must edit the pathname stored in the ./CVS/Repository file within each of the moved directories. @@ -5025,16 +6042,17 @@ toward the beginning of this document. no longer in the repository C xyz.c - and leaves the file alone. In the new directory, you see: + and leaves the file alone. In the new directory, you see: U xyz.c as you would if someone else executed "add" and "commit". - 3. For each file, copy the working file to a new name, "remove" - the old file and "add" the new one. Since there is no way (in - CVS 1.3) to remove a directory, this only works for files. + 3. For each file, copy the working file to a new name in the + working directory and use the "cvs remove" to get rid of the + old old file and "cvs add" to add the new one. Since there is + no way for CVS to remove a directory, this only works for files. - This is what most people think of first. Without a "rename" command, the remove/add technique seems obvious. @@ -5043,7 +6061,7 @@ toward the beginning of this document. revision history. -=4B.10 What are "Attic" directories? + 4B.9 What are "Attic" directories? When you use the "remove" command on a file, CVS doesn't delete the file, it only registers your desire to delete it. @@ -5061,7 +6079,7 @@ toward the beginning of this document. looked at when you refer to a <tag> or <date>. - 4B.11 Is it OK to remove anything from the Repository? + 4B.10 Is it OK to remove anything from the Repository? In general, removing anything from the Repository is a bad idea. The information in a deleted object is lost forever. There are @@ -5082,7 +6100,8 @@ toward the beginning of this document. command (or the equivalent RCS command "rcs -o"). They are lost forever. Any tags formerly attached to deleted - revisions are now pointing into Outer Space. + revisions are now pointing into the Phantom Zone. You'll need + to contact Jor-el to get them back. 3. Files @@ -5124,18 +6143,18 @@ toward the beginning of this document. unusual errors. - 4B.12 Can I convert to CVS from RCS without losing my revision history? + 4B.11 Can I convert to CVS from RCS without losing my revision history? Yes, you can simply move (or copy) your RCS files into a directory within the Repository, check out that directory and start working. -=4B.13 Can I move RCS files with branches in them into the Repository? + 4B.12 Can I move RCS files with branches in them into the Repository? Yes, but they may not work if you created branches in a way that conflicts with CVS's assumptions: - 1. You can't use .0. branches. (They are reserved for "Magic" + 1. You can't use .0. branches. (They are reserved for "Magic" branch tags.) 2. If you use branch 1.1.1, you can't use the Vendor branch. @@ -5145,7 +6164,7 @@ toward the beginning of this document. exists. -=4B.14 Can I use raw RCS commands on the Repository? + 4B.13 Can I use raw RCS commands on the Repository? You can use raw rcs commands directly on the Repository if you take a little care. The Repository itself contains no "CVS state" @@ -5159,50 +6178,83 @@ toward the beginning of this document. the "admin" command. - 4B.15 How do I convert from SCCS to RCS? + 4B.14 How do I convert from SCCS to RCS? You'll have to execute something like "sccs2rcs" (in the CVS contrib directory) on every file. Then you can move the resulting RCS files into the Repository as described above. -=4B.16 How do I limit access to the Repository? + 4B.15 How do I limit access to the Repository? There are all sorts of ways to restrict access to Repository - files, none of which are hooked directly into CVS. You can: + files, none of which are hooked directly into CVS. + + Techniques for limiting access include: + + 1. Training, management and good backups. + + The best form of Repository control is a combination of: + + - A reliable backup scheme (verify it!) + - Enough training to ensure your developers are competent + and knowledgeable about all areas of your sources. + - Effective management of the boundaries and grey areas. + + In many cases, technical solutions to "security" problems are + inadequate. You should first try to avoid them. - 1. Set Unix groups and permissions. See 4B.5. + Personal Opinion: In an environment where "unknowns" are + allowed to touch important sources the "owner" of the CVS + Repository must be a large, loud, vigorous lout with a + well-balanced truncheon and the right to use it. Don't + underestimate the effectiveness of letting everyone know they + will be strapped into the stocks on the Town Common and pelted + with vegetables if they break something they don't understand + without first asking the experts. - 2. Try the "setgid" trick described in 4D.16. + 2. Set Unix groups and permissions. See 4B.5. + You can set different owners, groups and permissions for each + sub-directory within the Repository if that helps. - 3. Catch every commit using the "commitinfo" file. + 3. Catch invocations of "commit" by defining pre-commit programs + in the "commitinfo" file. This is fairly powerful, since it + can block commits based on anything you can program. Take a + look at the programs in the "contrib" directory of the CVS + source tree. - 4. Try to use the RCS access control lists, though I don't + 4. Use multiple Repositories, each with its own protection scheme. + If you use NFS (or AFS) you can even use "export" restrictions + to various groups of machines to keep (for example) the + Engineering Repository off the Customer Service machines. + + 5. Try the "setgid" trick described in 4D.13. + + 6. Try to use the RCS access control lists, though I don't think CVS will handle them cleanly. - 5. Edit the source code to CVS. + 7. Edit the source code to CVS to add your own access control. -=4B.17 What are the Repository Administrator's responsibilities? + 4B.16 What are the Repository Administrator's responsibilities? Generally, the Administrator should set "policy", create the Repository and monitor its size and control files. Some specific responsibilities include: - 1. Examining the Repository once in a while to clean up: a. Trash files left by misguided developers who mistake the Repository for a working directory. b. Non-RCS files. Other than the files CVS needs in the - $CVSROOT/CVSROOT directory, everything in the Repository - should be "under" CVS. + $CVSROOT/CVSROOT directory, every file in the Repository + should be an RCS file. c. Lock files (both CVS '#*' and RCS ',*' files) left around after crashes. - + d. Wrong permissions, groups and ownerships. e. Locked files. (RCS locks, that is.) @@ -5216,68 +6268,82 @@ toward the beginning of this document. 2. Maintaining the modules file. - 3. Maintaining the other Repository control files: commitinfo, - loginfo, rcsinfo and editinfo files. + 3. Storing site-specific ignore patterns in the + $CVSROOT/CVSROOT/cvsignore file. - 4. Storing site-specific ignore patterns in the "cvsignore" file. + 4. Storing the names of non-standard CVSROOT files (See 4B.2) in + the $CVSROOT/CVSROOT/checkoutlist - 5. Pruning the history file every once in a while. (Try the + 5. Maintaining the other Repository control files: commitinfo, + loginfo, rcsinfo and editinfo. + + 6. Pruning the history file every once in a while. (Try the "cln_hist.pl" script in the "contrib" directory.) - 6. Staying aware of developments on the info-cvs mailing list and - what is available in the FTP archive. + 7. Staying aware of developments on the info-cvs mailing list and + what is available in the FTP and WWW archives. - 7. Run "ps ax" once in a while and kill off any "update" + 8. Running "ps ax" once in a while and kill off any "update" programs not running as "root". It is too easy to leave the - "cvs" off the front of the "cvs update" command. + "cvs" off the front of the "cvs update" command. - 8. Executing monitor programs to check the internal consistency of + 9. Executing monitor programs to check the internal consistency of the Repository files. Ideas: a. Files that have a default RCS branch that is not 1.1.1 (From an abuse of "admin -b".) b. Files that have only Revisions 1.1 and 1.1.1.1, with a - default branch of "MAIN". (From an abuse of "admin -o".) + default branch of "MAIN". (From an abuse of "admin -o".) c. Existing branch tags and various branch consistency checks. + 4B.17 How do I move the whole Repository? + + Copy or move the tree. (On Unix systems, a set of piped "tar" + commands works great. If the Repository does not contain any + symlinks, which it normally doesn't, you can also use "cp -r".) - 4B.18 How do I move the whole Repository? + If you can avoid changing $CVSROOT (i.e. the "logical" pathname of + the Repository) by replacing the old location with a symbolic link + to the new location, you don't have to do anything else. - The Repository itself contains pathnames only within the files in - the CVSROOT directory. If helper functions in the modules file or - logging programs executed out of the loginfo file point into the - Repository, you'll have to change the pathnames to point to the - new Repository location. + (You could also mount the new location on top of the old location + if you are using NFS or some other filesystem that allows it.) - The main change you'll have to make is to all the ./CVS/Repository - files in the checked-out working directories. - You have four choices: + If you must change $CVSROOT, you must also tell everyone to change + the CVSROOT environment variable in all running shells and in any + personal configuration files ('.' files on Unix) where it is set. - 1. If you can avoid changing $CVSROOT by using a symbolic link or - mount point you don't have to do anything else. + The Repository itself contains no references to its own name, + except possibly in some of the files in the CVSROOT directory. If + your modules (or loginfo, commitinfo, etc.) file mentions helper + programs directly in the Repository, you'll have to change the + pathnames to point to the new Repository location. - If you must change $CVSROOT, you must also tell everyone to - change the CVSROOT environment variable in all running shells - and in their '.' files where it is set. Then pick one of the - other three options that follow: + The main changes you'll have to make are to all the CVS + administrative files (./CVS/Repository and ./CVS/Root) in every + working directory ever checked out from the previous location of + the Repository you just moved. - 2. If all ./CVS/Repository files in all working directories + You have three choices: + + 1. If all ./CVS/Repository files in all working directories contain relative pathnames, you don't have to do anything else. - 3. Have everyone "release" or delete their working directories + 2. Have everyone "release" or delete their working directories (after committing, or just saving, their work) and check them all out again from the new Repository after the move. - 4. Use a PERL or shell script to run through all the - ./CVS/Repository files and edit the values in the files. + 3. Use "find . ( -name Repository -o -name Root )" and a + PERL or shell script to run through all the ./CVS/Repository + and ./CVS/Root files and edit the values in the files. -+4B.19 How do I change permissions on a file in the Repository by using - a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") + 4B.18 How do I change permissions on a file in the Repository by using + a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") When you first "import" or "add"/"commit" a file, the read and execute bits on the Repository file are inherited from the @@ -5285,8 +6351,9 @@ toward the beginning of this document. are are turned off. This is a standard RCS action. After that, there is no way to alter the permissions on a file in - the Repository other than by changing the Repository file - directly. + the Repository using CVS (or RCS) commands. You have to change + the permissions on both your working file and on the Repository + file from which it was retrieved. Whenever you "checkout" the file or retrieve a new revision via "update" (or after a "commit"), your working file is set to match @@ -5296,31 +6363,41 @@ toward the beginning of this document. ---------------- --- Section 4C -- Branching +-- Section 4C -- Branching and Merging ---------------- **** Questions: 4C.1 What is a branch? -=4C.2 Why (or when) would I want to create a branch? -=4C.3 How do I create and checkout a branch? + 4C.2 Why (or when) would I want to create a branch? + 4C.3 How do I create and checkout a branch? 4C.4 Once created, how do I manage a branch? 4C.5 Are there any extra issues in managing multiple branches? 4C.6 How do I merge a whole branch back into the trunk? - 4C.7 How do I merge changes from the trunk into my branch or between +=4C.7 How do I merge changes from the trunk into my branch or between branches? - 4C.8 How do I add a new file to a branch? -=4C.9 How do I know what branch I'm (working) on? + 4C.8 How do I merge onto the Main Branch a file that exists only on a + branch other than the Main Branch? (i.e. it is in the Attic) + 4C.9 How do I know what branch I'm (working) on? 4C.10 Do I really have to know the name of the branch I'm working on? 4C.11 How do I refer to the revision where I branched so I can see what changed since the Branch Point on another branch? 4C.12 Why didn't the command "cvs admin -bBRANCH1 *" create a branch? 4C.13 Is it possible to set the "default CVS branch" for everyone? -=4C.14 How do I perform a large merge? + 4C.14 How do I perform a large merge? 4C.15 Is a Vendor merge any different from a branch merge? -+4C.16 How do I go back to a previous version of the code on a branch? -+4C.17 Why do I get the latest files on the branch when I tried to + 4C.16 How do I go back to a previous version of the code on a branch? + 4C.17 Once I've found the files I want, how do I start changing them? + I keep getting warnings about sticky tags. + 4C.18 Why do I get the latest files on the branch when I tried to "update -r <tag>"? + 4C.19 How can I avoid a merge? I just want to move the latest revision + on my working branch directly onto the trunk. + 4C.20 How to I avoid merge collisions in the RCS $\Log$ data? + 4C.21 Why should I trust automatic merges? + 4C.22 How does CVS decide if it can safely perform a merge? + 4C.23 After resolving merge conflicts in a file, what if I want to keep + my previous version, and not take any of the branch changes? **** Answers: @@ -5350,7 +6427,7 @@ toward the beginning of this document. Essentially, the word "branch" implies a way to allow simultaneous development on the same files by multiple people. - The above terms are human-oriented. They refer to actions + The above terms are human-oriented. They refer to actions that people would like to take. They do *not* imply any particular implementation or set of procedures. Branches in development can be supported in many different ways. @@ -5371,7 +6448,7 @@ toward the beginning of this document. when you use the '-A' option to "update". Important Note: The CVS "Main Branch" is *not* the same as - the RCS concept with the same name. If you are using Vendor + the RCS concept with the same name. If you are using Vendor Branches, files you have never changed are on three branches at the same time: @@ -5384,14 +6461,14 @@ toward the beginning of this document. In referring to CVS, "branch" can be used in four other ways: - A CVS working directory satisfies the definition of - "branch" for a single developer -- you are on a "virtual - branch" that does not appear in any of the RCS files or - the CVS control files. + "branch" for a single developer -- you are on a private + "virtual branch" that does not appear in any of the RCS + files or the CVS control files. - The CVS "default branch" is the Repository source for the - collection of files in your working directory. It is + collection of files in your working directory. It is *not* the same as the RCS "default branch". Normally the - CVS default branch is the same as the CVS Main branch. If + CVS default branch is the same as the CVS Main branch. If you use the "-r <branch_tag>" option to the "checkout" command, you will record a "sticky" tag that changes your default branch to the one you checked out. @@ -5421,19 +6498,20 @@ toward the beginning of this document. the file since you executed "import". -=4C.2 Why (or when) would I want to create a branch? + 4C.2 Why (or when) would I want to create a branch? Remember that you can think of your working directory as a "branch for one". You can consider yourself to be on a branch all the time because you can work without interfering with others until your project (big or small) is done. - The four major situations when should create a branch are when: + The four major situations when you should create a branch: - 1. You expect to take a long enough time or make a large enough - set of changes that the merging process will be difficult. + 1. When you expect to take a long time or make a large set of + changes that the merging process will be difficult. Both + "long" and "large" are defined in your own environment. - 2. You want to be able to "commit" and "tag" your work + 2. When you want to be able to "commit" and "tag" your work repeatedly without affecting others. If you ever think you need Source Control for your own work, @@ -5441,8 +6519,8 @@ toward the beginning of this document. branch. (Put your username in the branch tag, to make it obvious that it is private.) - 3. You need to share code among a group of developers, but not the - whole development organization working on the files. + 3. When you need to share code among a group of developers, but + not the whole development organization working on the files. Rather than trying to share a working directory, you can move onto a branch and share your work with others by "committing" @@ -5450,19 +6528,19 @@ toward the beginning of this document. branch won't see your work unless they switch to your branch or explicitly merge your branch into theirs. - 4. You need to make minor changes to a released system. + 4. When you need to make minor changes to a released system. Normally a "release" is labeled by a branch tag, allowing later - work on the released files. If the release is labeled by a + work on the released files. If the release is labeled by a non-branch tag, it is easy to add a branch tag to a previously tagged module with the "rtag" command. If the release is not - tagged, you made a mistake. Recovery requires identifying all + tagged, you made a mistake. Recovery requires identifying all revisions involved in the release and adding a tag to them. -=4C.3 How do I create and checkout a branch? + 4C.3 How do I create and checkout a branch? - Suggested short form: + Suggested technique: 1. Attach a non-branch tag to all the revisions you want to branch from. (i.e. the branch point revisions) @@ -5470,99 +6548,103 @@ toward the beginning of this document. 2. When you decide you really need a branch, attach a branch tag to the same revisions marked by the non-branch tag. - 3. "Checkout" or move your working directory to the branch. - + 3. "Checkout" or "update" your working directory onto the branch. - Suggested short form using modules: - A1. cvs rtag <branch_point_tag> module - A2. cvs rtag -b -r <branch_point_tag> <branch_tag> module - A3. cvs checkout -r <branch_tag> module + A. Suggested procedure when using modules: + 1. cvs rtag <branch_point_tag> module + 2. cvs rtag -b -r <branch_point_tag> <branch_tag> <module> + 3. cvs checkout -r <branch_tag> module - Suggested short form using your working directory, which contains - the revisions of your working files you want to branch from: - B1. cvs tag <branch_point_tag> - B2. cvs rtag -b -r <branch_point_tag> <branch_tag> module - B3. cvs update -r <branch_tag> + B. Suggested procedure when using your working directory, which + contains the revisions of your working files you want to branch + from: + 1. cvs tag <branch_point_tag> + 2. cvs rtag -b -r <branch_point_tag> <branch_tag> <module> + 3. cvs update -r <branch_tag> - The <branch_tag> is an unusual creature. It labels a branch in a - way that allows you to "checkout" the branch, to "commit" files to - the end of the branch and to refer to the end of the branch. It - does not label the base of the branch (the branch point). - Step #1 applies a non-branch tag to all the branch point revisions - in the module/directory. Though this is not strictly necessary, - if you don't add a non-branch tag to the revisions you branch - from, you won't be able to refer to the branch point in the - future. + In each procedure above, Step #1 applies a non-branch tag to all + the branch point revisions in the module/directory. Though this + is not strictly necessary, if you don't add a non-branch tag to + the revisions you branch from, you won't be able to refer to the + branch point in the future. - Between steps 1 & 2 you may commit files and the result is the + Between steps 1 & 2 you may commit changes. The result would be same because "rtag -r <oldtag> <newtag>" applies <newtag> to the same revision that <oldtag> is attached to. You can use this technique to avoid attaching *any* branch tags until you need them. - Step B2 has two important corollaries: + Step B.2 has two corollaries: - 1. If you plan to create the branch tag before committing - anything in your working directory, you can use "cvs tag - -b <branch_tag>" instead of the "rtag" command. + 1. If you plan to create the branch tag before committing + anything in your working directory, you can use "cvs tag -b + <branch_tag>" instead of the "rtag" command. - 2. If you have trouble figuring out what "module" to use, - remember that "module" can also mean "relative path within - the Repository." If that doesn't help, you can aim it at - whatever parent directories you believe will cover all - your files. You can even aim it at the whole Repository - ($CVSROOT), if you have to. It might take some extra - time, but assuming that your Tag is a unique string and - you don't use the '-f' option to "rtag -r", "rtag" will - only add a Tag to files in which it actually *finds* the - earlier Tag. + 2. The <module> can be a relative path to a directory + from which your working directory was checked out. + If you have trouble figuring out what <module> to use (or + pathname to use in its place), you can aim it at whatever + parent directories you believe will cover all your work. - Step 3 may occur any time after step 2. Unless you explicitly - remove them with "tag -d", the Tags are permanent. + If you are sure the <branch_tag> is not being used anywhere + else, you can even aim it at the whole Repository ($CVSROOT), + if you have to. It might take some extra time, but assuming + that your <tag> is a unique string and you don't use the '-f' + option to "rtag -r", "rtag" will only add a <tag> to files in + which it actually *finds* the earlier <tag>. + In each procedure above, Step #3 may occur any time after step 2. + Unless you explicitly remove them with "tag -d", a <tag> is + permanent. - There are two obvious ways of to choose the <branch_point_tag> and - <branch_tag> names. Since the <branch_tag> is typed by any - developer who wants to work on the branch, you should make it mean - something to them. + + The <branch_tag> is an unusual creature. It labels a branch in a + way that allows you to "checkout" the branch, to "commit" files to + the end of the branch and to refer to the end of the branch. It + does not label the base of the branch (the branch point). + + There are two obvious ways to choose the <branch_point_tag> and + <branch_tag> names. But keep in mind that the <branch_tag> is + typed by any developer who wants to work on the branch -- you + should make it mean something to them. Style #1 presumes that the simple version string refers to a set of designed, documented or promised features, not to a specific set of files. In this case, you tag the branch with the generic Version string and assume that whenever you refer to "Version", you want the "latest" set of files associated with that Version, - including all patches. (You can substitute what ever you like for + including all patches. (You can substitute whatever you like for "bp_", as long as your <branch_point_tag> is some modification of the <branch_tag>.) - <branch_point_tag> Matching <branch_tag> + <branch_point_tag> Matching <branch_tag> bp_V1_3 V1_3 bp_Release2-3-5 Release2-3-5 - bp_Production4_5 Release4_5 + bp_Production4_5 Release4_5 Style #2 presumes that the simple version string refers to the specific set of files used to construct the first release of "version". In this case, you tag the branch-point revisions with the generic Version string and assume that whenever you refer to - this Version, you want the original set of released revisions. To + this Version, you want the original set of released revisions. To get the latest patched revisions of the release, you refer to the branch tag "latest_<branch_point_tag>". (You can substitute what ever you like for "latest_", as long as your <branch_tag> is some modification of the <branch_point_tag>.) - <branch_point_tag> Matching <branch_tag> + <branch_point_tag> Matching <branch_tag> V1_3 latest_V1_3 Release2-3-5 latest_Release2-3-5 - Release4_5 latest_Production4_5 + Release4_5 latest_Production4_5 In both styles you can find out what you had to change since the @@ -5579,9 +6661,9 @@ toward the beginning of this document. cvs diff -r <branch_point_tag> -r latest_<branch_point_tag> - Notes: + Notes on "being on a branch": - - The "-r <tag>" option tells CVS to attach a "sticky tag" to + - "update -r <tag>" tells CVS to attach a "sticky tag" to working directory (in ./CVS/Tag) and the checked-out files (on each line of ./CVS/Entries). @@ -5629,7 +6711,7 @@ toward the beginning of this document. If you plan to split from the "main line" and merge back after a time, the only problem will be scheduling the order of branch - merges. As each branch is merged, the main line must be rebuilt + merges. As each branch is merged, the main line must be rebuilt and tested. Merging multiple branches (i.e. "lines of development") before building and testing creates more problems than you are ready for. @@ -5667,7 +6749,7 @@ toward the beginning of this document. 'U' for files that you hadn't changed, but the branch did. 'M' for files that you changed and the branch didn't - *and* for files that you both changed that were merged + *and* for files that you both changed that were merged without overlaps. (This overload is unfortunate.) 'C' for files that you both changed in a way that conflicts @@ -5677,82 +6759,179 @@ toward the beginning of this document. Then you must commit them. - 4C.7 How do I merge changes from the trunk into my branch or between +=4C.7 How do I merge changes from the trunk into my branch or between branches? The idea is similar to the above, but since CVS doesn't treat the - main branch like other branches, you'll have to be more precise. + main branch like other branches, you'll have to be more careful. + There are 5 different ways to look at the problem. - Check out or "update" the files you want to merge into. + A. The way to merge *all* changes made on the trunk into a working + branch is to move to the branch you want via "checkout -r" or + "update -r": - Identify the two tags on the branch you want to merge from. - (Revisions don't work too well because the same revision doesn't - mean the same thing in different files.) Then type: + cvs update -r <branch_tag> {optional files} - cvs update -j <tag1> -j <tag2> + Then merge the changes from the trunk into your working branch + using the pseudo-tag named "HEAD": - You can use a <branch_tag> to refer to the latest revision on the - branch, but there is no built-in way to refer to the branch point. + cvs up -j HEAD {optional files} + You will get everything from the branch point of the branch + named <branch_tag> up to the HEAD of the main branch. This is + still kind of strange. If the file is on a branch, HEAD should + be the latest thing on the branch, not the HEAD of MAIN. But + that's not the way CVS (currently) works. - An alternative to merging is to identify a single revision you - want, grab it by using "update -p" and commit it. If you do - merges later, you'll get overlaps, but you get your file. + If you run "cvs up -j HEAD" again after adding more revisions + to the trunk, you may get overlaps for the text you have + already merged. It depends on your version of your RCS "merge" + command (actually the "co -j" option, which depends on the + version of "diff3" you configured RCS to use). - In the future, (but not yet) merging from the main branch will - look something like this: + B. You can merge the difference between any two <tags> using + two "-j" options on "update" or "checkout". - cvs update -j MAIN - cvs commit -m "Log message" + Identify the two tags on the branch you want to merge from. + cvs update -j <tag1> -j <tag2> {optional files} - 4C.8 How do I add a new file to a branch? + This step assumes you were careful about tagging milestones. + You can use this technique for any two <tags> on the same + branch, even the trunk. It is also possible to use tags on + different branches, but you'll have to ponder the meaning of + the difference between those two tags. - The obvious technique is broken in CVS 1.3. This is the way it is - supposed to work: + In place of one of the <tags>, you can use a <branch_tag> to + refer to the latest revision on that branch. See 4C.11 and + 4C.3 for info on branch points. - You are in a directory checked out (or updated) with the "-r - <branch_tag>" option. To add <file> to the branch named - <branch_tag> you type: + Merges can also be performed by handing RCS revisions to the + '-j' options, but since revision numbers aren't the same in all + files, merging by number is normally limited to one file. Sets + of files with the exact same trees of branches and revision + numbers would work too, but that's a rare situation. - cvs add <file> - cvs commit <file> + C. To "take" revisions from other branches instead of merging + them, see 4C.19 for an idea. - Until the next release appears, you must explicitly specify the - branch_tag: - cvs add <file> - cvs commit -r <branch_tag> <file> + D. A way to gain the effect of merging the main to the branch is + to merge the branch into the main using the normal - One not so obvious side-effect is that the file ends up in the - Attic. It wasn't added to the Main Branch so it doesn't show up - in the main part of the Repository, only in the Attic. + cvs update -A {optional files} + cvs update -j <branch_tag> {optional files} + cvs commit + cvs tag -F -b <same_branch_tag> {optional files} - You can add it to the Main Branch and branch off from there onto - the side-branch this way: + See part B of 4D.5 - 1. Move the working file back to the main branch: + + E. Other oddities. - cvs update -A <file> + This also works, but is probably not officially supported: + + cvs update -j N {optional files} + + where N is a number. This will merge all the changes from the + branch point up to the highest revision on the main branch + starting with N. For example, if your highest trunk revision + is 1.52, you can use this to grab revisions from the trunk: + + cvs update -j 1 {optional files} + + Another example: Say you have a branch point at rev 1.2 for a + branch named "BR1" and trunk revisions 1.3, 1.4, 2.1, 2.2, 2.3, + 3.1, 3.2. Then: + + cvs update -j 1 {optional files} + + will merge the changes from 1.2 to 1.4 + + cvs update -j 2 {optional files} + + will merge the changes from 1.2 to 2.3 + + cvs update -j 3 {optional files} + + will merge the changes from 1.2 to 3.2, which in this example, is + equivalent to the use of "-j HEAD" in part A above. + + The intuitive (at least to me): + + cvs up -j MAIN (or TRUNK) {optional files} + + doesn't work. If the trunk (i.e. "main branch") had an + implicit branch named "MAIN", you could use: + + cvs up -j MAIN:10/26 -j MAIN:now {optional files} + + and refer to date-stamped revisions on the trunk using the + <branch_tag>:<date> support that works on other branches. + + You might also think you could place an explicit tag on branch + 1 (or higher) (e.g. MAINHACK:1) and use it in place of the + implicit "MAIN", but I haven't found the right combination. + + [[If you find working techniques, I'll add them here.]] - 2. Add the file "normally": - cvs add <file> - cvs commit <file> + 4C.8 How do I merge onto the Main Branch a file that exists only on a + branch other than the Main Branch? (i.e. it is in the Attic) - 3. Branch-tag the file using the same <branch_tag> as you did on - all the other files in your directory: + For how such a file can exist, see 3A.2 and 3A.3. - cvs tag -b <branch_tag> <file> + For how to avoid creating such a file, see 3A.5. - 4. And move the file back onto the branch: + Though you might think that the "update -j" command could perform + the "merge" of a file from the side branch to the Main Branch, it + isn't (yet) smart enough. Unfortunately, there is no single CVS + command to do this -- it takes three steps: - cvs update -r <branch_tag> <file> + 1. To move something onto the Main Branch from the Attic, you have + to physically move the file from the Attic to the main + Repository directory associated with your working directory. + It is exactly like resurrecting a removed file. See 3L.4 -=4C.9 How do I know what branch I'm (working) on? + I use something like this: (csh-like syntax) + + set repos = `cat ./CVS/Repository` + mv $repos/Attic/filename,v $repos/filename,v + + (If you use relative paths in your Repository files, that first + line becomes: set repos = $CVSROOT/`cat ./CVS/Repository`) + + 2. Now that the file is physically in the right place within the + Repository, "update -A" will make it appear in your working + directory on the Main Branch. Do that now. + + 3. You now have a choice. The act of physically moving the file + has fused together the <branch_tag> branch and the Main Branch + for this file. You can continue that way, making changes along + the RCS Main Branch which CVS will (for this type of file only) + treat as both the Main Branch and the <branch_tag> branch. + + The other choice, which I would suggest, is to re-tag the file + with <branch_tag>, restoring a normal-looking magic branch tag + to the file: + + cvs tag -F -b <branch_tag> <file> + + + After you have done the above, you can run "update -A" or "update + -r <branch_tag>" to resume whatever you were doing before you + started this procedure. + + Caveat: The final result is a file whose revision tree doesn't + look like it was ever on any branch but the Main Branch until the + above "tag -F -b" command was executed. CVS and RCS have no way + of saving the history of the actions you have just performed. + + + 4C.9 How do I know what branch I'm (working) on? Type: cvs status @@ -5761,7 +6940,7 @@ toward the beginning of this document. 1. The *same* tag is on *every* file in your working tree, *and* 2. That tag matches the contents of the ./CVS/Tag file, *and* - 3. That tag is a branch tag, + 3. That tag is a branch tag, then you know what branch you are working on. You can get sticky Tag information directly from the ./CVS/Entries file instead of @@ -5774,9 +6953,8 @@ toward the beginning of this document. The sticky Tag on each file in the ./CVS/Entries file (as displayed by the "status" command) indicates what branch the - working file is on. New files should be added to the Tag stored - in ./CVS/Tag, but they are not. See the above question on adding - a new file to a branch. + working file is on. New files are added to the Tag stored + in ./CVS/Tag. To force your entire working directory onto the same branch, type: @@ -5796,13 +6974,13 @@ toward the beginning of this document. scheme) is a heavyweight act. Every time you create a real branch in development, you must spawn a set of managerial procedures and a schedule by which you plan to merge each branch into each other - branch. Unless you plan to keep it simple and collapse (by + branch. Unless you plan to keep it simple and collapse (by merging and forgetting) branches quickly, they are not to be created lightly. - In other words, if there aren't group meetings in which the branch - to be worked on is a major topic of discussion, then the group is - not managing branches properly. + In other words, if you don't regularly attend group meetings in + which the branch to be worked on is a major topic of discussion, + then the group is not managing branches properly. We created a couple major branches a few months ago and even the customer service people refer to the "XYZ branch" as a shorthand @@ -5845,7 +7023,7 @@ toward the beginning of this document. For one thing, CVS insists on control of the default branch. It is set either to the Main branch or the Vendor branch depending - on whether you have changed the Vendor's code. If you change + on whether you have changed the Vendor's code. If you change the default branch, you are monkeying with the internals and you will get unexpected results. @@ -5862,29 +7040,28 @@ toward the beginning of this document. When using CVS, all administrative information (such as what branch you checked out) is stored in CVS sub-directories, local to the user. There is no global state, other than the description - and logging files in $CVSROOT/CVSROOT. + and logging files in the $CVSROOT/CVSROOT directory. - You tell "checkout" or "update", via the "-r <tag>" option, - what branch you want to check out. The default is CVS's "Main - Branch". + You tell "checkout" or "update" what branch you want to check out + via the "-r <tag>" option. The default is CVS's "Main Branch". I don't see a problem in *designing* a new way to indicate what branch you get by default, instead of the main one, but that's not how it currently works. -=4C.14 How do I perform a large merge? + 4C.14 How do I perform a large merge? Large merges require a bit more planning to be able to track what has happened in the inevitable cases where something goes - wrong. No tool can make a "merge" make perfect sense. + wrong. No tool can force a "merge" to make perfect sense. Though you can handle the details in many different ways, the two ends of the spectrum of merge techniques are: gonzo and paranoid. - The gonzo method assumes that you know everything about your - sources so that recovery from failures is "just a matter of - typing." You created the branch this way: + A. The gonzo method assumes that you know everything about your + sources so that recovery from failures is "just a matter of + typing." You created the branch this way: cvs checkout <module> cd <module> @@ -5893,12 +7070,12 @@ toward the beginning of this document. >>> Edit away. cvs commit <<== Onto branch - Now you want to merge your branch back into the Main branch, you - are certain you can make it work, or at least detect all the - failures, so you dive in and hack away: (For simplicity, we will - assume you are collapsing (i.e. merging and forgetting) a - side-branch into the Main branch from your single working - directory.) + Now you want to merge your branch back into the Main branch, + you are certain you can make it work, or at least detect all + the failures, so you dive in and hack away: (For simplicity, we + will assume you are collapsing (i.e. merging and forgetting) a + side-branch into the Main branch from your single working + directory.) cvs update -A cvs update -j <branch_tag> @@ -5906,16 +7083,19 @@ toward the beginning of this document. >>> Edit some more to make it all compile and work. cvs commit - Looks simple. For more details on the output from the - "update -j" command, see 3P.2 and 4C.6. + Looks simple. For more details on the output from the + "update -j" command, see 3P.2 and 4C.6. - (Note: You could also checkout a whole new working directory and - perform the merge at the same time by replacing the two update - commands with "cvs checkout -j <branch_tag> <module>". + Note: You could also checkout a whole new working directory and + perform the merge at the same time by replacing the two + update commands with these two commands: + cvs checkout -j <branch_tag> <module> + cd <module> - The paranoid way is more difficult, but it can catch all sorts of - problems. You created the branch this way: + + B. The paranoid way is more difficult, but it can catch all sorts + of problems. You created the branch this way: cvs checkout <module> cd <module> @@ -5925,22 +7105,21 @@ toward the beginning of this document. >>> Edit away. cvs commit <<== Onto branch - The extra tag command places a non-magic tag on the Branch Point, - an act that makes it easier to do "diffs" later. Now we decide - to perform the merge: + The extra tag command places a non-branch tag on the Branch + Point, an act that makes it easier to do "diffs" later. Now we + decide to perform the merge: cvs tag <latest_on_branch_tag> cvs update -A - *1* cvs diff -r <branch_point_tag> -r <latest_on_branch_tag> - >>> *1* holds all the changes on the branch. - *2* cvs diff -r <branch_point_tag> -r HEAD - >>> *2* holds the changes on the trunk since branching. + *1* cvs diff -r <branch_point_tag> -r <latest_on_branch_tag> + >>> *1* shows all the changes on the branch. + *2* cvs diff -r <branch_point_tag> -r HEAD + >>> *2* shows the changes on the trunk since branching. cvs tag <premerge_tag> cvs update -j <branch_tag> >>> Edit the 'C' files and remove the overlaps. - *3* cvs diff - >>> Verify that *3* matches *1*, except for line numbers - and overlaps. + *3* cvs diff + >>> Verify that *3* matches *1*, except for line numbers. cvs commit cvs tag <just_merge_changes_tag> >>> Edit some more to make it all compile and work. @@ -5957,7 +7136,11 @@ toward the beginning of this document. Repository while you do this. If they commit something while you are in the middle of a merge, your job will be much more difficult. If they "update" at the wrong time, their work will - be randomized until you finish. It's better to call a halt. + be randomized until you finish. It's better to call a halt. + + See 3H.13 for some more information about dealing with merges + after import. The last part of the procedure is applicable to any + large merge. 4C.15 Is a Vendor merge any different from a branch merge? @@ -5970,7 +7153,7 @@ toward the beginning of this document. See the "import" command in section 3H. -+4C.16 How do I go back to a previous version of the code on a branch? + 4C.16 How do I go back to a previous version of the code on a branch? You can avoid digging into RCS revision numbers (executing "update -r <rev>" on each file) by trying one of these: @@ -5978,7 +7161,7 @@ toward the beginning of this document. 1. Use non-branch tags as you normally would. Non-branch tags attach to specific revisions, so a "tag <tag>" command would mark the revisions you have in your working directory, which - are on your branch. If you need to retrieve them, use "update + are on your branch. If you need to retrieve them, use "update -r <non-branch-tag>" Doing this overrides the sticky <branch_tag> attached to your @@ -5990,7 +7173,7 @@ toward the beginning of this document. This is almost like using the '-D' option, but it looks for revisions extant on <date> only along the given branch. - + As in #1, you can't commit to this kind of working area, because it has a sticky date referring to revisions in the middle of a branch. @@ -5999,28 +7182,196 @@ toward the beginning of this document. 3. You can branch a branch. If you add a branch tag to file in a working directory that was - checked out on a branch, you will branch the branch. This + checked out on a branch, you will branch the branch. This works just fine, though you'll have to play some games to merge everything back together again. You'll also create 6-part revision numbers. (They'll be 8-part revision numbers if you - branch a branch that started out with some unmodified files the - Vendor branch. Think about it. How does revision + branch a branch that started out with some unmodified files on + the Vendor branch. Think about it. How does revision 1.2.4.2.4.2.2.1 grab you?) -+4C.17 Why do I get the latest files on the branch when I tried to + 4C.17 Once I've found the files I want, how do I start changing them? + I keep getting warnings about sticky tags. + + What you probably did was type "cvs update -r <tag>" where <tag> + is a non-branch tag. "update" created a sticky tag for a specific + revision, not a branch. To start working right there, you have to + create a branch to work on. + + You have two choices. + + A. You can do it in place and keep working: + + cvs tag -b <branch_tag> <<== To tag the current files. + cvs update -r <branch_tab> <<== To move onto the branch. + + B. You can do it "externally" and create a new working directory: + + cvs rtag -b -r <tag> <branch_tag> <module> + cvs checkout -r <branch_tag> <module> + + <module> can be a relative path within the Repository. + + <tag> in the above is the non-branch tag you placed earlier + that caused the error in your question. Be warned that + if <tag> is not set on all the files (or all the right + revisions) you won't get exactly what you wanted. + + + 4C.18 Why do I get the latest files on the branch when I tried to "update -r <tag>"? If "update -r <tag>" always retrieves the latest files on a - branch, then <tag> is a branch tag. A branch tag is supposed to - be used to grab a branch to work on. Since you can't modify a - file in the middle of a branch, checking out a <branch_tag> will - give you the latest revision on the branch. + branch, then <tag> is really a <branch_tag>. A branch tag is + supposed to be used to grab a branch to work on. Since you can't + modify a file in the middle of a branch, checking out a + <branch_tag> will give you the latest revision on the branch. If you want to "checkout" a specific collection of revisions, you must use a "non-branch" tag. See the first part of 4C.16. - You *can* branch off a branch, but it is rarely needed. + + 4C.19 How can I avoid a merge? I just want to move the latest revision + on my working branch directly onto the trunk. + + There is no direct way to do this using CVS, though the technique + is not difficult using shell commands. Here's one way: + + 1. Move your working directory to the Main Branch. + + cvs update -A + + 2. Use "update -p" to grab the latest revision on the branch and + write it over your working files. Make sure you don't have an + modified files -- you will lose them. The following is in + "csh" syntax. Change the wildcard to grab the files you want + + foreach i (Makefile *.cc *.hh) + cvs update -p -r <branch_tag> $i > $i + end + + 3. Commit all the working files onto the Main Branch. + + cvs commit -m 'Moved branch <branch_tag> onto MAIN' + + You should experiment with the above before blasting everything. + + + 4C.20 How to I avoid merge collisions in the RCS $\Log$ data? + + In short, you can't. The RCS $\Log$ keyword is handled + differently from all other RCS keywords. + + On the info-cvs mailing list, there is a periodic discussion that + goes something like this: + + Question: How do I deal with $\Log$? + Answer1: You can't do much with it. Here's how it works. . . + Answer2: I've found a limited way to use it. . . + Answer3: Get rid of it. $\Log$ is an abomination. + + I tend to lean toward answer #3. There are only two sets of + people who would ever have access to logs stored within sources + files, developers and source customers. + + For developers: + + 1. Log entries within sources files are notoriously incomplete, + rushed, poorly phrased and in many cases incorrect, making them + useless for debugging or file maintenance. I remember a maxim + from "Software Tools" (I believe): "Read the code, not the + comments." No managerial order or plan for programmer + discipline will affect this in the real world. + + 2. Log entries are usually in an unreadable mixture of styles. + Many log entries are just plain meaningless. Some are foolish. + Some are even insulting. Examples: + + "Corrected spelling of misspelling." + "Bug fix." + "Reversed stupid change in previous revisions." + "If Joe could do his job, this would already have worked." + + 3. Log entries are not managed well by the tools. Any merge can + cause conflicts in the $\Log$ data. Branch merges produce + incomplete logs. They can be edited into chaos and they are + not regenerated. They waste space duplicating information + available to the developer with a single command. + + 4. Even if correct when originally entered, as changes are made to + the file, log entries become false over time. Humans are not + good at reading down through a list and remembering only the + last change affecting something. Over time *most* of the log + is wrong. + + 5. Even if still correct, the log data is almost useless to + developers without the code diffs. If you can get code diffs, + you can display the log. + + + For source customers the problem is even worse. The last thing + you want to show customers is a hodge-podge of tiny comments about + large changes followed by a series of emergency fixes before + delivery. If you distribute sources, then you should provide + documentation, or changelogs reviewed by people who won't let + comments like "Fixed for stupid customer." out the door. + + Conclusion: Though some people would prefer to see in this FAQ + techniques for making the $\Log$ entries the best they can be, I + believe them to be a lost cause. My suggestion is to hunt down, + root out and destroy all occurrences of $\Log$ and the unusable + data attached to it wherever you may find it. + + + 4C.21 Why should I trust automatic merges? + + Some developers have the feeling that three-way merging doesn't + work. They fear and distrust the way the "update" command + automatically merges committed changes from the Repository into + the working file. + + Experience has shown that most merges are utterly painless and + most of the rest are easily resolved. The few conflicts that + cause headaches are nearly all due to poor communication between + developers, a problem no source control system can obviate. + + Some developers were troubled in the past by flaky Unix software. + I can't say that everything is perfect, but the tools CVS depends + on (RCS and diff, mainly) are fairly solid nowadays. They work. + + Since it does seem to work for most of us, the algorithm is + unlikely to change soon. Why not test it on a couple trouble + spots and if it works for you, use it for a while? Then you can + make an informed decision. + + + 4C.22 How does CVS decide if it can safely perform a merge? + + CVS can merge any text file, possibly discovering a conflict and + leaving overlaps for you to edit. Editing the conflict markers + out of the file is a moment's work, but resolving the conflict + could take an arbitrary amount of time. CVS works to determine if + it *should* merge, not if it *can*. + + See 2B.6 for how the merge proceeds. + + + 4C.23 After resolving merge conflicts in a file, what if I want to keep + my previous version, and not take any of the branch changes? + + If you want to retain your previous version, a version on the + MAIN branch greater than 1.1 (one you committed there), just throw + the merged file away and "cvs update" the file. + + You don't need to commit something to remember it. The tags you + place before and after the merge should give all the handles you + need to find various versions. You don't have to create a new + version of the file. + + If you want to retain the previous Vendor revision, you can grab a + copy of it using "cvs update -p" and commit it or use the + technique described in 3B.3 to revert back to the Vendor branch. @@ -6036,49 +7387,47 @@ Some are therefore dangerous. Avoid anything you don't fully understand. **** Questions: -=4D.1 How can you even check in binary files, let alone allow CVS to + 4D.1 How can you even check in binary files, let alone allow CVS to do its auto-merge trick on them? 4D.2 Can I edit the RCS (",v") files in the Repository? 4D.3 Can I edit the ./CVS/{Entries,Repository,Tag} files? -=4D.4 Someone executed "admin -o" and removed revisions to which + 4D.4 Someone executed "admin -o" and removed revisions to which tags/symbols were attached. How do I fix them? -=4D.5 How do I move a magic branch tag? + 4D.5 How do I move or rename a magic branch tag? 4D.6 Can I use RCS locally to record my changes without making them globally visible by committing them? 4D.7 How can I allow access to the Repository by both CVS and RCS? -=4D.8 I "updated" a file my friend "bubba" committed yesterday. + 4D.8 I "updated" a file my friend, "bubba", committed yesterday. Why doesn't the file now have a modified date of yesterday? -#4D.9 While in the middle of a large "commit", how do I run other + 4D.9 While in the middle of a large "commit", how do I run other commands, like "diff" or "stat" without seeing lock errors? - 4D.10 Why does the merge occasionally resurrect lines of code? -=4D.11 Why does the merge fail when my "rcsmerge" program is - configured to use GNU diff version 2.1 or later? - 4D.12 What the hell is Entries.Static? -=4D.13 Why did I get the wrong Repository in the loginfo message? -=4D.14 Can I have multiple source repositories, one for each project? - 4D.15 How do I run CVS setuid so I can only allow access through the + 4D.10 Where did the ./CVS/Entries.Static file come from? What is it for? + 4D.11 Why did I get the wrong Repository in the loginfo message? + 4D.12 How do I run CVS setuid so I can only allow access through the CVS program itself? - 4D.16 How about using groups and setgid() then? - 4D.17 How do I use the "commitinfo" file? - 4D.18 How do I use the "loginfo" files? + 4D.13 How about using groups and setgid() then? + 4D.14 How do I use the "commitinfo" file? + 4D.15 How do I use the "loginfo" files? + 4D.16 How can I keep people with restrictive umask values from blocking + access to the Repository? + 4D.17 Why do timestamps sometimes get set to the date of the revision, + sometimes not? The inconsistency causes unnecessary recompiles. **** Answers: -=4D.1 How can you even check in binary files, let alone allow CVS to + 4D.1 How can you even check in binary files, let alone allow CVS to do its auto-merge trick on them? If you configure RCS and CVS to use the GNU version of diff with - the '-a' option, CVS and RCS will handle binary files. See + the '-a' option, CVS and RCS will handle binary files. See section 4A for configuration info. - You also need to apply the '-ko' flag to the files to avoid + You may also need to apply the '-ko' flag to the files to avoid expanding RCS keywords, which can be done via: cvs admin -ko filename - (You should be able to do it by handing "import" the -ko option, - but that isn't yet in the official release.) The only real problem occurs when "cvs update" attempts to merge binary revisions committed elsewhere into a modified working file. @@ -6086,10 +7435,9 @@ Some are therefore dangerous. Avoid anything you don't fully understand. Frame or Interleaf (document processing systems) that produce non-text output. - [[I know of no solution to this other than to keep binaries in - some text form as "source" (real binaries could be uuencoded, - documents could be stored in "exported" (something like SGML) - form.]] + See 3C.8 for a way to serialize access to binary files. + See 3A.8 for adding binary files, 3H.4 for importing binary files + and 3B.4 for some more information about "admin". 4D.2 Can I edit the RCS (",v") files in the Repository? @@ -6104,17 +7452,17 @@ Some are therefore dangerous. Avoid anything you don't fully understand. people have succumbed to the urge to do so when pressed for time. The reasons given, usually with evident contrition, include: - - Editing mistakes in, or adding text to, log entries. (If you + - Editing mistakes in, or adding text to, log entries. (If you have RCS 5.6 or later, you should use `cvs admin -m'.) - Renaming or moving symbolic names. (You should `cvs admin -N' instead.) - - Unlocking a file by changing the "locker" from someone else to + - Unlocking a file by changing the "locker" from someone else to yourself. (It's safer to use `cvs admin -u -l'.) - Making global changes to past history. Example: Eradicating former employees names from old documents and Author entries. (And someone thought the "history" command was evidence of Big - Brother! I had never realized how much help CVS/RCS could have - provided to The Ministry of Truth.) + Brother! I never realized how much help a wide-open revision + control system could have provided to The Ministry of Truth.) 4D.3 Can I edit the ./CVS/{Entries,Repository,Tag} files? @@ -6127,10 +7475,10 @@ Some are therefore dangerous. Avoid anything you don't fully understand. large tree. But that is nearly the only reason to do so. -=4D.4 Someone executed "admin -o" and removed revisions to which + 4D.4 Someone executed "admin -o" and removed revisions to which tags/symbols were attached. How do I fix them? - It depends on what you mean by "fix". I can think of three ways + It depends on what you mean by "fix". I can think of three ways to fix your predicament: @@ -6140,14 +7488,20 @@ Some are therefore dangerous. Avoid anything you don't fully understand. associated tags, you can remove them with the "admin" command. The "tag -d" command will only remove tags attached to existing revisions. You can remove a tag, even if it is attached to a - non-existent filename, by typing: + non-existent revision, by typing: cvs admin -N<tag> <file> 2. Retrieve the outdated revision. - Using CVS and RCS, there is no way to reconstruct an outdated - revision. You will have to resort to backups. + You should first look in your backup system for recent versions + of the file. If you can't use them, you can carefully extract + each revision that followed the earliest outdated revision + using RCS (or "cvs admin") commands and reconstruct the file + with all the right revisions, branches and tags. This is a lot + of work. + + You *can't* insert a revision into the current RCS file. 3. Move the Tags to another revision in each file. @@ -6162,40 +7516,140 @@ Some are therefore dangerous. Avoid anything you don't fully understand. cvs update -r <rev> <file> cvs tag <tag> <file> - b. Use "admin" to set the tag to a specific revision: + b. Use "admin" to set the tag to a specific revision: cvs admin -N<tag>:<rev> <file> -=4D.5 How do I move a magic branch tag? + 4D.5 How do I move or rename a magic branch tag? + + (To rename a non-branch <tag> see 3O.9.) + + Before reading this, read 3M.3 and 3M.4 and understand exactly + how tag and rtag use '-r' and why it won't do the right job here. + + A. First, I have to explain exactly what a magic branch tag is. + + A magic <branch_tag> is an artificial tag attached to a + non-existent revision on a non-existent branch number zero. It + looks like this: + + TAG1:<X>.0.Y + + <X> is the "branch point revision", a normal revision with an + odd number of '.'s in it. (e.g. 1.5, 1.3.1.6, etc) + + Y is an even number (e.g. 2, 4, 6, etc.) All CVS branches, + other than the Vendor branch, are even numbered. + + TAG1 is considered by CVS to be attached to revision <X>. The + first "update -r TAG1 <file>" after applying TAG1 will produce + a copy of revision <X> with a sticky tag of TAG1. The first + "commit" to that file will cause CVS to construct an RCS branch + named <X>.Y and check in revision <X>.Y.1 on the new branch. + + Note: TAG1 is *not* considered to be attached to <X> by RCS, + which explains why you can't refer directly to the branch point + revision for some CVS commands. + + + B. Moving a magic <branch_tag> is the act of reapplying the same + tag to different revisions in the file: + + TAG1:<X>.0.Y + to + TAG1:<X>.0.Z or TAG1:<A>.0.B + + You can move a magic branch tag to the revisions of your choice + by using "update" to find the revisions you want to tag and + reapplying the tag to all the files with the '-F' option to + force it to move the existing <branch_tag>. + + cvs update -r <tag/rev> (or '-A' for the Main Branch) + cvs tag -F -b <branch_tag> + + If the earlier location of TAG1 refers to a physical branch + within any RCS file, moving it will make the existing branch in + the file seem to disappear from CVS's view. This is not a good + idea unless you really want to forget the existence of those + RCS branches. + + If the "update" above retrieves the original branch point + revision (<X>), the "tag" command above will create the tag: + + TAG1:<X>.0.Z + + Where Z is 2 greater than the highest magic branch already on + revision <X>. The TAG1 branch will still have the same branch + point (i.e. revision <X>), but the first commit to the new TAG1 + branch will create a different RCS branch number (<X>.Z instead + of <X>.Y). + + + C. Renaming a magic <branch_tag> is the act of changing + + TAG1:<X>.0.Y + to + TAG2:<X>.0.Y + + There is no harm in changing a tag name as long as you forget + that TAG1 ever existed and you clean up any working directories + with sticky TAG1 tags on them by using "update -A", "update -r + <other_tag>" or by removing the working directories. + + On the other hand, actually changing the tag is not easy. + + See 3M.3 for why the seemingly obvious solution won't work: + + cvs tag -b -r <old_branch_tag> <new_branch_tag> + + The only direct way to rename a magic tag is to use the "admin" + command on each file: (You might want to use '-n'. Read "man + rcs" and look at the '-n' and '-N' options.) + + cvs admin -N<new_branch_tag>:<old_branch_tag> . + cvs tag -d <old_branch_tag> + + But you have to be careful because "admin" is different from + other CVS commands: - If the <branch_tag> refers to a physical branch within an RCS - file, renaming a tag will make the existing branch in the file - seem to disappear. This is not a good idea. + 1. "admin" can be used recursively, but only by specifying + directory names in its argument list (e.g. '.'), - If the magic branch has never had a revision committed to it, you - can move the branch by re-executing the "tag" or "rtag" command - that created it. The <branch_tag> will be moved to the place - where it would have appeared if you were tagging the file for the - first time. + 2. Where "rtag -r <old_branch_tag>" would interpret + <old_branch_tag> as a magic CVS branch tag, "admin" is a + direct interface to RCS which sees a magic branch tag as + a simple (though non-existent) RCS revision number. + + This is good for us in this particular case, but different + from normal CVS. + + 3. "admin" also skips the Attic and produces different kinds + of errors than CVS usually does. (Because they are coming + directly from RCS.) + + + The other way to rename a magic <branch_tag> is to edit the + Repository files with a script of some kind. I've done it in + the past, but I'll leave it as an exercise for the reader. 4D.6 Can I use RCS locally to record my changes without making them globally visible by committing them? You can, but it will probably confuse CVS to have ",v" files in - your working directory. And you will lose all your log entries + your working directory. And you will lose all your log entries when you finally commit it. Your best bet is to create your own CVS branch and work there. You can commit as many revisions as you want, then merge it back - into the main line when you are finished. + into the main line (or parent branch) when you are finished. 4D.7 How can I allow access to the Repository by both CVS and RCS? The first step is to try not to. If some people are using CVS, - there is no reason for everyone not to. It is not hard to learn + there is no reason for everyone not to. It is not hard to learn the basics and CVS makes certain operations *easier* than a series of RCS commands. Personal preference in what software tools can be applied to a shared Repository has to take second place to @@ -6229,19 +7683,22 @@ Some are therefore dangerous. Avoid anything you don't fully understand. 3. Normal RCS practice locks a file on checkout with "co -l". In such an environment, RCS users should plan to keep survival - gear and food for at least 30 days near their desks. When + gear and food for at least 30 days near their desks. When faced with bizarre and unexpected permission errors, howling mobs of slavering CVS users will run the RCS users out of town with pitchforks and machetes. + See 3C.8 for a way to avoid machetes aroused by lock collisions. + 4. Though files checked in by RCS users will correctly cause "up-to-date" failures during CVS "commits" and they will be auto-merged into CVS working directories during "update", the opposite won't happen. RCS users will get no warning and will not be required to merge - older work into their code. They can easily checkin an old - file on top of a new revision added by CVS, discarding work. + older work into their code. They can easily checkin an old + file on top of a new revision added by CVS, discarding work + committed earlier by CVS users. See the howling mob scenario described above. @@ -6249,24 +7706,26 @@ Some are therefore dangerous. Avoid anything you don't fully understand. RCS is great. I have used it for years. But I wouldn't mix it this way. In a two-camp society, you are asking for real trouble, both in technical hassles to clean up and in political hassles to - soothe. + soothe. Branch merges will also be a major problem. -=4D.8 I "updated" a file my friend "bubba" committed yesterday. + 4D.8 I "updated" a file my friend, "bubba", committed yesterday. Why doesn't the file now have a modified date of yesterday? CVS restores dates from the RCS files only on first "checkout". After that, it is more important to maintain a timestamp relative to the other files in the working directory. - Example: I commit a source file at 5PM. You commit the same file - at 6PM. At 7PM, I compile my file. Then I execute "update". If - CVS sets the date to the one in the RCS file, the file would be - given a timestamp of 6PM and my Makefile wouldn't rebuild anything - that depended on it. Bad news. + Example: You committed a source file at 5PM. Bubba updated his + copy of the file, grabbing your changes, then changed and + committed a new revision of the file at 6PM. At 7PM, you compile + your file. Then you execute "update". If CVS sets the date to + the one in the RCS file, the file would be given a timestamp of + 6PM and your Makefile wouldn't rebuild anything that depended on + it. Bad news. Note that the same logic applies to retrieving a revision out of - the repository to replace a deleted file. If CVS changes your + the Repository to replace a deleted file. If CVS changes your file in an existing working directory, whether it was because a new revision was committed by someone else or because you deleted your working file, the timestamp on the retrieved working file @@ -6278,9 +7737,10 @@ Some are therefore dangerous. Avoid anything you don't fully understand. more important for the timestamps on the local files to be consistent with each other than than it is for working files to match the timestamps on the files in the Repository. + See 4D.17 for some more about timestamps. -#4D.9 While in the middle of a large "commit", how do I run other + 4D.9 While in the middle of a large "commit", how do I run other commands, like "diff" or "stat" without seeing lock errors? Type: @@ -6288,22 +7748,24 @@ Some are therefore dangerous. Avoid anything you don't fully understand. The '-n' option to the main cvs command turns off lock checking, a - reasonable act given the promise offered by '-n' not to alter - anything. The "diff", "log" and "stat" commands all provide the - same information with and without the '-n' option. + reasonable act for read-only commands given the promise offered by + '-n' not to alter anything. The "diff", "log" and "stat" commands + provide the same information (for files that are not being + committed) when used with and without the '-n' option. Warning: Ignoring locks can produce inconsistent information across a collection of files if you are looking at the revisions affected by an active commit. Be careful when creating "patches" from the output of "cvs -n diff". If you are looking only at your working files, tagged revisions, and BASE revisions (revisions - whose numbers are read from your CVS/Entries files), you should - get consistent results. Of course, if you catch a single file in + whose numbers are read from your ./CVS/Entries files), you should + get consistent results. Of course, if you catch a single file in the middle of RCS activity, you might get some strange errors. - Note that this is "cvs -n <command>". The visually similar - command "cvs <command> -n" has no relation to the former usage and - has an entirely different meaning for each command. + Note that the suggested command is "cvs -n <command>". The + visually similar command "cvs <command> -n" has no relation to the + suggested usage and has an entirely different meaning for each + command. "cvs -n update" also works in the middle of a commit, providing slightly different information from a plain "cvs update". But, of @@ -6314,143 +7776,69 @@ Some are therefore dangerous. Avoid anything you don't fully understand. Repository files. You need RCS version 5 or later for the commands described above - to work entirely reliably. - - - 4D.10 Why does the merge occasionally resurrect lines of code? + to work reliably. - The diff3 program provided by GNU diff version 1.15 has a bug - that occasionally causes text to come back from the dead. - If you plan to upgrade to the latest GNU diff program, see the - next question. + 4D.10 Where did the ./CVS/Entries.Static file come from? What is it for? + Each CVS working directory contains a ./CVS/Entries file listing + the files managed by CVS in that working directory. Normally, if + the "update" command finds a file in the Repository that is not in + the ./CVS/Entries file, "update" copies the appropriate revision + of the "new" file out of the Repository and adds the filename to + the Entries file. This happens for files: -=4D.11 Why does the merge fail when my "rcsmerge" program is - configured to use GNU diff version 2.1 or later? + 1. Added to the Repository from another working directory. + 2. Dragged out of the Attic when switching branches with + "update -A" or "update -r". + 3. Whose names were deleted from the ./CVS/Entries file. - A change in the overlap format was introduced in GNU diff3 - between versions 2.0 and 2.1. + If the ./CVS/Entries.Static file exists, CVS will only bring out + revisions of files that are contained in either ./CVS/Entries or + ./CVS/Entries.Static. If a Repository file is found in *neither* + file, it is ignored. - To get consistent rcsmerge behavior, you have four choices: + The ./CVS/Entries.Static file is created when you check out an + individual file or a module that creates working directories that + don't contain all files in the corresponding Repository directory. + In those cases, without an ./CVS/Entries.Static file, a simple + "update" would bring more files out of the Repository than the + original "checkout" wanted. - 1. Go back to using GNU diff 1.15 or 2.0. If you want to use - GNU diff 2.1 or later, you'll have to pick one of the other - three choices in this list. + The ./CVS/Entries.Static file can be removed by hand. It is + automatically removed if you run "update -d" to create new + directories (even if no new directories are created). + (Internally, since "checkout" turns on the '-d' flag and calls the + "update" routine, a "checkout" of a module or directory that + writes into an existing directory will also remove the + ./CVS/Entries.Static file.) - 2. Grab RCS version 5.6.0.1 from an FSF archive and set the - DIFF3_A macro to '1' as it tells you to in the Makefile: - #define DIFF3_A 1 - - 3. Patch the RCS 5.6 source. Change line 84 in "merger.c" from: - - DIFF3, "-am", "-L", label[0], "-L", label[1], - to - DIFF3, "-amE", "-L", label[0], "-L", "", "-L", label[1], - - 4. Wait both for RCS version 5.7 to be released and for a new - version of CVS that can deal with it. - - - 4D.12 What the hell is Entries.Static? - - Each ./CVS administrative directory contains an Entries file, - listing the files under CVS in the directory above it. If a new - file is added to the Repository, an "update" command copies it - out of the Repository and adds it to the Entries file. - - If your ./CVS directory has an Entries.Static file in it, CVS - checks it before bringing new files out of the Repository. If a - new file is *not* in Entries.Static, it is not checked out. - - The Entries.Static file is created by checking out something that - doesn't include all files in a directory. Without an - Entries.Static file, the first "update" would bring more files - out of the Repository. - - Examples: - - - A multi-module checkout renamed with the "checkout -d" option. - - - Checking out a module specified with directory and filenames. - - The Entries.Static file is removed by an "update" with the - '-A', '-r' or '-D' option. - - -=4D.13 Why did I get the wrong Repository in the loginfo message? + 4D.11 Why did I get the wrong Repository in the loginfo message? You probably: - - Use multiple Repositories. + 1. Use multiple Repositories. - - Configured CVS to use absolute pathnames in the - ./CVS/Repository file. + 2. Configured CVS to use absolute pathnames in the + ./CVS/Repository file. - - Typed the "commit" command in one Repository with your - $CVSROOT pointing at the other. + 3. Configured CVS not to use the ./CVS/Root file. + 4. Typed the "commit" command in one Repository with your + $CVSROOT pointing at another. "commit" and all other CVS commands will heed an absolute pathname in the ./CVS/Repository file (or in the "-d CVSrootdir" override), but the log function doesn't take arguments -- it just looks at $CVSROOT. - -=4D.14 Can I have multiple source repositories, one for each project? - - Yes, you can have as many Repositories as you like. But each - Repository must be managed separately, creating additional work. - - Question 4A.1 provides a short description of setting up a - single Repository. A few additional considerations: - - 1. It is a good idea to start by creating a single Repository and - split it up (or create additional Repositories) only if you - believe it is really necessary. I would only create a new - Repository if the data is completely disconnected from the rest - of the main Repository. - - 2. If there is a lot of overlap among the developers working on - the collections of files you want to place in different - Repositories, or if there is any connection between those - collections, I would go out of my way to create a single - Repository. It is much easier to manage. - - 3. Disk space should not be a factor since you can build up a - Repository using symbolic links and/or remote mounts. - - 4. Each Repository is completely distinct. You can't check out - modules from different Repositories at the same time. A better - way of looking at it is that if you *can* check out two modules - or directories with a single "checkout" command (without - contortions or explicit absolute pathnames), then they are in - the same Repository. - - 5. To "checkout" modules from multiple Repositories, you must use - the "cvs -d" option on all CVS commands or alter your $CVSROOT - variable when you change focus to another Repository. If you - work with multiple Repositories, it is a good idea to configure - CVS to use absolute pathnames in the ./CVS/Repository file, - since most commands (other than "checkout") will use that file - rather than $CVSROOT. - - 6. If you configure CVS to use relative pathnames in your - ./CVS/Repository files, you must always be careful to set your - $CVSROOT properly or you will get unexpected results. - - One monster of an unexpected result can happen when you have - two modules or directories by the same name at the same - relative path inside the Repository, in two different - Repositories. You can update a directory with completely - unrelated files. This is not a fanciful example -- a - Repository is occasionally duplicated for release purposes in - which case *all* the paths in the two Repositories are the - same. + If you avoid even one of the four steps above, you won't see this + problem. If you configure ./CVS/Root, you won't be allowed to + execute the program causing the error. - 4D.15 How do I run CVS setuid so I can only allow access through the + 4D.12 How do I run CVS setuid so I can only allow access through the CVS program itself? Setuid to root is not a great idea. Any program that modifies @@ -6470,7 +7858,7 @@ Some are therefore dangerous. Avoid anything you don't fully understand. uid's. Also, you have to invent a fake user whose name will show up in - various places. But many sites, especially those who might want a + various places. But many sites, especially those who might want a setuid CVS for "security", want personal accountability -- no generic accounts. I don't know whether accountability outweighs file security. @@ -6479,7 +7867,7 @@ Some are therefore dangerous. Avoid anything you don't fully understand. command, you are leaving yourself unprotected anyway. - 4D.16 How about using groups and setgid() then? + 4D.13 How about using groups and setgid() then? Here is a way to run CVS setgid in some environments: @@ -6490,10 +7878,11 @@ Some are therefore dangerous. Avoid anything you don't fully understand. This will allow "access" to work on systems where it only works on the real gid. - 1. Create a group named "cvsg", for example. Name it as you wish. + 1. Create a group named "cvsg". (This example uses "cvsg". You + can name it as you wish.) - 2. Put *no* users in the "cvsg" group. You can put Repository - administrators in this group, if you really want to. + 2. Put *no* users in the "cvsg" group. You can put Repository + administrators in this group if you want to. 3. Set the cvs executable to setgid (not setuid): @@ -6507,14 +7896,18 @@ Some are therefore dangerous. Avoid anything you don't fully understand. access to the files by the "cvsg" group (which has no members!) and no access at all to anyone else. - find $CVSROOT -type d -exec chmod 2770 {} \; + find $CVSROOT -type d -exec chmod 2770 {} \; + + On some systems you might have to type: - This should allow only the cvs program (or other setgid to group - cvsg) programs to write into the area, but no one else. Yes the + find $CVSROOT -type d -exec chmod u=rwx,g=rwx,o=,g+s {} \; + + This should allow only the cvs program (or other "setgid to group + cvsg") programs to write into the area, but no one else. Yes the user winds up owning the file, but s/he can't find it again later - since s/he can't traverse the tree. (If you allow the world - execute bit (octal 001) on directories, the user who last wrote - the file can still write to it.) + since s/he can't traverse the tree. (If you enable the world + execute bit (mode 2771) on directories, users can traverse the + tree and the user who last wrote the file can still write to it.) If you want to allow read access, check out an entire tree somewhere. You have to do this anyway to build it. @@ -6525,17 +7918,18 @@ Some are therefore dangerous. Avoid anything you don't fully understand. reset the group every time you create a new file. I have not tested this. - The setgid() method shares the "admin" problem with the - setuid() method. + The setgid() method shares with the setuid() method the problem of + keeping "admin" from breaking things. - 4D.17 How do I use the "commitinfo" file? + 4D.14 How do I use the "commitinfo" file? Go read 4B.2 first. The "commitinfo" file allows you to execute "sanity check" - functions before allowing a commit. If the function exits with a - non-zero status, the commit is denied. + functions before allowing a commit. If any function called from + within the commitinfo file exits with a non-zero status, the + commit is denied. To fill out a "commitinfo" file, ask yourself (and those sharing your Repository) these questions: @@ -6543,8 +7937,12 @@ Some are therefore dangerous. Avoid anything you don't fully understand. - Is there anything you want to check or change before someone is allowed to commit a file? If not, forget commitinfo. + If you want to serialize binary files, you might consider + something like the rcslock.pl program in the contrib directory + of the CVS sources. + - Do you want to execute the same exact thing before committing to - every file in the Repository? (This is useful if you want to + every file in the Repository? (This is useful if you want to program the restrictions yourself.) If so, set up a single line in the commitinfo: @@ -6554,7 +7952,8 @@ Some are therefore dangerous. Avoid anything you don't fully understand. traverses, passing as arguments the directory and the files to be committed within that directory. - Write your program accordingly. + Write your program accordingly. Some examples exist in the + contrib directory. - Do you want a different kind of sanity check performed for different directories? If so, you'll have to decide what to do @@ -6572,10 +7971,10 @@ Some are therefore dangerous. Avoid anything you don't fully understand. ALL /absolute/path/to/program It is executed independently of all the above. And it's - repeatable as many times as you like. + repeatable -- you can have as many ALL lines as you like. - 4D.18 How do I use the "loginfo" files? + 4D.15 How do I use the "loginfo" files? See 4B.2 and the "commitinfo" question above. @@ -6587,7 +7986,7 @@ Some are therefore dangerous. Avoid anything you don't fully understand. All the commands in the "loginfo" file should read data from standard input, then either append it to a file or send a message to a mailing list. If you want to make it simple, you can put - shell (the shell used by "popen") command lines directly in the + shell (the shell used by "popen(3)") command lines directly in the "loginfo" (or "commitinfo") file. These seem to work: ^special /usr/ucb/Mail -s %s special-mailing-list @@ -6595,21 +7994,71 @@ Some are therefore dangerous. Avoid anything you don't fully understand. DEFAULT (echo '===='; echo %s; cat) > /path/name/to/log/file + 4D.16 How can I keep people with restrictive umask values from blocking + access to the Repository? + + If a user creates a new file with restricted permissions + (e.g. 0600), and commits it, the Repository will have a file in it + that is unreadable by everyone. The 0600 example would be + unreadable by *anyone* but root and the user who created it. + + There are 3 solutions to this: + + 0. Let it happen. This is a valid way to protect things. If + everyone is working alone, a umask of 077 is OK. If everyone + is working only in small groups, a umask of 007 is OK. + + 1. Train your users not to create such things if you expect to + share them. + + 2. See 4B.5 for a small script that will reset the umask. + + I personally don't like the idea of a program automatically + *loosening* security. It would be better for you all to talk + about the issue and decide how to work together. + + + 4D.17 Why do timestamps sometimes get set to the date of the revision, + sometimes not? The inconsistency causes unnecessary recompiles. + + The "checkout" command normally sets the timestamp of a working + file to match the timestamp stored on the revision in the + Repository's RCS file. + + The "commit" command retains the timestamp of the file, if the + act of checking it in didn't change it (by expanding keywords). + + The "update" command sets the time to the revision time the first + time it sees the file. After that, it sets the time of the file + to the current time. See 4D.8 for a reason why. + + Here's a two-line PERL program to set timestamps on files based on + other timestamps. I've found this program useful. When you are + certain you don't want a source file to be recompiled, you can set + its timestamp to the stamp on the object file. + + #!/usr/local/bin/perl + # + # Set timestamp of args 2nd-Last to that of the first arg. + # + ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime) + = stat(shift); + utime($atime,$mtime,@ARGV); + + ---------------- --- Section 4E -- Weirdness +-- Section 4E -- Internal errors ---------------- **** Questions: 4E.1 Explain: "ci error: unexpected EOF in diff output" -=4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" -=4E.3 I don't want a Vendor branch. Why can't I work on the main trunk? - 4E.4 Merges can't work. I don't trust them. If you won't change it to - something I can understand, I won't use CVS. - 4E.5 Explain: "co error, line 2: Missing access list" -+4E.6 Explain: "error: RCS file name `xyz .c' contains white space" -+4E.7 Explain: cvs checkout: warning: <X> is not (any longer) pertinent + 4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" + 4E.3 Explain: "co error, line 2: Missing access list" + 4E.4 Explain: "error: RCS file name `xyz .c' contains white space" + 4E.5 Explain: cvs checkout: warning: <X> is not (any longer) pertinent + 4E.6 Why did a Repository file change from <file>,v to ,<file>,? **** Answers: @@ -6623,12 +8072,12 @@ Some are therefore dangerous. Avoid anything you don't fully understand. - Committing a binary file. - Filesystem failures (NFS!) that put nulls in your file. - The solution is to upgrade to RCS 5.5 or later. (Of course, this - won't fix filesystem failures. It will merely allow RCS (and + The solution is to upgrade to RCS 5.5 or later. (Of course, this + won't fix filesystem failures. It will merely allow RCS (and therefore CVS) to handle the file without error.) -=4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" + 4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" This is an RCS error that occurs when its internal lock file has been left around by an RCS command interrupted by some sort of @@ -6638,63 +8087,39 @@ Some are therefore dangerous. Avoid anything you don't fully understand. "file.c,v", usually starting with ',', '_' or '#'. Make sure they are really crash remnants and do not belong to transactions in progress -- a recent last-modified timestamp - is a good indicator of a live transaction. Delete them. - - -=4E.3 I don't want a Vendor branch. Why can't I work on the main trunk? - - The Vendor branch is the way "import" deals with a Vendor - release. If you do it any other way, you are wasting your time. - CVS was designed to work this way. - - If you are not working with 3rd party (i.e. Vendor) sources, you - can skip the "import" and either move pre-existing RCS files into - the Repository, or apply the RCS "ci" command to your source files - by hand (creating ",v" files) and move them into the Repository. - - - 4E.4 Merges can't work. I don't trust them. If you won't change it to - something I can understand, I won't use CVS. + is a good indicator of a live transaction. Delete them if they + are old. - Some developers have the feeling that three-way merging doesn't - work. They don't trust the way the "update" command - automatically merges committed changes from the Repository into - the working file. - Experience has shown that most merges are utterly painless and - most of the rest are easily resolved. The few conflicts that - cause headaches are nearly all due to poor communication between - developers, a problem no source control system can obliterate. - - Some developers were troubled in the past by flaky Unix software. - I can't say that everything is perfect, but the tools CVS depends - on (RCS and diff, mainly) are fairly solid nowadays. They work. - - Since it does seem to work for most of us, the algorithm is - unlikely to change soon. Why not test it on a couple trouble - spots and if it works for you, use it for a while? Then you can - make an informed decision. - - - 4E.5 Explain: "co error, line 2: Missing access list" + 4E.3 Explain: "co error, line 2: Missing access list" This is an error message from RCS Version 3 when it tries to read a file created by a later version of RCS. - You should upgrade to the latest version of RCS, which is Version - 5.6.0.1 as I write this. + HP decided to "standardize" on an ancient version of RCS some time + ago. You can't use it for CVS. See 4H.6. + + Since the error comes from having a later version of RCS than HP + supports, you probably did install the later version but must have + recently changed your $PATH or installed the HP package that has + RCS in it. + You should either reconfigure CVS to use absolute pathnames to the + proper versions of the RCS programs that CVS uses, or change your + PATH to look there first. If you haven't installed the latest + version of RCS, you should upgrade. See 1B.4 -+4E.6 Explain: "error: RCS file name `xyz .c' contains white space" - RCS 5.6 doesn't allow white space in filenames. Apparently this + 4E.4 Explain: "error: RCS file name `xyz .c' contains white space" + + RCS 5.6 doesn't allow white space in filenames. Apparently this restriction will be removed in RCS 5.7, but CVS may still require that filenames have no white space in them. -+4E.7 Explain: cvs checkout: warning: <X> is not (any longer) pertinent + 4E.5 Explain: cvs checkout: warning: <X> is not (any longer) pertinent - This message occurs in two instances: + This message occurs in three instances: 1. When there is an entry in the ./CVS/Entries for file <X> and there is no RCS file in the Repository to back it up. @@ -6711,7 +8136,7 @@ Some are therefore dangerous. Avoid anything you don't fully understand. above) exists, but some part of the rest of it does not. The checkout command checks the modules file first for the - whole path, then for a prefix of the path as a module name. If + whole path, then for a prefix of the path as a module name. If it doesn't find *any* portion of your path in the modules file, it says: @@ -6727,15 +8152,50 @@ Some are therefore dangerous. Avoid anything you don't fully understand. Check the permissions on the files involved. + + 4E.6 Why did a Repository file change from <file>,v to ,<file>,? + + This is an RCS problem, since the ,<file>, syntax for file names + is used by RCS and not CVS. + + RCS constructs a new <file>,v in a temporary file named ,<file>, + (which doubles as a lock file) then renames it to <file>,v when it + is done. The only way this is reliable is if your system's + version of rename(2) is an atomic, as required by POSIX. + + If your system has a non-atomic (and therefore non-POSIX) + rename(2) system call, RCS runs uses an internal version of this + algorithm to approximate the atomic rename: + + rm <file>,v; ln ,<file>, <file>,v; rm ,<file>, + + If the system crashes, or you lose your NFS connection between the + first "rm", but before the "ln", you can be left only with the + ,<file>, file. If the crash or network failure occurs between the + "ln" and the final "rm", you could be left with a pair of linked + names. + + Recovery: + - If only the ,<file>, exists, rename it to <file>,v. + + - If both ,<file>, and <file>,v exist and are linked, remove the + ,<file>, file. + + - If both ,<file>, and <file>,v exist and are separate files, look + at the dates, "diff" them and make your best guess. This sounds + like the remnants of two separate events. + + + ---------------- -- Section 4F -- Related Software ---------------- **** Questions: -+4F.1 How do I use CVS under Emacs? Is there an Emacs cvs-mode? -+4F.2 What is GIC (Graphical Interface to CVS)? -+4F.3 What is CAVEMAN? + 4F.1 How do I use CVS under Emacs? Is there an Emacs cvs-mode? + 4F.2 What is GIC (Graphical Interface to CVS)? + 4F.3 What is CAVEMAN? **** Answers: @@ -6744,22 +8204,21 @@ This section covers a small handful of subsystems that connect to CVS in some way. Most are "front ends" in that they offer a different user interface to CVS, but use CVS to perform the normal tasks. - NOTE: The short summaries below combine details culled from public + NOTE: The short summaries below combine details culled from public announcements of the listed software with the personal opinions of the author of the FAQ entry. + 4F.1 How do I use CVS under Emacs? Is there an Emacs cvs-mode? -+4F.1 How do I use CVS under Emacs? Is there an Emacs cvs-mode? - - The pcl-cvs package distributed with CVS 1.3 is an emacs package - that helps with the update/commit process. When you are ready to - update, you use the 'cvs-update' command within emacs. This + The pcl-cvs package distributed with CVS is an emacs package that + helps with the update/commit process. When you are ready to + update, you use the 'cvs-update' command within emacs. This executes "update" and fills a cvs-mode buffer with a line for each file that changed. The most helpful features are: descriptive - words for what happened (i.e. Merged or Conflict rather than 'U'), - single keys bound to diffs and commits, and the ability to mark - arbitrary groups of files, possibly from different directories, - for commit as a whole. + words for what happened (i.e. Merged or Conflict rather than 'U' + or 'C'), single keys bound to diffs and commits, and the ability + to mark arbitrary groups of files, possibly from different + directories, for commit as a whole. All the developers in my group that use emacs find pcl-cvs a much friendlier and more helpful way to update/commit than raw cvs. @@ -6767,26 +8226,34 @@ interface to CVS, but use CVS to perform the normal tasks. Contributed by Jeffrey M Loomis -+4F.2 What is GIC (Graphical Interface to CVS)? + 4F.2 What is GIC (Graphical Interface to CVS)? - GIC is a window interface to CVS written in Tcl/Tk, which attempts - to hide the normal CVS command line options from novice users. + GIC provides a graphical user interface to the Concurrent Version + System (CVS), a powerful revision control system. GIC is + implemented in the Tcl/Tk programming language and is intended to + augment the sometimes cumbersome CVS command line interface. + Novices should find GIC to be much easier to learn than the CVS + command line. - GIC works only in a single directory at a time, but it offers most - of the CVS commands you would normally use. - - GIC can be obtained by anonymous ftp to - - ftp.cpsc.ucalgary.ca:/pub/marwood/gic-1.0b5.tar.Z + While GIC is easy to use, it does not contain any documentation on + CVS. Users of GIC must first learn the concepts of CVS such as + modules and merging, as well as the simple functions, such as + committing and updating. The CVS manual page and the README file + are good places to look. contact David Marwood marwood@cpsc.ucalgary.ca - [[Does someone want to try to describe this better?]] + [Extracted from an announcement by David Marwood.] + + GIC can be obtained by anonymous ftp to (on the date of this FAQ) + + ftp.cpsc.ucalgary.ca:/pub/users/marwood/gic-1.1.tar.Z + ftp.cpsc.ucalgary.ca:/pub/users/marwood/gic-1.2b1.tar.Z -+4F.3 What is CAVEMAN? + 4F.3 What is CAVEMAN? CAVEMAN is a front end to CVS written in PERL providing a collection of features desired by the site where it was developed. @@ -6799,31 +8266,497 @@ interface to CVS, but use CVS to perform the normal tasks. - All sorts of error messages. - Many changes to the semantics of commands. - It is available via anonymous ftp on llnl.gov [128.115.18.253] as - gnu/caveman_vX.Y.Z.tar.gz (The numbers X, Y, & Z vary with time.) + It is available via anonymous ftp on ftp.llnl.gov [128.115.54.18] + in gnu/caveman_vX.Y.Z.tar.gz (The numbers X, Y, & Z vary.) - contact - Kathleen Dyer kdyer@llnl.gov + contact + Kathleen Dyer kdyer@llnl.gov (510)423-6803 (510)423-5112 FAX + [[Does someone want to elaborate?]] + + +---------------- +-- Section 4G -- Engineering +---------------- - [[Does someone want to try to describe this better?]] + **** Questions: + 4G.1 Where can I find out about Software Engineering? + 4G.2 How do I flexibly arrange the modules file to describe my sources? + 4G.3 Can I have multiple source repositories, one for each project? + 4G.4 Who should administer the Repository and manage the modules file? + 4G.5 Isn't disk space a big factor? CVS copies files out of the + Repository, duplicating everything. ----------------- --- Section 4G -- Other Systems + **** Answers: + +This section is really beyond the scope of CVS, but so many people ask +questions about how to do Software Configuration and Engineering that I +thought I'd try to include some information. If you have any +improvements, references or ideas, speak up. + + + + 4G.1 Where can I find out about Software Engineering? + + A couple different people suggested this book: + + Software Configuration Management: Coordination for Team + Productivity; Wayne A. Babich; Addison Wesley; 1986; + ISBN 0-201-10161-0 + + + A number of others suggested Appendix B of the book "Decline and + Fall of the American Programmer" by Ed Yourdon, called "The + Programmer's Bookshelf". It list 87 books you are expected to + have read. Since they publish many of the books, Prentice-Hall + distributes this list as "Prentice Hall Professional Technical + reference PTR-125-AA3. + + One interesting item from the Yourdon book: The total number + of professional computer books sold is less than the number + of programmers currently in the United States. It wasn't clear + from the book whether this meant "per year" or not, but it is + still frightening. + + + 4G.2 How do I flexibly arrange the modules file to describe my sources? + + An equivalent question might be, "How do I structure my sources?" + This can be a difficult question especially in the areas that are + more political than technical. + + Generally you want to think about which pieces of your system need + to be checked out together, built as one system or tagged as a + consistent whole. You should certainly create module names that + correspond to complete, buildable collections that you would tag + and release as one "product". It is also convenient to create + module names for small sections of the Repository containing + files that will all be worked on at the same time by the same + person or group. + + Once you have defined the structure of your work, you can usually + see how to lay it out in a Repository. After that the modules + file is easy. You set up module names and aliases to match what + you need to check out by name. If you like relative directories, + it is possible, but not recommended, to work completely without a + modules file. See 1D.11 and 2C.7 for some info about the modules + file. + + Here are a few types of modules. You should experiment to see + what kind of structure each of these produces. They all have + different uses. + + 1. Connected projects in one group with two separate helper + directories. The helper directories can contain build tools, + header files, libraries, or whatever you like. + + These are all aliases that checkout relative pathnames. The + equivalent results could be produced by placing the selected + relative pathnames on the "cvs checkout" command line. + + pr1 -a P1 HELPERS + pr2 -a P2 HELPERS + pr3 -a P3 HELPERS + pr12 -a P1 P2 HELPERS + pr13 -a P1 P3 HELPERS + pr23 -a P2 P3 HELPERS + + P1 -a group1/proj1 + P2 -a group1/proj2 + P3 -a group1/proj3 + HELPERS -a group1/helper1 group1/helper2 MAKEFILE + MAKEFILE -a group1/Makefile + + Actual Repository directory structure: (from $CVSROOT down) + + group1/ + Makefile + The top level Makefile. + helper1/ + helper2/ + Helper files and dirs + proj1/ + Files and dirs + proj2/ + Files and dirs + proj3/ + Files and dirs + + "checkout group1" produces a duplicate of the above. + "checkout projX" produces all but "projY" and "projZ". + "checkout projXY" produces all but "projZ". + + + 2. Here is the exact same set of module names describing the same + Repository layout using module names (and aliases containing + module names) instead of merely aliases for relative pathnames. + + There is one difference in the result. The name of the top + level directory in the checked out working tree will match the + "module" name (e.g. pr1) instead of always being "group1" as it + was in the first example above. + + pr1 group1 proj1 &HELPERS + pr2 group1 proj2 &HELPERS + pr3 group1 proj3 &HELPERS + pr12 group1 proj1 proj2 &HELPERS + pr13 group1 proj1 proj3 &HELPERS + pr23 group1 proj2 proj3 &HELPERS + + HELPERS -a helper1 helper2 group1-Makefile + helper1 group1/helper1 + helper2 group1/helper2 + group1-Makefile -d . group1 Makefile + + The above line (with the -d in it) says that when the + module named "group1-Makefile" is checked out, the file + named Makefile file will be found in a directory named + $CVSROOT/group1 and will be checked out into a directory + named '.', which obviously already exists. + + The & references say to interpret those pathnames relative + to the directory where the whole module is stored. For + the "pr1" module, that directory is "group1", so the + &HELPERS reference winds up placing Makefile in '.' + relative to "group1". + + + 3. A short one containing the basic "module" actions: + + m1 head/path file1 dir2 file3 dir4 file5 + + When checked out, a directory named "m1" appears in your + current directory. Elements named file1, dir2, file3, + dir4, and file5 appear in it. They were originally taken + as relative paths from $CVSROOT/head/path. + + + 4. Here's another way to construct a working directory out of + pieces of the Repository: + + projX projX Makefile &projX_inc &projX_src &projX_doc + + # The first line selects a single file within projX, plus + # the contents of three other modules. Those three other + # modules rename their directories. + + projX_inc -d include projX/inc + projX_src -d source projX/src + projX_doc -d documentation projX/doc + + + 5. A Unix tree. This is similar to what CVS was developed for and + the way I have used it for years. + + # Top level + unix unix + u_bin unix/bin + u_etc unix/etc + u_man unix/man + usr-bin unix/usr.bin + + # Subdirs of top level dirs. (tiny subset) + ls unix/bin/ls + fsck unix/etc/fsck + man8 unix/man/man8 + + # Programs without subdirs. (tiny subset) + cat unix/bin Makefile cat.c + uniq unix/usr.bin Makefile uniq.c + + # /usr/local/src + localsrc localsrc + gnu localsrc/gnu + public localsrc/public + X11 localsrc/X11 + + # GNU and PD tools + cvs localsrc/gnu/cvs + emacs localsrc/gnu/emacs + rcs localsrc/gnu/rcs + btoa localsrc/public/btoa + tcsh localsrc/public/tcsh + + # X11 related items. + tvtwm localsrc/X11/contrib/tvtwm + + "unix" was checked out and built from the top down, using a set + of Makefiles that knew about the whole structure. "localsrc" + was kept checked out in /usr/local/src. + + At any time I could run "checkout ls" or "checkout cat" and get + a simple directory with only that tool in it, plus a subset + Makefile that knew how to build that tool against the installed + (or alternate, via environment variables) headers and libraries. + + I found it very handy to be able to run "ls" and see the three + tools I was porting that week. + + + 4G.3 Can I have multiple source repositories, one for each project? + + Yes, you can have as many Repositories as you like. But each + Repository must be managed separately, creating additional work. + + Question 4A.1 provides a short description of setting up a + single Repository. A few additional considerations: + + 1. It is a good idea to start by creating a single Repository and + split it up (or create additional Repositories) only if you + believe it is really necessary. I would only create a new + Repository if the data is completely disconnected from the rest + of the main Repository. + + 2. If there is a lot of overlap among the developers working on + the collections of files you want to place in different + Repositories, or if there is any connection between those + collections, I would go out of my way to create a single + Repository. It is much easier to manage. + + 3. Disk space should not be a factor since you can build up a + Repository using symbolic links and/or remote mounts. + + 4. Each Repository is completely distinct. You can't check out + modules from different Repositories at the same time. A better + way of looking at it is that if you *can* check out two modules + or directories with a single "checkout" command (without + contortions or explicit absolute pathnames), then they are in + the same Repository. + + 5. To "checkout" modules from multiple Repositories, you must use + the "cvs -d" option on all CVS commands or alter your $CVSROOT + variable when you change focus to another Repository. If you + work with multiple Repositories, it is a good idea to configure + CVS to use absolute pathnames in the ./CVS/Repository file, + since most commands (other than "checkout") will use that file + rather than $CVSROOT. + + 6. If you configure CVS to use relative pathnames in your + ./CVS/Repository files, you must always be careful to set your + $CVSROOT properly or you will get unexpected results. + + If you have two modules or directories by the same name at the + same relative path inside two different Repositories, you are + asking for disaster. You could unexpectedly update a directory + with completely unrelated files. This is not a fanciful + example -- a Repository is occasionally duplicated for release + purposes in which case *all* the paths in the two Repositories + are the same. + + + 4G.4 Who should administer the Repository and manage the modules file? + + This is a "management style" question. In large or traditional + groups, the CVS procedures are warped to conform to local + conventions. In small groups, in groups with strong personalities + or on new projects the choice of source control procedures can + help create some of the working environment. Here is a taxonomy + of environments I have worked in or helped set up: + + Situation 1. + + A small number of competent developers working on a medium + size project. We all got along and we all respected each + other (at least technically). Anyone edited anything. + + Modules and Repository admin was mostly left to me. I never + found a problem in minor changes made by anyone else. + + + Situation 2. + + A large number of experienced developers sprinkled with + wackos. Many of the developers didn't want to deal with any + kind of source control. They wanted a full-service source + control system that caused them zero thought. + + I learned "big stick" diplomacy here. There was a small + number of "designated" (by me) people who were allowed to do + *anything* other than "update" and "commit". Even "checkouts" + were controlled. This is where I found "history" and + "release" the most useful. + + Situation 3. + + A small number of developers who wanted me to "help", but who + didn't want to deal with anything other than their favorite + algorithms. + + I didn't have the time to baby-sit this group, so I designated + one of them to be my official contact and made him do it all. + He felt sullied by the requirement to pay attention to + anything other than his pet coding projects, but enjoyed the + "status" of being the only one who could touch the control + files without my kicking the chair out from under him. + + Situation 4. + + A huge number of developers of covering the whole spectrum of + competence and experience split into 20 groups, none of which + cooperated with the others, working on 57 different projects, + most of which didn't inter-operate. + + Managing it in any coherent way was not my responsibility (and + beyond my tolerance for chaos). Too many people. So I + privately designated a person in each group to be the contact + and kept watch on the Repository activity. When something + went wrong, I notified the contact for the group and told him + what was happening and *he* kept his troops in line. They + were tougher with their own group that I would have been. + + Eventually only a few people were willing to touch the control + files, since they were flamed from all directions if they + screwed up. + + Situation 5. + + In a medium group of really *serious*, and seriously + overworked, people, someone else was designated the "master". + I convinced the master I knew what I was doing and went on my + way. + + No one else in the world was allowed to touch anything. + + Situation 6. + + In a large amorphous group of beginners, experts and clowns, + over whom no one had official control, I was forced to employ + a group of relative beginners (who became experts rather + quickly) to police the world. The ultimate in locking the + barn after the horse was stolen, we kept Chaos from destroying + us only by use of superior firepower. + + + + My choice, if allowed, is to let anyone touch anything. I keep + backups of important items and let people know individually + whether I want them to touch things or not. If someone on my "no + touch" list touches and succeeds, they are allowed more slack. If + they screw up after being warned, their screwup becomes public. + After a few months, I usually have no trouble keeping the world + running smoothly, at least from my (and CVS's) perspective. + + + 4G.5 Isn't disk space a big factor? CVS copies files out of the + Repository, duplicating everything. + + Everyone knows that disk space is getting cheaper. How do we + reconcile this with the equally well-known problem that *all* disk + is *always* filled up? + + In my opinion, the main reason disk space will never be an + unlimited resource is that it is the major variable in + organizational time/space tradeoffs. It isn't a problem of waste + or an aspect of Murphy's law, as some claim it is, but rather a + direct consequence of good management. Disk space is, and will + always be, a limited resource. + + First, the cost of *deploying* that disk is not dropping as fast + as the cost of the storage medium. The cost of machines to hold + the disks and the networks to connect them are dropping more + slowly than disk media. And the cost of the human time necessary + to manage the machines, networks, disks, and the developers using + them, is not dropping at all. The cost of human time continues to + rise. + + If management decides that expensive human time can be saved by + using all that new disk space to keep the last three releases + online, then that's what it will be used for. If each release + takes up a Gigabyte and you support 30 platforms, a simple + time-saving suggestion has just grabbed 100 Gigabytes of disk + space. And we've ignored the potential disk storage needed to + support "better Customer Service", another management refrain. + + Even at 30 cents per Megabyte (next year's price), you've just + used up $30,000 of disk space. And that doesn't count the + computers, tape drives and humans necessary to maintain and deploy + all of it. Spending money to save time has its own overhead, too. + + + Binaries are getting bigger. Graphics and data collection devices + can eat up any amount of disk. There are more tools available, + more libraries, more raw data than you can ever store. My home + computer has a Gigabyte of disk on it. It could easily handle 30. + + The "economy" of disk storage media will never remove the need to + manage disk space. + + + So, here's an un-reviewed suggestion originally from Graydon Dodson + <grdodson@lexmark.com>, which I've altered and edited heavily. + + - Keep a directory where the whole tree is checked out. (It might + be built and tested once in a while to make sure it is worth + linking to, but that doesn't affect the source control aspect of + this procedure). Let's call it /master/build. + + - Write a tool that creates a tree of directories (like the X11 + "lndir" command) filled with links to the checked out files in + the /master/build tree. + + This tool should also provide real copies of, not symlinks to, + all the files within the CVS administrative directories. + + - You could also provide a way for the tool to take a list of + whole directories that you will never change, for which it would + create a single symlink to the directory and not a subtree of + symlinks to files. Or you could rm -r pieces of the resulting + working directory yourself and replace it with links. + + - If you want to edit a file, you have to grab a real copy and + keep it until your revision shows up in the /master/build tree. + I'd create a script to do this: cvsgrab <file> + + #!/bin/csh -f + set f = $1 + if (! -l $f) then + echo "file $f is not a symlink" + exit 1 + endif + rm $f + set rev = `grep "^/$f/" CVS/Entries | awk -F/ '{print $3}'` + cvs update -p -r $rev $f > $f + + You can't do a plain "cvs update" since that would grab newer + revisions from the Repository, not the revision you wanted to + start with. After the file is no longer a symlink, you can work + normally. You'll have to run "update" before "commit" anyway if + there are newer revisions. + + - Presumably there would also be a tool to traverse the link tree + and revert it to links if there are no modified files and/or if + all the real files match the revision of the /master/build tree. + + - To avoid confusing CVS when the /master/build revisions are + updated but your CVS/Entries files is not, CVS would have to + change to handle symlinks. It currently causes problems with + this scenario: + + 1. ./<file> is a symlink. + 2. ./CVS/Entries says you are revision 1.2. + 3. The corresponding CVS/Entries file in /master/build + says the latest revision is 1.3. + 4. cvs update <file> shows a 'C' conflict flag. + + +---------------- +-- Section 4H -- Other Systems ---------------- **** Questions: - 4G.1 I use a NeXT. Is there anything I need to know? - 4G.2 I use OS/2. Is there anything I need to know? - 4G.3 I use SCO Unix. Is there anything I need to know? - 4G.4 I use AIX. Is there anything I need to know? -=4G.5 I use IRIX. Is there anything I need to know? -=4G.6 I use an HP system. Is there anything I need to know? + 4H.1 I use a NeXT. Is there anything I need to know? + 4H.2 I use OS/2 and/or DOS. Is there anything I need to know? + 4H.3 I use SCO Unix. Is there anything I need to know? + 4H.4 I use AIX. Is there anything I need to know? + 4H.5 I use IRIX. Is there anything I need to know? + 4H.6 I use an HP system. Is there anything I need to know? + 4H.7 I use AFS. Is there anything I need to know? + 4H.8 I use A/UX. Is there anything I need to know? **** Answers: @@ -6831,44 +8764,101 @@ interface to CVS, but use CVS to perform the normal tasks. Out of the box, CVS works on most varieties of Unix. Some near-Unix systems have a few problems and non-Unix systems have a *lot* of problems. - 4G.1 I use a NeXT. Is there anything I need to know? + 4H.1 I use a NeXT. Is there anything I need to know? + + NeXTSTEP 3.0's Interface Builder uses "nib" directories, rather + than the files used in previous revisions. It removes files it + doesn't recognize, making it impossible to place such a directory + under CVS -- the CVS admin directory will be removed. + + Some time ago, <Bob_Vadnais@pdh.com> posted a palette named + CVSPalette that claimed to resolve this problem. It was intended + to preserve the CVS administrative directories within nib + documents (directories) that Interface Builder usually removes. + + CVSPalette is no longer in its announced place: + + ftp.cs.orst.edu:/pub/next/submissions + + though I did find two other interesting files on ftp.cs.orst.edu: + + /software/NeXT/sources/tools/cvs-next-2_1_1.tar.Z + + which is a port of CVS 1.3 (along with RCS and diff) and: - Under NeXTSTEP 2.2, the tmpnam() function always returns the - same filename, which breaks "cvs patch". Apparently the - "mktemp()" function works OK, but you'll have to hack it up to - build something that acts like "tmpnam()". + /software/NeXT/sources/programming/cvs.postamble-2.4.gz - NeXTSTEP 3.0's Interface Builder uses "nib" directories, - rather than files in previous revisions. It removes files it - doesn't recognize, making it impossible to place such a - directory under CVS -- the CVS admin directory will be removed. + which appears to be a set of wrappers for CVS commands that claim + to allow you to use CVS effectively (and without need for the + "command line") on a NeXT machine. + + + [[Anyone know the truth about CVS and NeXT?]] + + + 4H.2 I use OS/2 and/or DOS. Is there anything I need to know? + + You can share RCS files between Unix and DOS while avoiding the + MS-DOS file name limits by setting your RCSINIT environment + variable to '-x/,v'. New RCS files will be created without the + standard ",v" suffix, though files ending in ",v" will still be + found if there is no matching file in the same directory without + the ",v". + + Erik van Linstee <linstee@dutecaj.et.tudelft.nl> offers an + OS/2 and a DOS port of CVS 1.3 in: + + ftp.informatik.tu-muenchen.de:/pub/comp/os/os2/gnu/devtools + or + ftp.rrzn.uni-hannover.de:/pub/os2-local - [[Anything else?]] + The files are named: + cvs13p?[bs].zip - 4G.2 I use OS/2. Is there anything I need to know? + Where the ? stands for the patch level (currently 8) and the b is + for the binaries, the s for the sources. - [[Well?]] + There are three binaries. An OS/2 only one (32-bit), a DOS only one + (16-bit) and an EMX one that runs on both (32-bit). + There are many differences between the Unix and the DOS versions + of CVS. Read the material that comes with the DOS version before + using it. - 4G.3 I use SCO Unix. Is there anything I need to know? + [[Updates?]]. - [[Well?]] + 4H.3 I use SCO Unix. Is there anything I need to know? - 4G.4 I use AIX. Is there anything I need to know? + On SCO/UNIX 3.2 V2.0 POSIX signals don't work. Unfortunately the + configure program detects POSIXness and configures in the use of + POSIX signals. Workaround : Edit out the check for POSIXness in + the configure script. [[You could also remove all occurrences of + "-DPOSIX=1" from the Makefiles after configure is run. -dgg-]] - [[Well?]] + SCO/UNIX doesn't understand #!/<some shell> syntax. This breaks + the use of log.pl as it gets invoked by /bin/sh instead of + !#/usr/local/bin/perl. WorkAround : edit log.pl and change it into + a shell script which invokes perl with log.perl (renamed from + log.pl) as input. + Contributed by Joe Drumgoole -=4G.5 I use IRIX. Is there anything I need to know? + 4H.4 I use AIX. Is there anything I need to know? + + The only report on AIX claims to have no trouble using it in + concert with SunOS and IRIX platforms. + + + 4H.5 I use IRIX. Is there anything I need to know? If you see "uid" numbers where you would expect user names, try - adding -lsun to the link line. Without it CVS is unable to + adding -lsun to the link line. Without it CVS is unable to retrieve "passwd" data through NIS. -=4G.6 I use an HP system. Is there anything I need to know? + 4H.6 I use an HP system. Is there anything I need to know? HP distributes RCS version 3 (a circa 1983 release!) with HP-UX. CVS does not work with RCS version 3; it requires RCS version 4 @@ -6876,13 +8866,13 @@ systems have a few problems and non-Unix systems have a *lot* of problems. and install it somewhere. HP-UX 8.07 has a serious bug with the mmap system call and NFS - files; the bug can crash the operating system. Make sure that + files; the bug can crash the operating system. Make sure that you configure RCS to avoid mmap by setting has_mmap to 0 in RCS's conf.h. This bug is fixed in HP-UX 9. Contributed by Paul Eggert - If using the setgid() trick described in 4D.16, you will have to + If using the setgid() trick described in 4D.13, you will have to create an entry in the /etc/privgroup file to give the group assigned to the cvs executable setgid permission (see setprivgrp(1m)). Additionally, if you are restricting "read" @@ -6893,6 +8883,27 @@ systems have a few problems and non-Unix systems have a *lot* of problems. Contributed by Dale Woolridge + + 4H.7 I use AFS. Is there anything I need to know? + + There is a problem with the way CVS performs its locking when the + files are within AFS. When your current PTS id != your uid, the + locks are not deleted. The stat() system call returns the PTS id + of the owner. If that id != your uid, CVS assumes you did not lock + it, and leaves the lock files alone. The next time you try to use + it, it complains that someone has the repository locked. + + Contributed by Michael Ganzberger + + [[This was against CVS 1.3. Is it still in CVS 1.4?]] + + + 4H.8 I use A/UX. Is there anything I need to know? + + [[??]] + + + ============================================= == Section 5 ==== Past & Future ==== @@ -6904,40 +8915,59 @@ systems have a few problems and non-Unix systems have a *lot* of problems. **** Questions: - 5A.1 Who wrote CVS? -=5A.2 You didn't write all of this FAQ, did you? +=5A.1 Who wrote CVS? + 5A.2 You didn't write all of this FAQ, did you? **** Answers: - 5A.1 Who wrote CVS? +=5A.1 Who wrote CVS? Brian Berliner <berliner@sun.com> converted a collection of scripts written by Dick Grune <dick@cs.vu.nl> into a C program, then added all sorts of features. He continues to maintain CVS. Jeff Polk <polk@bsdi.com> wrote much of the code added between - revisions 1.2 and 1.3. Many others were involved at some level. + revisions 1.2 and 1.3. Many others were involved at some level. + + david d zuhn <zoo@armadillo.com> fixed a number of bugs, added + some of the new features, reworked the whole thing to be more + portable, and provided much of the energy to push CVS 1.4 out + the door. + + Jim Kingdon implemented CVS 1.5's remote repository access + features, fixed many bugs, and managed the release of version 1.5. Take a look at the README and the ChangeLog files in the CVS - sources for more details. + sources for more contributors. -=5A.2 You didn't write all of this FAQ, did you? + 5A.2 You didn't write all of this FAQ, did you? In the original hunt for questions to answer (performed in Jan/Feb, 1993), I polled hundreds of people and I rephrased all - sorts of text found on the net. Because there are so many posers - of questions, I will list only those who contribute answers or - help significantly with the content and structure of this - document. + sorts of text found on the net. Between 2/93 and 10/93, I + released about 20 versions, with corrections and additions from + the info-cvs mailing list and private correspondence. + + Between 10/93 and 10/94 I extracted frequently asked questions + from the 1200 mail messages to the info-cvs mailing list, + turned them into focused questions and tried to answer them. - Unless a name is included in the answer itself, I didn't use - anyone else's answers verbatim. On the other hand, I did use - ideas and information provided by many. The people whose email - postings have added to this document or who have added to my - understanding are: + 93/02/?? ~4000 lines + 93/06/?? ~5000 lines + 93/10/23 7839 lines 278K + 94/10/29 9856 lines 360K + 95/05/09 9981 lines 365K + + Because there are so many posers of questions, I will list only + those who contribute answers or help significantly with the + content and structure of this document. + + If I used someone else's text verbatim, I mentioned it in the + given answer. The people whose email postings have added to this + document or who have added to my understanding are: Brian Berliner <berliner@sun.com>, CVS maintainer. Paul Eggert <eggert@twinsun.com>, RCS maintainer. @@ -6952,21 +8982,28 @@ systems have a few problems and non-Unix systems have a *lot* of problems. Additional contributors, who have sent me ideas, text, corrections and support include (in alphabetical order): + Per Abrahamsen <amanda@iesd.auc.dk> Donald Amby <amby@mixcom.mixcom.com> + Mark D Baushke <mdb@cisco.com> + Jim Blandy <jimb@cyclic.com> Tom Cunningham <tomc@bouwsma,sps.mot.com> + Graydon Dodson <grdodson@lexmark.com> + Joe Drumgoole <joed@splatter.demon.co.uk> Don Dwiggins <dwig@markv.com> + Bryant Eastham <bryant@ced.utah.edu> Dan Franklin <dan@diamond.bbn.com> + Michael Ganzberger <ganzbergermd@ES.net> + Steve Harris <vsh%etnibsd@uunet.uu.net> + Erik van Linstee <linstee@dutecaj.et.tudelft.nl> Jeffrey M Loomis <jml@world.std.com> - Barry Margolin <barmar@think.com> + Barry Margolin <barmar@near.net> Mark K. Mellis <mkm@ncd.com> Chris Moore <Chris.Moore@src.bae.co.uk> - Gary Oberbrunner <garyo@think.com> + Gary Oberbrunner <garyo@avs.com> Steve Turner <stevet@carrier.sps.mot.com> Dave Wolfe <dwolfe@pffft.sps.mot.com> Dale Woolridge <dwoolridge@cid.aes.doe.ca> - Plus a myriad Thinking Machines people who posed hundreds of - questions. Please send corrections. If I forgot you, remind me and I'll add @@ -6984,46 +9021,39 @@ See the Development section later for stuff being worked on. **** Questions: 5B.1 Why can't CVS handle deletion of directories? -=5B.2 Why can't CVS handle the moving of sources from one place in the + 5B.2 Why can't CVS handle the moving of sources from one place in the directory hierarchy to another? -=5B.3 Why does "checkout" recurse indefinitely if an alias contains - its own name? - 5B.4 When I typed "cvs update -D <date>", why did it check out all - sorts of ancient files from the Attic? Shouldn't it just create + 5B.3 When I typed "cvs update -D <date>", why did it check out all + sorts of ancient files from the Attic? Shouldn't it just create the set of files and revisions that existed at that date? - 5B.5 When I typed "cvs update -D <date>" in my branch, why did it + 5B.4 When I typed "cvs update -D <date>" in my branch, why did it screw up all my files? - 5B.6 When I executed "checkout" into an existing directory I got "No - such file or directory" errors. Why? - 5B.7 Why does "update" send all output to the terminal after 26 files + 5B.5 When I executed "checkout" into an existing directory I got "No + such file or directory" errors. Why? + 5B.6 Why does "update" send all output to the terminal after 26 files have been updated? -+5B.8 Why doesn't the "-I !" option work in update and import? + 5B.7 Why does the merge occasionally resurrect lines of code? + 5B.8 Why does the merge fail when my "rcsmerge" program is + configured to use GNU diff version 2.1 or later? **** Answers: 5B.1 Why can't CVS handle deletion of directories? - An oversight, probably. [[Fixed in a future release?]] + An oversight, probably. [[Fixed in a future release?]] -=5B.2 Why can't CVS handle the moving of sources from one place in the + 5B.2 Why can't CVS handle the moving of sources from one place in the directory hierarchy to another? A "renaming database" has been proposed to track the history of pathname changes in the Repository. A general solution is a - difficult problem. See 4B.9 and 2C.4. - + difficult problem. See 4B.8. -=5B.3 Why does "checkout" recurse indefinitely if an alias contains - its own name? - A bug in the handling of aliases. [[I'll remove this one when - the bug is fixed.]] - - - 5B.4 When I typed "cvs update -D <date>", why did it check out all - sorts of ancient files from the Attic? Shouldn't it just create + 5B.3 When I typed "cvs update -D <date>", why did it check out all + sorts of ancient files from the Attic? Shouldn't it just create the set of files and revisions that existed at that date? This seems to be a bug, but is really the lack of any obvious @@ -7051,8 +9081,15 @@ See the Development section later for stuff being worked on. is better to use "-r <tag>, or even "-r HEAD" than to use a date spec. + If you must use "-D <date>", then you should either archive and + delete Attic files (losing some past history) or construct your + Makefiles to work with an explicit list of files and let the old + source files stay in the working directory. The contents of the + revision-controlled Makefile can then be considered to contain + deletion "information". - 5B.5 When I typed "cvs update -D <date>" in my branch, why did it + + 5B.4 When I typed "cvs update -D <date>" in my branch, why did it screw up all my files? Currently, the internal routine ("version_ts") that looks up @@ -7061,9 +9098,14 @@ See the Development section later for stuff being worked on. date is specified, it should not override a branch tag, but it does. + In CVS 1.3, the documented "-D <branch_tag>:<date>" syntax only + works with the Main Branch and the Vendor Branch. + + [[Is this fixed in CVS 1.4? This is one item I didn't check.]] - 5B.6 When I executed "checkout" into an existing directory I got "No - such file or directory" errors. Why? + + 5B.5 When I executed "checkout" into an existing directory I got "No + such file or directory" errors. Why? Though the man page says that "checkout" turns into an "update -d" in directories that already exist, it is referring @@ -7076,7 +9118,7 @@ See the Development section later for stuff being worked on. non-CVS directories. - 5B.7 Why does "update" send all output to the terminal after 26 files + 5B.6 Why does "update" send all output to the terminal after 26 files have been updated? CVS uses the "tmpnam()" function to generate temporary file names. @@ -7104,52 +9146,67 @@ See the Development section later for stuff being worked on. workaround is to provide a "tmpnam()" that doesn't have a limit on the number of calls to it. -+5B.8 Why doesn't the "-I !" option work in update and import? - A bug. See the patch named "unoff/import_ignore" in the CVS FTP - archive + 5B.7 Why does the merge occasionally resurrect lines of code? + The diff3 program provided by GNU diff version 1.15 has a bug + that occasionally causes text to come back from the dead. - [[Section 5B needs more, but should probably wait until the - patch release comes out. Then we can document them for real and - provide pointers to patches in the FTP area.]] + This is an old problem which you can avoid by upgrading to the + latest GNU "diffutils" package. If you were using GNU diff + version 1.15 and plan to upgrade to the latest GNU diff program, + see the next question. ----------------- --- Section 5C -- Development ----------------- + 5B.8 Why does the merge fail when my "rcsmerge" program is + configured to use GNU diff version 2.1 or later? + + A change in the overlap format was introduced in GNU diff3 + between versions 2.0 and 2.1 that causes RCS versions before + 5.6.0.1 to fail during a merge. + + To get consistent rcsmerge behavior, you have four choices: + + 1. Go back to using GNU diff 1.15 or 2.0 with RCS versions 5.5 or + 5.6. If you want to use GNU diff 2.1 or later, you'll have to + pick one of the other three choices in this list. + + 2. Grab RCS version 5.6.0.1 from an FSF archive and set the + DIFF3_A macro to '1' as it tells you to in the Makefile: + + #define DIFF3_A 1 + + 3. Patch the RCS 5.6 source. Change line 84 in "merger.c" from: - I hope to record three types of information here: + DIFF3, "-am", "-L", label[0], "-L", label[1], + to + DIFF3, "-amE", "-L", label[0], "-L", "", "-L", label[1], - 1. Plans (with the developer's name attached) for fixing larger - bugs. (Smaller bugs should just show up, with a patch or a - reference to a patch stored in the FTP archive, in the - "Bugs" section above.) + 4. Wait both for RCS version 5.7 to be released and for a new + version of CVS that can deal with it. - 2. Plans for new development, with the developer's name attached. - (If the developer is particularly gonzo, it might also show a - completion date.) - 3. Requests for ideas and code to fix unresolved issues. +---------------- +-- Section 5C -- Development +---------------- **** Questions: -=5C.1 Where do I send bug reports? -=5C.2 Where do I send fixes and patches? + 5C.1 Where do I send bug reports? + 5C.2 Where do I send fixes and patches? 5C.3 Where do I send ideas for future development? - 5C.4 What plans are there for fixing bugs? -=5C.5 What plans are there for new features? -=5C.6 I have some time and I'd like to help. What can I do for you? +=5C.4 What plans are there for new features? + 5C.5 I have some time and I'd like to help. What can I do for you? **** Answers: -=5C.1 Where do I send bug reports? + 5C.1 Where do I send bug reports? First make sure it is a bug. Talk to your friends, coworkers and anyone you know who uses CVS. Search this FAQ for related issues. - Then test it carefully. Try out variations to narrow down the + Then test it carefully. Try out variations to narrow down the problem. Make sure it is repeatable. Look for workarounds so you can report them. @@ -7165,16 +9222,13 @@ See the Development section later for stuff being worked on. question to the info-cvs mailing list. Include any information you have describing the symptoms. - If careful testing reveals an RCS bug rather than a CVS bug, you - can sendbug reports to: rcs-bugs@cs.purdue.edu - -=5C.2 Where do I send fixes and patches? + 5C.2 Where do I send fixes and patches? - First make sure the "fix" does something useful. Have someone to - review your fix. It is better to spend a bit of one person's - thinking time than to waste the time of thousands of people trying - to understand your fix. + First make sure the "fix" does something useful. Have someone + review your fix. Spend a bit of one person's time in a detailed + analysis of your vast idea before displaying a half-vast idea to + hundreds of people. If you tried to fix it and the patch is small, include the patch in your message. Make sure the patch is based on the latest @@ -7184,98 +9238,186 @@ See the Development section later for stuff being worked on. about why it is so large. Did you add a generally useful feature, or did it grow out of hand? - If you still believe it is solid, send it to the maintainer of the - FTP archive (currently the author of this FAQ) for inclusion in - the CVS FTP archive and to Brian Berliner, the maintainer of CVS. + If you still believe it is solid, produce a patch file using the + CVS commands "patch" or "diff -c". [[You *are* keeping CVS under + CVS, right?]] The patch should be based on the latest released + version of CVS. Then use the "cvsbug" program (provided with the + CVS sources) to send it to the CVS maintainers. A self-contained + patch that provides a single useful feature or correction might + show up independently in the patches directory of the FTP archive. + + If careful testing reveals an RCS bug rather than a CVS bug, you + can send bug reports to: rcs-bugs@cs.purdue.edu 5C.3 Where do I send ideas for future development? - [[Brian?]] + If you have a bright idea, discuss it on the info-cvs mailing + list. If you have the time to implement something you can test, + send the diffs along too as described above. - 5C.4 What plans are there for fixing bugs? +=5C.4 What plans are there for new features? - David G. Grubbs <dgg@think.com> plans/hopes to: + A "rename" or "per-directory" database has been bandied about on + the net for years. It is needed, but it is a lot of work. - - Fix the release command to be more sophisticated about - foreign directories, renaming and to allow the release of - anything in the working directory. + CVS version 1.5 supports remote repository access, but Paul + F. Kunz <Paul_Kunz@slac.stanford.edu> has produced another version + (rCVS) that also runs remotely. It is available for testing. - - Fix the history command to track changes made in the - underlying layers since I originally wrote it, including - making "tag" work with history. + On the host "preprint.slac.stanford.edu", you can find: + Paper: slacpubs/5000/slac-pub-5923.ps.Z + This was for a conference in Sept, 1993, before first beta. + + On the host "ftp.slac.stanford.edu", you can find: + Sources: pub/sources/rcvs-0.8.1.tar.Z + + With the caveat that until version 1.0 is available, rCVS should + be considered an unreliable Beta release, you are invited to + grab a copy and test it. - [[Brian?]] [[Others?]] -=5C.5 What plans are there for new features? + 5C.5 I have some time and I'd like to help. What can I do for you? - David G. Grubbs <dgg@think.com> plans/hopes to: + You can review this document, correct errors and fill in any of + the incomplete sections. - - Implement the design described in the Branching spec - distributed to this list in January, 93. It attempts to - address the problem of merging between arbitrary branches - and to fully support the idea of "branching". + You can add to the contrib area, which contains useful ways to use + some of the programmable CVS facilities (loginfo, commitinfo) or + ways of connecting to work environments (pcl-cvs). - - Add a feature to "cvs add" (Maybe a '-a' switch.) to - add a file by the same name as one in the Attic, by - dragging it back out of the Attic. (This connects to the - branching code, since a way has to be added to drag a - file out of the Attic that is merged onto the Main branch - from being only on a side-branch.) + You could write a regression test suite. Or at least a scaffold + into which we can drop tests. - - If no one else wants to deal with it, I would like to - enhance the whole "modules" concept to cover more of - the naming problem and to allow more complicated access - control. (Optional, of course.) + You can write specs for new features, fix bugs, review the man + page or . . . - - Create a set of configuration files (in addition to or to - supersede the cvsignore files) to allow the setting of - a wide variety of site-specific options. + [[Brian?]] + [[Is there some way we can register someone as working + on something or should we just stay in the "implement it and + send it to me" mode?]] - Brian Berliner <berliner@sun.com> plans/hopes to: +---------------- +-- Section 5D -- Professional Support +---------------- - - [[Rename database?]] - [[Brian? Any plans?]] + **** Questions: ++5D.1 Doesn't Cygnus support CVS? ++5D.2 What is Cyclic Software doing with CVS? - Paul F. Kunz <pfkeb@slac.stanford.edu> has produced a version of - CVS (RCVS) that runs remotely. - On the host "ftp.slac.stanford.edu", you can find: - Sources: pub/sources/rcvs-0.5.0.tar.Z - Paper: pub/preprints/slac-pub-5923.ps + **** Answers: ++5D.1 Doesn't Cygnus support CVS? - [[Others?]] + Cygnus is a company that supports a variety of FSF software. It + uses a version of CVS and people from Cygnus are on the info-cvs + mailing list. + [[Could someone from Cygnus state Cygnus's official and unofficial + relationship with CVS?]] -=5C.6 I have some time and I'd like to help. What can I do for you? - You can review this document, correct errors and fill in any of - the incomplete sections. ++5D.2 What is Cyclic Software doing with CVS? - You can add to the contrib area, which contains useful ways to use - some of the programmable CVS facilities (loginfo, commitinfo) or - ways of connecting to work environments (pcl-cvs). + Cyclic Software exists to provide support for CVS. Here's a copy + of their product line sheet: - You could write a regression test suite. Or at least a scaffold - into which we can drop tests. - You can write specs for new features, fix bugs, review the man - page or . . . + Cyclic Software + Standard Support - [[Brian?]] + Cyclic Software offers support contracts for CVS. This + includes: + + * Full source, binaries, and documentation for CVS, RCS, GNU + diffutils, patch, and gzip -- that is, CVS and everything it + wants to run -- via FTP or tape; + + * guaranteed responses for bugs within 5 business days; + + * guaranteed fixes for reproducible bugs within 10 business days. + + (By "reproducible bugs", we mean instances where the software + clearly does not behave as documentation or reasonable + expectations indicate it should, and that we are able to + reproduce this misbehavior reliably. Naturally, we will make + every possible effort to reproduce the bugs you report; our + experience has been that it's usually not difficult.) + + We charge a fixed fee for: + + * one year + + * one host type (hardware & operating system) + + * twenty users at your site, with two of those users designated + as "contacts" for CVS, to reduce communication problems. + + If the host type is not one we have access to for testing + purposes, you can either lend us a machine of the appropriate type + for the duration of the contract, or pay an additional fee + up-front. We have access to Solaris, Irix, HP-UX, Linux and + Ultrix. (This list is subject to change; contact us for details.) + + If the above fee structure is not well-suited to your + organization, please say so. We're interested in tailoring our + services to be as useful to you as possible. + + Training + + We offer on-site training in the use of CVS at a daily rate, + plus expenses (inc. travel, accommodations). The classes target + new and intermediate users of CVS; we feel advanced users benefit + more from a written manual and the source code. + + Custom Enhancements + + We will implement enhancements to CVS or its documentation, and + port CVS to new architectures. Our rates for this work depend on + the amount of work to be done. + + We strongly prefer to work on enhancements suitable for + incorporation into the general CVS release upon completion; we + will help you design the enhancement in a way that makes this + possible. + + Short-Term Consulting + + We will do short-term consulting at hourly rates. These rates + are calculated to include the overhead of dealing in short time + periods. Therefore, in sufficiently large projects, we recommend + arranging a long-term support contract instead of dealing on an + hourly basis. + + Anything Else + + Cyclic Software is interested in arranging contracts for work + in other areas, to be produced as free software. Everything is + negotiable. + + How To Contact Us + + (Email is preferred.) + + Email: <info@cyclic.com> + Phone: +1 812 335 9023 + Web: http://www.cyclic.com + SnailMail: Cyclic Software + P.O. Box 804 + Bloomington, IN 47402-0804 + USA + + Contributed by Jim Blandy - [[Is there some way we can register someone as working - on something or should we just stay in the "implement it and - send it to me" mode?]] ================================================= @@ -7298,7 +9440,7 @@ within each sub-section, though they are in no particular order. A. What is CVS? What's it for? Why CVS? B. Where do I find it? Where can I find Help? C. How does CVS differ from other similar software? - D. What do you mean by . . .? (Definitions) + D. What do you mean by . . .? (Definitions) 2. User Tasks A. Getting Started @@ -7312,16 +9454,18 @@ within each sub-section, though they are in no particular order. 4. Advanced Topics A. Installing CVS B. Setting up and Managing the Repository - C. Branching + C. Branching and Merging D. Tricks of the Trade - E. Weirdness + E. Internal errors F. Related Software - G. Other Systems + G. Engineering + H. Other Systems 5. Past & Future A. Contributors. B. Bugs and Patches C. Development + D. Professional Support 6. Table of Contents @@ -7337,52 +9481,50 @@ within each sub-section, though they are in no particular order. 1A.1 What does CVS stand for? Can you describe it in one sentence? 1A.2 What is CVS for? What does it do for me? 1A.3 How does CVS work? -=1A.4 What is CVS useful for? -=1A.5 What is CVS *not* useful for? -=1A.6 Why isn't it called OSCO (Online Source COntrol)? + 1A.4 What is CVS useful for? + 1A.5 What is CVS *not* useful for? ---------------- -- Section 1B -- Where do I find CVS? Where can I find Help? ---------------- 1B.1 How do I get more information about CVS? 1B.2 Is there an archive of CVS material? - 1B.3 How do I get a copy of the latest version of CVS? - 1B.4 Is there any other documentation? How about tutorials? - 1B.5 Is there a mailing list devoted to CVS? How do I get on it? - 1B.6 What prayers are appropriate for each of the major denominations - (e.g. 20's, 50's, 100's) when issuing complex CVS commands? -+1B.7 How do I get files out of the archive if I don't have FTP? + 1B.3 How do I get files out of the archive if I don't have FTP? + 1B.4 How do I get a copy of the latest version of CVS? + 1B.5 Is there a mailing list devoted to CVS? How do I find it? + 1B.6 What happened to the CVS Usenet newsgroup I heard about? ---------------- --- Section 1C -- How does CVS differ from other similar software? +-- Section 1C -- How does CVS differ from other, similar software? ---------------- -=1C.1 How does CVS differ from RCS? + 1C.1 How does CVS differ from RCS? 1C.2 How does CVS differ from SCCS? -=1C.3 How does CVS differ from ClearCase? - 1C.4 How does CVS differ from TeamWare? - 1C.5 How does CVS differ from SunPro? - 1C.6 How does CVS differ from Aegis? - 1C.7 How does CVS differ from Shapetools? -+1C.8 How does CVS differ from TeamNet? -+1C.9 How does CVS differ from ProFrame? -+1C.10 How does CVS differ from CaseWare/CM? -+1C.11 How does CVS differ from Sublime? + 1C.3 How does CVS differ from ClearCase? +#1C.4 How does CVS differ from TeamWare/SparcWorks? + 1C.5 How does CVS differ from Aegis? + 1C.6 How does CVS differ from Shapetools? + 1C.7 How does CVS differ from TeamNet? + 1C.8 How does CVS differ from ProFrame? + 1C.9 How does CVS differ from CaseWare/CM? + 1C.10 How does CVS differ from Sublime? + 1C.11 How does CVS differ from PVCS? + 1C.12 How does CVS differ from CMVC? ---------------- -- Section 1D -- What do you mean by . . .? (Definitions) ---------------- -#1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? + 1D.1 What are "The Repository", "$CVSROOT" and "CVSROOT"? 1D.2 What is an RCS file? 1D.3 What is a working file? 1D.4 What is a working directory (or working area)? 1D.5 What is "checking out"? -=1D.6 What is a revision? + 1D.6 What is a revision? 1D.7 What is a "Tag"? -=1D.8 What are "HEAD" and "BASE"? -=1D.9 What is a Branch? -=1D.10 What is "the trunk"? -=1D.11 What is a module? -+1D.12 What does "merge" mean? + 1D.8 What are "HEAD" and "BASE"? + 1D.9 What is a Branch? + 1D.10 What is "the trunk"? + 1D.11 What is a module? + 1D.12 What does "merge" mean? ========================================== @@ -7394,7 +9536,7 @@ within each sub-section, though they are in no particular order. ---------------- 2A.1 What is the first thing I have to know? 2A.2 Where do I work? -=2A.3 What does CVS use from my environment? + 2A.3 What does CVS use from my environment? 2A.4 OK, I've been told that CVS is set up, my module is named "ralph" and I have to start editing. What do I type? 2A.5 I have been using RCS for a while. Can I convert to CVS without @@ -7403,12 +9545,12 @@ within each sub-section, though they are in no particular order. ---------------- -- Section 2B -- Common User Tasks ---------------- -#2B.1 What is the absolute minimum I have to do to edit a file? -=2B.2 If I edit multiple files, must I type "commit" for each one? - 2B.3 How do I get rid of the directory that "checkout" created? -=2B.4 How do I find out what has changed? -=2B.5 I just created a new file. How do I add it to the Repository? -=2B.6 How do I merge changes made by others into my working directory? + 2B.1 What is the absolute minimum I have to do to edit a file? + 2B.2 If I edit multiple files, must I type "commit" for each one? + 2B.3 How do I get rid of the <module> directory that "checkout" created? + 2B.4 How do I find out what has changed since my last update? + 2B.5 I just created a new file. How do I add it to the Repository? + 2B.6 How do I merge changes made by others into my working directory? 2B.7 How do I label a set of revisions so I can retrieve them later? 2B.8 How do I checkout an old release of a module, directory or file? 2B.9 What do I have to remember to do periodically? @@ -7416,34 +9558,34 @@ within each sub-section, though they are in no particular order. ---------------- -- Section 2C -- Less Common User Tasks ---------------- - 2C.1 Can I create sub-directories in my working directory? + 2C.1 Can I create non-CVS sub-directories in my working directory? 2C.2 How do I add new sub-directories to the Repository? 2C.3 How do I remove a file I don't need? -=2C.4 How do I rename a file? + 2C.4 How do I rename a file? 2C.5 How do I make sure that all the files and directories in my working directory are really in the Repository? -=2C.6 How do I create a branch? -=2C.7 How do I modify the modules file? How about the other files in + 2C.6 How do I create a branch? + 2C.7 How do I modify the modules file? How about the other files in the CVSROOT administrative area? -+2C.8 How do I split a file into pieces, retaining revision histories? + 2C.8 How do I split a file into pieces, retaining revision histories? ---------------- -- Section 2D -- General Questions ---------------- -=2D.1 How do I see what CVS is trying to do? + 2D.1 How do I see what CVS is trying to do? 2D.2 If I work with multiple modules, should I check them all out and commit them occasionally? Is it OK to leave modules checked out? 2D.3 What is a "sticky" tag? What makes it sticky? How do I loosen it? 2D.4 How do I get an old revision without updating the "sticky tag"? -=2D.5 What operations disregard sticky tags? -=2D.6 Is there a way to avoid reverting my Emacs buffer after + 2D.5 What operations disregard sticky tags? + 2D.6 Is there a way to avoid reverting my Emacs buffer after committing a file? Is there a "cvs-mode" for Emacs? 2D.7 How does conflict resolution work? What *really* happens if two of us change the same file? 2D.8 How can I tell who has a module checked out? -#2D.9 Where did the .#<file>.1.3 file in my working directory come from? - 2D.10 What is this "ignore" stuff? - 2D.11 Why does .cvsignore not ignore directories? + 2D.9 Where did the .#<file>.1.3 file in my working directory come from? + 2D.10 What is this "ignore" business? What is it ignoring? + 2D.11 Is there a way to set user-specific configuration options? 2D.12 Is it safe to interrupt CVS using Control-C? 2D.13 How do I turn off the "admin" command? 2D.14 How do I turn off the ability to disable history via "cvs -l"? @@ -7459,20 +9601,24 @@ within each sub-section, though they are in no particular order. ---------------- 3A.1 What is "add" for? 3A.2 How do I add a new file to the branch I'm working on? - 3A.3 Why did my newly added file end up in the Attic? - 3A.4 How do I put a new file on the Main Branch and branch off from - there onto my default branch? + 3A.3 Why did my new file end up in the Attic? + 3A.4 Now that it's in the Attic, how do I connect it to the Main branch? + 3A.5 How do I avoid the hassle of reconnecting an Attic-only file to + the Main Branch? + 3A.6 How do I cancel an "add"? + 3A.7 What are the ./CVS/file,p and ./CVS/file,t files for? + 3A.8 How do I "add" a binary file? ---------------- -- Section 3B -- "admin", "adm", "rcs" ---------------- 3B.1 What is "admin" for? 3B.2 Wow! Isn't that dangerous? -=3B.3 What would I normally use "admin" for? -=3B.4 What should I avoid when using "admin"? --3B.5 How do I restrict the "admin" command? The -i flag in the modules + 3B.3 What would I normally use "admin" for? + 3B.4 What should I avoid when using "admin"? + 3B.5 How do I restrict the "admin" command? The -i flag in the modules file can restrict commits. What's the equivalent for "admin"? -+3B.6 I backed out a revision with "admin -o" and committed a + 3B.6 I backed out a revision with "admin -o" and committed a replacement. Why doesn't "update" retrieve the new revision? ---------------- @@ -7486,41 +9632,41 @@ within each sub-section, though they are in no particular order. 3C.6 How do I avoid dealing with those long relative pathnames? 3C.7 Can I move a checked-out directory? Does CVS remember where it was checked out? -#3C.8 How can I lock files on checkout the way RCS does? -+3C.9 What is "checkout -s"? How is it different from "checkout -c"? + 3C.8 How can I lock files while I'm working on them the way RCS does? + 3C.9 What is "checkout -s"? How is it different from "checkout -c"? ---------------- -- Section 3D -- "commit", "ci", "com" ---------------- 3D.1 What is "commit" for? -=3D.2 If I edit ten files, do I have to type "commit" ten times? + 3D.2 If I edit ten files, do I have to type "commit" ten times? 3D.3 Explain: cvs commit: Up-to-date check failed for `<file>' 3D.4 What happens if two people try to "commit" conflicting changes? 3D.5 I committed something and I don't like it. How do I remove it? -=3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch -=3D.7 Why does "commit -r <branch_tag>" put new files in the attic? -+3D.8 Why does "commit -r <rev>" ignore <rev> on an added file? + 3D.6 Explain: cvs commit: sticky tag `V3' for file `X' is not a branch + 3D.7 Why does "commit -r <tag/rev>" put newly added files in the Attic? + 3D.8 Why would a "commit" of a newly added file not produce rev 1.1? ---------------- -- Section 3E -- "diff", "di", "dif" ---------------- 3E.1 What is "diff" for? -=3E.2 Why did "diff" display nothing when I know there are later + 3E.2 Why did "diff" display nothing when I know there are later committed revisions in the Repository? -#3E.3 How do I display what changed in the Repository since I last + 3E.3 How do I display what changed in the Repository since I last executed "checkout", "update" or "commit"? -=3E.4 How do I display the difference between my working file and what + 3E.4 How do I display the difference between my working file and what I checked in last Thursday? -=3E.5 Why can't I pass the --unified option to "diff"? + 3E.5 Why can't I pass long options, like --unified, to "diff"? ---------------- -- Section 3F -- "export", "exp", "ex" ---------------- 3F.1 What is "export" for? -=3F.2 Why does it remove the RCS keywords so I can't use the "ident" + 3F.2 Why does it remove the RCS keywords so I can't use the "ident" command on the source files? -=3F.3 Can I override the '-kv' flag CVS passes to RCS? -=3F.4 Why the hell not? + 3F.3 Can I override the '-kv' flag CVS passes to RCS? + 3F.4 Why doesn't "export" have a '-k' flag like "import" does? 3F.5 Why does "export -D" check out every file in the Attic? ---------------- @@ -7530,51 +9676,53 @@ within each sub-section, though they are in no particular order. 3G.2 Of what use is it? 3G.3 What is this, Big Brother? 3G.4 I deleted my working directory and "history" still says I have - it checked out. How do I fix it? + it checked out. How do I fix it? 3G.5 So I *can* edit the History file? 3G.6 Why does the history file grow so quickly? 3G.7 What is the difference between "cvs history -r <tag/rev>" and "cvs history -t <tag>"? 3G.8 Why does "cvs history -c -t <tag>" fail to print anything? 3G.9 "cvs history -a -o" only printed one line for each checked-out - module. Shouldn't it print all the directories where the + module. Shouldn't it print all the directories where the modules are checked out? -=3G.10 I can't figure out "history", can you give me concrete examples? + 3G.10 I can't figure out "history", can you give me concrete examples? + 3G.11 Can we merge history files when we merge Repositories? ---------------- -- Section 3H -- "import", "im", "imp" ---------------- -=3H.1 What is "import" for? -=3H.2 How am I supposed to use "import"? -=3H.3 Why does import put files on a branch? Why can't you put it on - the Main Trunk and let me work on a branch? + 3H.1 What is "import" for? + 3H.2 How am I supposed to use "import"? + 3H.3 Why does import put files on a branch? Why can't I work on the + main trunk instead of a Vendor branch? 3H.4 Is there any way to import binary files? -=3H.5 Why does "import" corrupt some binary files? - 3H.6 How do I keep "import" from expanding all the $\Revision$ strings - to be 1.1.1.1? -#3H.7 I imported some files for the Yarg compiler that compiles files + 3H.5 Why does "import" corrupt some binary files? + 3H.6 How do I retain the original $\Revision$ strings in the sources? +=3H.7 I imported some files for the Yarg compiler that compiles files with a suffix of ".yarg" and whose comment prefix is "YARG> ". When I check them out, they will no longer compile because they - have this junk in them. Why? + have this junk in them. Why? 3H.8 How do I make "import" save the timestamps on the original files? - 3H.9 Why didn't "import" ignore the directories I told it to? - 3H.10 Why can't I "import" 3 releases on different branches? - 3H.11 What do I do if the Vendor adds or deletes files between releases? - 3H.12 What about if the Vendor changes the names of files or + 3H.9 Why can't I "import" 3 releases on different branches? + 3H.10 What do I do if the Vendor adds or deletes files between releases? + 3H.11 What about if the Vendor changes the names of files or directories, or rearranges the whole structure between releases? - 3H.13 I thought "import" was for Vendor releases, why would I use it + 3H.12 I thought "import" was for Vendor releases, why would I use it for code of my own? Do I have to use import? -=3H.14 How do I import a large Vendor release? -+3H.15 Explain: ERROR: cannot create link to <file>: Permission denied + 3H.13 How do I import a large Vendor release? + 3H.14 Explain: ERROR: cannot create link to <file>: Permission denied + 3H.15 Where does the -m <message> go when the file doesn't change? + 3H.16 How do I "import" just the files ignored by a previous "import"? + 3H.17 Why did "import" ignore all the symlinks? ---------------- -- Section 3I -- "log", "lo", "rlog" ---------------- -=3I.1 What is "log" for? + 3I.1 What is "log" for? 3I.2 How do I extract the log entries between two revisions? -=3I.3 How do I extract the log entries on a whole branch? + 3I.3 How do I extract the log entries on a whole branch? 3I.4 How do I generate ChangeLogs from RCS logs? -=3I.5 Why does "log" tell me a file was committed exactly 5 hours later + 3I.5 Why does "log" tell me a file was committed exactly 5 hours later than I know it was? ---------------- @@ -7589,17 +9737,15 @@ within each sub-section, though they are in no particular order. -- Section 3K -- "release", "re", "rel" ---------------- 3K.1 What is "release" for? - 3K.2 Why does release -d delete directories within my directory that - weren't ever in the CVS Repository? - 3K.3 Why can't I reverse a "cvs checkout path/name/subdir" with a + 3K.2 Why can't I reverse a "cvs checkout path/name/subdir" with a "cvs release path/name/subdir" without an "unknown module name"? - 3K.4 Why can't I "release" portions of a checked out directory? I + 3K.3 Why can't I "release" portions of a checked out directory? I should be able to "release" any file or sub-directory within my working directory. - 3K.5 I removed the tree that I was about to start working on. How do I + 3K.4 I removed the tree that I was about to start working on. How do I tell cvs that I want to release it if I don't have it anymore? - 3K.6 Why doesn't "release -d module" reverse a "checkout module"? - 3K.7 Why can't I release a module renamed with "cvs checkout -d"? + 3K.5 Why doesn't "release -d module" reverse a "checkout module"? + 3K.6 Why can't I release a module renamed with "cvs checkout -d"? ---------------- -- Section 3L -- "remove", "rm", "delete" @@ -7607,169 +9753,198 @@ within each sub-section, though they are in no particular order. 3L.1 What is "remove" for? 3L.2 Why doesn't "remove" work on directories when it appears to try? 3L.3 I don't like removing files. Is there another way to ignore them? - 3L.4 I just removed a file. How do I resurrect it? - 3L.5 Why doesn't "remove" delete the file? Instead, it prints: - cvs remove: no files removed; use `rm' to remove the file first + 3L.4 I just removed a file. How do I resurrect it? + 3L.5 Why doesn't "remove" delete the file? Instead, it prints an + error message and tells me to remove the file by hand. ---------------- -- Section 3M -- "rtag", "rt", "rfreeze" ---------------- 3M.1 What is "rtag" for? - 3M.2 Why would you use "rtag"? It assumes a static Repository. + 3M.2 Why use "rtag"? It assumes no one is changing the Repository. + 3M.3 What revision does "rtag -r <tag1> <tag2>" actually put the tag on? + 3M.4 What happens if the tags are the same in "rtag -r <tag> <tag>"? + 3M.5 Why doesn't "rtag -b -r <branch_tag1> <branch_tag2>" rename or + duplicate a magic branch tag? ---------------- -- Section 3N -- "status", "st", "stat" ---------------- -=3N.1 What is "status" for? + 3N.1 What is "status" for? 3N.2 Why does "status" limit the File: at the top to 17 characters? -+3N.3 Shouldn't the status "Needs Checkout" be "Needs Update"? + 3N.3 Why does it print "Sticky" lines when the values are "(none)"? + 3N.4 Shouldn't the status "Needs Checkout" be "Needs Update"? ---------------- -- Section 3O -- "tag", "ta", "freeze" ---------------- 3O.1 What is "tag" for? -=3O.2 What is the difference between "tag" and "rtag"? -=3O.3 Why does "tag -b" not put a tag on the Branch Point revision? + 3O.2 What is the difference between "tag" and "rtag"? + 3O.3 Why does "tag -b" not put a tag on the Branch Point revision? How do I refer to the Branch Point? --3O.4 So "tag" labels a bunch of files. What do you use a Tag for? + 3O.4 So "{r}tag" labels a bunch of files. What do you use a Tag for? 3O.5 How do I get "tag" and "rtag" to send mail the way "commit" does? 3O.6 Why can't "tag" handle the '-r' option that "rtag" takes? --3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout - -r <tag>" somewhere else produce copy of my current files? -#3O.8 Why doesn't "tag" write a history record the way "rtag" does? + 3O.7 After a "tag <tag>" in my working directory, why doesn't "checkout + -r <tag>" somewhere else produce copies of my current files? + 3O.8 Why doesn't "tag" write a history record the way "rtag" does? + 3O.9 How do I rename a <tag>? ---------------- -- Section 3P -- "update", "up", "upd" ---------------- 3P.1 What is "update" for? -=3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they + 3P.2 What do 'U', 'M' and 'C' mean when I type "update"? Are they different for "cvs -n update"? 3P.3 What's the difference between "update" and "checkout"? -=3P.4 Why don't I get new files when I execute "update"? -#3P.5 Why does "update" say 'M' both for plain modified files and for - successful (i.e. conflict-free) merges? Aren't they different? -=3P.6 After a merge ("update" or "update -j"), why doesn't CVS remember - the conflict and not allow you to commit the result until the - conflict is resolved? + 3P.4 Why don't I get new files when I execute "update"? + 3P.5 Why does "update" say 'M' both for plain modified files and for + successful (i.e. conflict-free) merges? Aren't they different? + 3P.6 What's a "sticky conflict"? How does it know a conflict occurred? 3P.7 Is there a feature to tell me what I have changed, added and removed without changing anything? -=3P.8 Why does "cvs update" not flag directories that are not in the - Repository as it does with new files? - 3P.9 Why are all my files deleted when I execute "update"? + 3P.8 Why were all my files deleted when I executed "update"? =============================================== -== Section 4 ==== Advanced Topics ==== +== Section 4 ==== Advanced Topics ==== =============================================== ---------------- -- Section 4A -- Installing CVS ---------------- -#4A.1 What do I have to do before I install CVS? + 4A.1 What do I have to do before I install CVS? 4A.2 How do I configure the CVS programs? -=4A.3 What do I have to install? - 4A.4 How do I get around the bugs I've heard of GNU diff version 2.2? + 4A.3 What do I have to install? + 4A.4 How do I work around the merge problems in GNU diff version 2.1 + or later? ---------------- -- Section 4B -- Setting up and Managing the Repository ---------------- -=4B.1 What do I do first? How do I create a Repository? -=4B.2 What are those files in $CVSROOT/CVSROOT? + 4B.1 What do I do first? How do I create a Repository? + 4B.2 What are those files in $CVSROOT/CVSROOT? 4B.3 Is there any other state stored in the Repository besides in the $CVSROOT/CVSROOT directory? 4B.4 How do I put sources into the Repository? -=4B.5 What file permissions should I use on (and in) the Repository? -=4B.6 How do I structure my Repository? -=4B.7 How do I manage the modules file? - 4B.8 Why would anyone use "modules"? They are too restrictive. I + 4B.5 What file permissions should I use on (and in) the Repository? + 4B.6 How do I structure my Repository? + 4B.7 Why would anyone use "modules"? They are too restrictive. I want to be able to select just the files I want to edit. -=4B.9 How do I rename a file or directory? What are the consequences? -=4B.10 What are "Attic" directories? - 4B.11 Is it OK to remove anything from the Repository? - 4B.12 Can I convert to CVS from RCS without losing my revision history? -=4B.13 Can I move RCS files with branches in them into the Repository? -=4B.14 Can I use raw RCS commands on the Repository? - 4B.15 How do I convert from SCCS to RCS? -=4B.16 How do I limit access to the Repository? -=4B.17 What are the Repository Administrator's responsibilities? - 4B.18 How do I move the whole Repository? -+4B.19 How do I change permissions on a file in the Repository by using - a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") - ----------------- --- Section 4C -- Branching + 4B.8 How do I rename a file or directory? What are the consequences? + 4B.9 What are "Attic" directories? + 4B.10 Is it OK to remove anything from the Repository? + 4B.11 Can I convert to CVS from RCS without losing my revision history? + 4B.12 Can I move RCS files with branches in them into the Repository? + 4B.13 Can I use raw RCS commands on the Repository? + 4B.14 How do I convert from SCCS to RCS? + 4B.15 How do I limit access to the Repository? + 4B.16 What are the Repository Administrator's responsibilities? + 4B.17 How do I move the whole Repository? + 4B.18 How do I change permissions on a file in the Repository by using + a CVS command? (i.e. without using "chmod 777 $CVSROOT/dir/file") + +---------------- +-- Section 4C -- Branching and Merging ---------------- 4C.1 What is a branch? -=4C.2 Why (or when) would I want to create a branch? -=4C.3 How do I create and checkout a branch? + 4C.2 Why (or when) would I want to create a branch? + 4C.3 How do I create and checkout a branch? 4C.4 Once created, how do I manage a branch? 4C.5 Are there any extra issues in managing multiple branches? 4C.6 How do I merge a whole branch back into the trunk? - 4C.7 How do I merge changes from the trunk into my branch or between +=4C.7 How do I merge changes from the trunk into my branch or between branches? - 4C.8 How do I add a new file to a branch? -=4C.9 How do I know what branch I'm (working) on? + 4C.8 How do I merge onto the Main Branch a file that exists only on a + branch other than the Main Branch? (i.e. it is in the Attic) + 4C.9 How do I know what branch I'm (working) on? 4C.10 Do I really have to know the name of the branch I'm working on? 4C.11 How do I refer to the revision where I branched so I can see what changed since the Branch Point on another branch? 4C.12 Why didn't the command "cvs admin -bBRANCH1 *" create a branch? 4C.13 Is it possible to set the "default CVS branch" for everyone? -=4C.14 How do I perform a large merge? + 4C.14 How do I perform a large merge? 4C.15 Is a Vendor merge any different from a branch merge? -+4C.16 How do I go back to a previous version of the code on a branch? -+4C.17 Why do I get the latest files on the branch when I tried to + 4C.16 How do I go back to a previous version of the code on a branch? + 4C.17 Once I've found the files I want, how do I start changing them? + I keep getting warnings about sticky tags. + 4C.18 Why do I get the latest files on the branch when I tried to "update -r <tag>"? + 4C.19 How can I avoid a merge? I just want to move the latest revision + on my working branch directly onto the trunk. + 4C.20 How to I avoid merge collisions in the RCS $\Log$ data? + 4C.21 Why should I trust automatic merges? + 4C.22 How does CVS decide if it can safely perform a merge? + 4C.23 After resolving merge conflicts in a file, what if I want to keep + my previous version, and not take any of the branch changes? ---------------- -- Section 4D -- Tricks of the Trade ---------------- -=4D.1 How can you even check in binary files, let alone allow CVS to + 4D.1 How can you even check in binary files, let alone allow CVS to do its auto-merge trick on them? 4D.2 Can I edit the RCS (",v") files in the Repository? 4D.3 Can I edit the ./CVS/{Entries,Repository,Tag} files? -=4D.4 Someone executed "admin -o" and removed revisions to which + 4D.4 Someone executed "admin -o" and removed revisions to which tags/symbols were attached. How do I fix them? -=4D.5 How do I move a magic branch tag? + 4D.5 How do I move or rename a magic branch tag? 4D.6 Can I use RCS locally to record my changes without making them globally visible by committing them? 4D.7 How can I allow access to the Repository by both CVS and RCS? -=4D.8 I "updated" a file my friend "bubba" committed yesterday. + 4D.8 I "updated" a file my friend, "bubba", committed yesterday. Why doesn't the file now have a modified date of yesterday? -#4D.9 While in the middle of a large "commit", how do I run other + 4D.9 While in the middle of a large "commit", how do I run other commands, like "diff" or "stat" without seeing lock errors? - 4D.10 Why does the merge occasionally resurrect lines of code? - 4D.11 Why does the merge fail when my "rcsmerge" program is - configured to use GNU diff version 2.1 or later? - 4D.12 What the hell is Entries.Static? -=4D.13 Why did I get the wrong Repository in the loginfo message? -=4D.14 Can I have multiple source repositories, one for each project? - 4D.15 How do I run CVS setuid so I can only allow access through the + 4D.10 Where did the ./CVS/Entries.Static file come from? What is it for? + 4D.11 Why did I get the wrong Repository in the loginfo message? + 4D.12 How do I run CVS setuid so I can only allow access through the CVS program itself? - 4D.16 How about using groups and setgid() then? - 4D.17 How do I use the "commitinfo" file? - 4D.18 How do I use the "loginfo" files? + 4D.13 How about using groups and setgid() then? + 4D.14 How do I use the "commitinfo" file? + 4D.15 How do I use the "loginfo" files? + 4D.16 How can I keep people with restrictive umask values from blocking + access to the Repository? + 4D.17 Why do timestamps sometimes get set to the date of the revision, + sometimes not? The inconsistency causes unnecessary recompiles. ---------------- --- Section 4E -- Weirdness +-- Section 4E -- Internal errors ---------------- 4E.1 Explain: "ci error: unexpected EOF in diff output" -=4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" -=4E.3 I don't want a Vendor branch. Why can't I work on the main trunk? - 4E.4 Merges can't work. I don't trust them. If you won't change it to - something I can understand, I won't use CVS. - 4E.5 Explain: "co error, line 2: Missing access list" -+4E.6 Explain: "error: RCS file name `xyz .c' contains white space" -+4E.7 Explain: cvs checkout: warning: <X> is not (any longer) pertinent + 4E.2 Explain: "RCS file /Repository/module/file.c,v is in use" + 4E.3 Explain: "co error, line 2: Missing access list" + 4E.4 Explain: "error: RCS file name `xyz .c' contains white space" + 4E.5 Explain: cvs checkout: warning: <X> is not (any longer) pertinent + 4E.6 Why did a Repository file change from <file>,v to ,<file>,? + +---------------- +-- Section 4F -- Related Software +---------------- + 4F.1 How do I use CVS under Emacs? Is there an Emacs cvs-mode? + 4F.2 What is GIC (Graphical Interface to CVS)? + 4F.3 What is CAVEMAN? ---------------- --- Section 4G -- Other Systems +-- Section 4G -- Engineering ---------------- - 4G.1 I use a NeXT. Is there anything I need to know? - 4G.2 I use OS/2. Is there anything I need to know? - 4G.3 I use SCO Unix. Is there anything I need to know? - 4G.4 I use AIX. Is there anything I need to know? -=4G.5 I use IRIX. Is there anything I need to know? - 4G.6 I use an HP system. Is there anything I need to know? + 4G.1 Where can I find out about Software Engineering? + 4G.2 How do I flexibly arrange the modules file to describe my sources? + 4G.3 Can I have multiple source repositories, one for each project? + 4G.4 Who should administer the Repository and manage the modules file? + 4G.5 Isn't disk space a big factor? CVS copies files out of the + Repository, duplicating everything. + +---------------- +-- Section 4H -- Other Systems +---------------- + 4H.1 I use a NeXT. Is there anything I need to know? + 4H.2 I use OS/2 and/or DOS. Is there anything I need to know? + 4H.3 I use SCO Unix. Is there anything I need to know? + 4H.4 I use AIX. Is there anything I need to know? + 4H.5 I use IRIX. Is there anything I need to know? + 4H.6 I use an HP system. Is there anything I need to know? + 4H.7 I use AFS. Is there anything I need to know? + 4H.8 I use A/UX. Is there anything I need to know? ============================================= @@ -7779,37 +9954,42 @@ within each sub-section, though they are in no particular order. ---------------- -- Section 5A -- Contributors ---------------- - 5A.1 Who wrote CVS? -=5A.2 You didn't write all of this FAQ, did you? +=5A.1 Who wrote CVS? + 5A.2 You didn't write all of this FAQ, did you? ---------------- -- Section 5B -- Bugs and Patches ---------------- 5B.1 Why can't CVS handle deletion of directories? -=5B.2 Why can't CVS handle the moving of sources from one place in the + 5B.2 Why can't CVS handle the moving of sources from one place in the directory hierarchy to another? -=5B.3 Why does "checkout" recurse indefinitely if an alias contains - its own name? - 5B.4 When I typed "cvs update -D <date>", why did it check out all - sorts of ancient files from the Attic? Shouldn't it just create + 5B.3 When I typed "cvs update -D <date>", why did it check out all + sorts of ancient files from the Attic? Shouldn't it just create the set of files and revisions that existed at that date? - 5B.5 When I typed "cvs update -D <date>" in my branch, why did it + 5B.4 When I typed "cvs update -D <date>" in my branch, why did it screw up all my files? - 5B.6 When I executed "checkout" into an existing directory I got "No - such file or directory" errors. Why? - 5B.7 Why does "update" send all output to the terminal after 26 files + 5B.5 When I executed "checkout" into an existing directory I got "No + such file or directory" errors. Why? + 5B.6 Why does "update" send all output to the terminal after 26 files have been updated? -+5B.8 Why doesn't the "-I !" option work in update and import? + 5B.7 Why does the merge occasionally resurrect lines of code? + 5B.8 Why does the merge fail when my "rcsmerge" program is + configured to use GNU diff version 2.1 or later? ---------------- -- Section 5C -- Development ---------------- -=5C.1 Where do I send bug reports? -=5C.2 Where do I send fixes and patches? + 5C.1 Where do I send bug reports? + 5C.2 Where do I send fixes and patches? 5C.3 Where do I send ideas for future development? - 5C.4 What plans are there for fixing bugs? -=5C.5 What plans are there for new features? -=5C.6 I have some time and I'd like to help. What can I do for you? +=5C.4 What plans are there for new features? + 5C.5 I have some time and I'd like to help. What can I do for you? + +---------------- +-- Section 5D -- Professional Support +---------------- ++5D.1 Doesn't Cygnus support CVS? ++5D.2 What is Cyclic Software doing with CVS? ================================================= diff --git a/gnu/usr.bin/cvs/README b/gnu/usr.bin/cvs/README index 6b70021..b257f89 100644 --- a/gnu/usr.bin/cvs/README +++ b/gnu/usr.bin/cvs/README @@ -1,11 +1,11 @@ $CVSid: @(#)README 1.32 94/10/22 $ - CVS Kit, Version 1.4 Alpha + CVS Kit - Copyright (c) 1993-1994 Brian Berliner - Copyright (c) 1992 Brian Berliner and Jeff Polk - Copyright (c) 1989-1992, Brian Berliner - All Rights Reserved + Copyright (c) 1993-1994 Brian Berliner + Copyright (c) 1992 Brian Berliner and Jeff Polk + Copyright (c) 1989-1992, Brian Berliner + All Rights Reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,9 +23,14 @@ $CVSid: @(#)README 1.32 94/10/22 $ ------------------------------------------------------------------------------- -Welcome to the CVS 1.4 Alpha release! We thank you for taking the time to -test this (potentially unstable) release and report bugs (ideally, with -fixes). To report bugs, run the "cvsbug" program and fill out the template: +Welcome to CVS! + +Bug reports are accepted, however note that someone may or may not +feel like taking care of your bug report. Support contracts are +available from Cyclic Software (http://www.cyclic.com). + +To report bugs send mail to bug-cvs@prep.ai.mit.edu, or run the "cvsbug" +program and fill out the template: $ cvsbug @@ -33,12 +38,11 @@ The "cvsbug" program is installed in the same location as the "cvs" program. If your installation failed, you may need to run "cvsbug" directly out of the "src" directory as "src/cvsbug.sh". -While we cannot promise to be responsive to said bugs, we do appreciate -your support through this Alpha process. Please consult the INSTALL file -for information on tested configurations. If you have a comment about an -already tested configuration, or have tried CVS on a new configuration, -please write to the above address and let us know! Free software only -works if we all help out. +Please consult the INSTALL file for information on tested +configurations. If you have a comment about an already tested +configuration, or have tried CVS on a new configuration, please write +to the above address and let us know! Free software only works if we +all help out. Finally, we cannot guarantee that this release will not completely wipe out all of your work from your system. We do some simple testing before each @@ -48,7 +52,7 @@ SOFTWARE IS SUPPLIED COMPLETELY "AS IS". NO WARRANTY.... Thanks for your support! - -The CVS Alpha Team + -The CVS Team, and the Cyclic CVS Hackers ------------------------------------------------------------------------------- @@ -78,14 +82,14 @@ Special note to current CVS 1.3 users: --> You can skip this section and go straight to "Installation" if you <-- --> have not been running any previous releases of CVS. <-- -See the ChangeLog file in this directory to find out what has changed from -CVS 1.3. +See the NEWS file for a description of features new in this version. -Some files have been renamed from the CVS 1.3 distribution. If you're not -careful, this can cause your build of CVS 1.4 to fail in strange ways. In -particular, be sure to remove the src/config.h file (which is now -src/options.h), as the correct config.h file is generated automatically by -the "configure" stage of installation (and installed in this directory). +Some files have been renamed from the CVS 1.3 distribution. If you're +not careful, this can cause your CVS build to fail in strange ways. +In particular, be sure to remove the src/config.h file (which is now +src/options.h), as the correct config.h file is generated +automatically by the "configure" stage of installation (and installed +in this directory). ------------------------------------------------------------------------------- @@ -95,31 +99,25 @@ Please read the INSTALL file for installation instructions. Brief summary: $ ./configure $ make + $ make check # optional, long-running, step $ make install $ cvsinit ------------------------------------------------------------------------------- -Mailing Lists: +* How do I get up-to-date information and information about other +versions of CVS? -I have setup the following mailing list for CVS users and other interested -parties. I have no idea what kind of volume will be generated on this -list. Nor can I guarantee to personally respond to questions posted to the -list. Anyway, the mailing list is: +On the web, http://www.winternet.com/~zoo/cvs/ or +http://www.loria.fr/~molli/cvs-index.html. - info-cvs-request@prep.ai.mit.edu - Requests for addition to or removal from the mailing list - must be sent to this address. Problems with the list - (like bounced mail) should also be sent here. Please be - specific about your email address. +The mailing list for CVS is info-cvs@prep.ai.mit.edu. Send +subscription and removal requests for that list to +info-cvs-requests@prep.ai.mit.edu. - info-cvs@prep.ai.mit.edu - Questions, bugs, porting problems, hints, or whatever - can be sent to this address. A Frequently Asked Questions - (FAQ) my be available from host "think.com" in the - "/pub/cvs" directory. Please consult the FAQ before - sending questions to info-cvs. DO NOT SEND ADDITION AND - REMOVAL REQUESTS TO THIS ALIAS. +[Historical note: info-cvs@prep.ai.mit.edu is now the union of + info-cvs@prep and cyclic-cvs@cyclic.com. Please use the prep + address.] ------------------------------------------------------------------------------- @@ -170,6 +168,9 @@ Paul Sander, HaL Computer Systems, Inc. <paul@hal.com> wrote and contributed the code in lib/sighandle.c. I added support for POSIX, BSD, and non-POSIX/non-BSD systems. +Jim Kingdon and others at Cygnus Support <info@cygnus.com> wrote the +remote repository access code. + In addition to the above contributors, the following Beta testers deserve special mention for their support. If I have left off your name, I apologize. Just write to me and let me know! @@ -203,5 +204,4 @@ contributed GNU Emacs CVS-mode in contrib/pcl-cvs. ------------------------------------------------------------------------------- - Brian Berliner - berliner@sun.com + Cyclic Software <info@cyclic.com> diff --git a/gnu/usr.bin/cvs/TODO b/gnu/usr.bin/cvs/TODO index 90795c4..1b8cb60 100644 --- a/gnu/usr.bin/cvs/TODO +++ b/gnu/usr.bin/cvs/TODO @@ -1,11 +1,10 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ -01. testing. An automated testing enviroment would be a big win. - 14. Pathname stripper, for checkout, as well as for writing the Repository file. [[ I have a simple one, but need to make sure to call it at all the appropriate points ]] + (I'm not sure what this means -kingdon, Jun 1995). 16. List of current users of a directory needs to be maintained. [[ sort of solved by history database ]] @@ -13,6 +12,8 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 22. Catch signals for cleanup when "add"ing files. 24. Insist on a log message. + (This should be configurable via commitinfo or some new config file + -kingdon, Jun 1995). 30. Add "patch" program option to the modules database. @@ -37,11 +38,7 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ file system directly, using special known command names? 49. cvs xxx commands should be able to deal with files in other - directories. I want to do a cvs ci foo/bar.c. This complicates things - a bit because one might specify files in different directories, but you - could just bucket sort them and do everything for each directory - together. Other than that, it's just a matter of using the adm - directory from the directory containing the file rather than the cwd. + directories. I want to do a cvs add foo/bar.c. [[ most commands now use the generic recursion processor, but not all; this note is left here to remind me to fix the others ]] @@ -85,11 +82,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ that a merge will be necessary before their checkin can be successful. -56. There should be a .cvsrc file that is sourced to customize various - variables. Perhaps there should be a system-wide .cvsrc file that is - sourced, then the one in one's home directory, then the environment - variables are checked for overriding values. - 62. Consider using revision controlled files and directories to handle the new module format -- consider a cvs command front-end to add/delete/modify module contents, maybe. @@ -135,14 +127,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 85. Add revision controlled symbolic links to CVS using one of the tag fields in the RCS file. -87. Consider renaming directories and files. - -88. Consider using mmap to read files on Sun systems and using a smaller - buffer to read files on other systems. A patch has been supplied. - -89. Study the new Dick Grune version of CVS and port any new interesting - features to my version of CVS. - 91. Better document the format of the source repository and how one might convert their current SCCS or RCS files into CVS format. @@ -156,13 +140,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 93. Need to think hard about release and development environments. Think about execsets as well. -94. Need to think hard about remote source control and environments - together. - [[ a contributor has this working over Internet TCP links! ]] - -97. Think about some way to undo a change. This looks hard given the - current framework of CVS. - 98. If diff3 bombs out (too many differences) cvs then thinks that the file has been updated and is OK to be commited even though the file has not yet been merged. @@ -177,10 +154,8 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 104. cvs should recover nicely on unreadable files/directories. 105. cvs should have administrative tools to allow for changing permissions - and modes and what not. - -106. Need to figure out how to delete and rename directories from a release - and yet have old releases still be accessible. + and modes and what not. In particular, this would make cvs a + more attractive alternative to rdist. 107. It should be possible to specify a list of symbolic revisions to checkout such that the list is processed in reverse order looking for @@ -213,17 +188,8 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ error. I think a later cvs get sys seemed to work so perhaps something is amiss in handling multiple arguments to cvs get? -112. When merging in changes (Glist) and the file ends up exactly as the - RCS revision, an "M" is displayed as the "cvs update" output. This - should really just be a "U". Just an optimization. - 113. The "cvs update" command should tee its output to a log file in ".". - -114. I wanted to check in my metapreen code tonight, which I had put into - a new file called preen.c. So, recalling your excellent instructions, - I typed "cvs add preen.c". However, cvs complained that there was - already a preen.c in /master/etc/fsck/Attic and therefore it wouldn't - take mine. Now what? + (why? What is wrong with piping stdout to "tee"? -kingdon, Jun 1995) 115. I still think "cvs modules" is a good idea. Since everything else is inside cvs, "mkmodules" should be in there too: @@ -239,13 +205,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ "mod -l module ..." Prints more info about relative filenames ("ls -l"?) "mod -f file ..." Tells you what module(s) the filenames are in. -116. The first thing import did was to complain about a missing CVSROOT.adm. - How about having "import()" copy some "CVSROOT.adm/{loginfo,modules}" - templates into place if it discovers none pointed to by $CVSROOT? As it - stands, one has to hand-craft them. It would be real nice to have it - happen automatically. - [[ I hope to add a "cvsinit" command to the installation instructions ]] - 119. Consider an option to have import checkout the RCS or SCCS files if necessary. @@ -294,14 +253,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ number of each file it is checking in. [[ Sort of solved by contrib/log.pl, which does a good job of this ]] -136. Is it possible to specify a command to be run on each file when it is - checked out and another command to be run before it is checked in? - My idea of how this feature would be used: - On checkout: - run indent with user's preferred style - On checkin: - run indent with space-saving, style-free for checkin - 137. Some sites might want CVS to fsync() the RCS ,v file to protect against nasty hardware errors. There is a slight performance hit with doing so, though, so it should be configurable in the .cvsrc file. @@ -313,14 +264,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ variable to limit the amount of sources that need to be duplicated for sites with giant source trees and no disk space. -139. murf@dingus.sps.mot.com (Steve Murphy) suggests adding a mode where - CVS can work across e-mail to a single repository located at some - "known" mail address. The update/commit operations would work through - email aliases, causing them to be slow, but would work nonetheless. - This could allow for very cheap remote development sites. - [[ We may get to TCP connections over the Internet for the next - release, but probably won't do an e-mail linkup right away ]] - 141. Import should accept modules as its directory argument. 143. Update the documentation to show that the source repository is @@ -357,14 +300,8 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 146. The merging of files should be controlled via a hook so that programs other than "rcsmerge" can be used, like Sun's filemerge or emacs's - emerge.el. - -148. It would be nice if cvs import (and perhaps commit when the rcs file - is created) would set the comment leader automagically to the prefix - string of $Log entry, if some option is given. For example, if a file - has a line `%*&# $Log...' the comment leader would be set to `%*&# '. - It would help a lot for unknown files with unknown suffix, and if the - comment leader is not standard. Perhaps for cvs 1.4. + emerge.el. (but be careful in making this work client/server--it means + doing the interactive merging at the end after the server is done). 149. On Sun, 2 Feb 92 22:01:38 EST, rouilj@dl5000.bc.edu (John P. Rouillard) said: @@ -452,33 +389,15 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ 161. The date parser included with CVS (lib/getdate.y) does not support such RCS-supported dates as "1992/03/07". It probably should. -162. We have had a number of cases where some idiot does a "cd" into $CVSROOT - and tries to run checkout. I suggest you make it impossible for someone - to check out anything directly into $CVSROOT. This works (though there - is no error checking): - - getwd(curdir); - chdir(getenv("CVSROOT")); - getwd(cvsrootdir); - strcat(cvsrootdir, "/"); - chdir(curdir); - - if (!strncmp (curdir, cvsrootdir, strlen(cvsrootdir))) { - abort with a nasty message about writing into the repository. - } - - (In other words, if the real path where $CVSROOT is stored is a parent of - the real pathname of your current directory, die horribly.) - 163. The rtag/tag commands should have an option that removes the specified tag from any file that is in the attic. This allows one to re-use a tag (like "Mon", "Tue", ...) all the time and still have it tag the real main-line code. -164. The rcsinfo file should be able to expand environment variables to - find the pathname to the template file. Perhaps it should just - popen("cat <line>"); and read the resulting output, to let the shell - do the dirty work. +164. The *info files should allow multiple ocurrences of $CVSROOT and/or + other cvs variables. They probably should *not* expand environment + variables, as their behavior probably should not depend on who is + running CVS. 165. The "import" command will create RCS files automatically, but will screw-up when trying to create long file names on short file name @@ -500,37 +419,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ where "C2" is a magic branch that resolves to 1.63.2 do not print the same things. Sigh. -168. After making changes to a set of files, some of which were in - sub-directories, I wanted to build a patch file for the whole works: - - cvs diff -c -rPROD-REL . - - However, any diffs for files in sub-directories did not have relative - pathnames. For example, with local changes to perl's hints/aix_rs.sh: - - =================================================================== - RCS file: /local/src-CVS/misc/perl/hints/aix_rs.sh,v - retrieving revision 1.1.1.1 - diff -c -r1.1.1.1 aix_rs.sh - *** 1.1.1.1 1992/12/17 19:43:32 - --- aix_rs.sh 1993/01/05 21:33:12 - *************** - *** 1,3 **** - - It was easy enough to fix in this case, but I'd suggest that the file - name have the relative directory prepended and a proper patch "Index:" - line be added, such as this: - - =================================================================== - RCS file: /local/src-CVS/misc/perl/hints/aix_rs.sh,v - retrieving revision 1.1.1.1 - diff -c -r1.1.1.1 hints/aix_rs.sh - Index: hints/aix_rs.sh - *** 1.1.1.1 1992/12/17 19:43:32 - --- hints/aix_rs.sh 1993/01/05 21:33:12 - *************** - *** 1,3 **** - 169. We are using CVS as the configuration control for a software reuse library. What we do is do system calls passing the needed arguments. In the next release, it would be nice to see an option to put cvs .o files into a @@ -553,19 +441,6 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ the checkout and update commands that could turn this invocation of the script off, for mature users. -171. We have been actively using CVS since September, and we still have the - opinion that automerge is dangerous. We have been burned by it a - couple of times so far, when people missed the notification of a - conflict during an update, and then they committed the files with the - >>>>>>> and <<<<<<< reports in them. This kind of problem usually gets - noticed before commit in compiled files when the compiler croaks, but - we also maintain many documentation files in CVS, and in one case the - problem was not noticed until months later. - -172. "cvs add foo/bar" doesn't work, but "cvs remove foo/bar" works. Maybe - "cvs add" should be rewritten to use the recursive directory code that - most of CVS uses. - 173. We have a tagged branch in CVS. How do we get the version of that branch (for an entire directory) that corresponds to the files on that branch on a certain day? I'd like to specify BOTH -r and -D to 'cvs checkout', but I @@ -595,16 +470,5 @@ $CVSid: @(#)TODO 1.26 94/09/21 $ use "-I" to ignore the directory, as well as .cvsignore, but this doesn't work. -178. At our site we tag all releases of the sw with - product name and version number, the tag is pretty - long and if you by accident write an old tag which - was in use on an old release, the default behaviour - of cvs is to change the old tag!!! - - Could the CVS system reject to reuse an old tag? - You have the possibility to manually remove it, - but you will not have to be afraid of one tag - silently changing. - 179. "cvs admin" does not log its actions with loginfo, nor does it check whether the action is allowed with commitinfo. It should. diff --git a/gnu/usr.bin/cvs/contrib/README b/gnu/usr.bin/cvs/contrib/README index 8d90180..e84b176 100644 --- a/gnu/usr.bin/cvs/contrib/README +++ b/gnu/usr.bin/cvs/contrib/README @@ -1,9 +1,9 @@ $CVSid: @(#)README 1.12 94/09/25 $ This "contrib" directory is a place holder for code/scripts sent to -me by contributors around the world. This READM file will be kept +me by contributors around the world. This README file will be kept up-to-date from release to release. BUT, I must point out that these -contributions are really, REALLY UNSSUPPORTED. In fact, I probably +contributions are really, REALLY UNSUPPORTED. In fact, I probably don't even know what they do. Nor do I guarantee to have tried them, or ported them to work with this CVS distribution. If you have questions, you might contact the author, but you should not necessarily expect @@ -12,7 +12,7 @@ a reply. USE AT YOUR OWN RISK -- and all that stuff. Contents of this directory: README This file. - log.pl A perl script suitable for including in your + log A perl script suitable for including in your $CVSROOT/CVSROOT/loginfo file for logging commit changes. Includes the RCS revision of the change as part of the log. @@ -20,15 +20,15 @@ Contents of this directory: pcl-cvs A directory that contains GNU Emacs lisp code which implements a CVS-mode for emacs. Contributed by Per Cederqvist <ceder@lysator.liu.se>. - commit_prep.pl A perl script, to be combined with log_accum.pl, to - log_accum.pl provide for a way to combine the individual log + commit_prep A perl script, to be combined with log_accum.pl, to + log_accum provide for a way to combine the individual log messages of a multi-directory "commit" into a single log message, and mail the result somewhere. - Also does other checks for $Id and that you are + Can also do other checks for $Id and that you are committing the correct revision of the file. Read the comments carefully. Contributed by David Hampton <hampton@cisco.com>. - mfpipe.pl Another perl script for logging. Allows you to + mfpipe Another perl script for logging. Allows you to pipe the log message to a file and/or send mail to some alias. Contributed by John Clyne <clyne@niwot.scd.ucar.edu>. @@ -46,12 +46,15 @@ Contents of this directory: help your system support opendir/readdir/closedir, if it does not already. Copied from the C-News distribution. - rcslock.pl A perl script that can be added to your commitinfo + rcslock A perl script that can be added to your commitinfo file that tries to determine if your RCS file is currently locked by someone else, as might be the case for a binary file. Contributed by John Rouillard <rouilj@cs.umb.edu>. - cvs_acls.pl A perl script that implements Access Control Lists + ccvs-rsh A Perl script which allows "rsh pipelines" to + be built in order to use Cyclic CVS from + behind some varieties of firewall. + cvs_acls A perl script that implements Access Control Lists by using the "commitinfo" hook provided with the "cvs commit" command. Contributed by David G. Grubbs <dgg@ksr.com>. @@ -62,7 +65,7 @@ Contents of this directory: the commands are recursive. However, this may still come in handy. Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net> - cln_hist.pl A perl script to compress your + cln_hist A perl script to compress your $CVSROOT/CVSROOT/history file, as it can grow quite large after extended use. Contributed by David G. Grubbs <dgg@ksr.com> diff --git a/gnu/usr.bin/cvs/contrib/cln_hist.pl b/gnu/usr.bin/cvs/contrib/cln_hist.pl index 534863d..ff49d0a 100644 --- a/gnu/usr.bin/cvs/contrib/cln_hist.pl +++ b/gnu/usr.bin/cvs/contrib/cln_hist.pl @@ -1,6 +1,7 @@ -#!/usr/bin/perl -- # -*-Perl-*- +#! xPERL_PATHx +# -*-Perl-*- # -# $Id: cln_hist.pl,v 1.1 1992/04/10 03:04:15 berliner Exp $ +# $Id: cln_hist.pl,v 1.2 1995/07/10 02:01:26 kfogel Exp $ # Contributed by David G. Grubbs <dgg@ksr.com> # # Clean up the history file. 10 Record types: MAR OFT WUCG diff --git a/gnu/usr.bin/cvs/contrib/commit_prep.pl b/gnu/usr.bin/cvs/contrib/commit_prep.pl index b3f7e9a..5272c04 100644 --- a/gnu/usr.bin/cvs/contrib/commit_prep.pl +++ b/gnu/usr.bin/cvs/contrib/commit_prep.pl @@ -1,9 +1,11 @@ -#!/usr/local/bin/perl -w +#! xPERL_PATHx +# -*-Perl-*- # +#ident "@(#)cvs/contrib:$Name: $:$Id: commit_prep.pl,v 1.2 1995/07/10 02:01:29 kfogel Exp $" # # Perl filter to handle pre-commit checking of files. This program # records the last directory where commits will be taking place for -# use by the log_accumulate script. For new file, it forcing the +# use by the log_accum.pl script. For new files, it forces the # existence of a RCS "Id" keyword in the first ten lines of the file. # For existing files, it checks version number in the "Id" line to # prevent losing changes because an old version of a file was copied @@ -11,63 +13,62 @@ # # Possible future enhancements: # -# # Check for cruft left by unresolved conflicts. Search for # "^<<<<<<<$", "^-------$", and "^>>>>>>>$". # # Look for a copyright and automagically update it to the -# current year. +# current year. [[ bad idea! -- woods ]] +# # # Contributed by David Hampton <hampton@cisco.com> # +# Hacked on lots by Greg A. Woods <woods@web.net> -############################################################ -# -# Configurable options # -############################################################ -# -# Check each file (except dot files) for an RCS "Id" keyword. +# Configurable options # -$check_id = 1; +# Constants (remember to protect strings from RCS keyword substitution) # -# Record the directory for later use by the log_accumulate stript. -# -$record_directory = 1; +$LAST_FILE = "/tmp/#cvs.lastdir"; # must match name in log_accum.pl +$ENTRIES = "CVS/Entries"; -############################################################ +# Patterns to find $Log keywords in files # -# Constants -# -############################################################ -$LAST_FILE = "/tmp/#cvs.lastdir"; -$ENTRIES = "CVS/Entries"; +$LogString1 = "\\\$\\Log: .* \\\$"; +$LogString2 = "\\\$\\Log\\\$"; +$NoLog = "%s - contains an RCS \$Log keyword. It must not!\n"; +# pattern to match an RCS Id keyword line with an existing ID +# +$IDstring = "\"@\\(#\\)[^:]*:.*\\\$\Id: .*\\\$\""; $NoId = " -%s - Does not contain a line with the keyword \"Id:\". - Please see the template files for an example.\n"; +%s - Does not contain a properly formatted line with the keyword \"Id:\". + I.e. no lines match \"" . $IDstring . "\". + Please see the template files for an example.\n"; + +# pattern to match an RCS Id keyword line for a new file (i.e. un-expanded) +# +$NewId = "\"@(#)[^:]*:.*\\$\Id\\$\""; -# Protect string from substitution by RCS. $NoName = " -%s - The ID line should contain only \"\$\I\d\:\ \$\" for a newly created file.\n"; +%s - The ID line should contain only \"@(#)module/path:\$Name\$:\$\Id\$\" + for a newly created file.\n"; $BadName = " %s - The file name '%s' in the ID line does not match - the actual filename.\n"; + the actual filename.\n"; $BadVersion = " -%s - How dare you!! You replaced your copy of the file '%s', - which was based upon version %s, with an %s version based - upon %s. Please move your '%s' out of the way, perform an - update to get the current version, and them merge your changes - into that file.\n"; +%s - How dare you!!! You replaced your copy of the file '%s', + which was based upon version %s, with an %s version based + upon %s. Please move your '%s' out of the way, perform an + update to get the current version, and them merge your changes + into that file, then try the commit again.\n"; -############################################################ # -# Subroutines +# Subroutines # -############################################################ sub write_line { local($filename, $line) = @_; @@ -80,58 +81,107 @@ sub check_version { local($i, $id, $rname, $version); local($filename, $cvsversion) = @_; - open(FILE, $filename) || die("Cannot open $filename, stopped"); - for ($i = 1; $i < 10; $i++) { - $pos = -1; - last if eof(FILE); - $line = <FILE>; - $pos = index($line, "Id: "); - last if ($pos >= 0); + open(FILE, "<$filename") || return(0); + + @all_lines = (); + $idpos = -1; + $newidpos = -1; + for ($i = 0; <FILE>; $i++) { + chop; + push(@all_lines, $_); + if ($_ =~ /$IDstring/) { + $idpos = $i; + } + if ($_ =~ /$NewId/) { + $newidpos = $i; + } } - if ($pos == -1) { - printf($NoId, $filename); + if (grep(/$LogString1/, @all_lines) || grep(/$LogString2/, @all_lines)) { + print STDERR sprintf($NoLog, $filename); return(1); } - ($id, $rname, $version) = split(' ', substr($line, $pos)); + if ($debug != 0) { + print STDERR sprintf("file = %s, version = %d.\n", $filename, $cvsversion{$filename}); + } + if ($cvsversion{$filename} == 0) { - if ($rname ne "\$") { - printf($NoName, $filename); + if ($newidpos != -1 && $all_lines[$newidpos] !~ /$NewId/) { + print STDERR sprintf($NoName, $filename); return(1); } return(0); } + if ($idpos == -1) { + print STDERR sprintf($NoId, $filename); + return(1); + } + + $line = $all_lines[$idpos]; + $pos = index($line, "Id: "); + if ($debug != 0) { + print STDERR sprintf("%d in '%s'.\n", $pos, $line); + } + ($id, $rname, $version) = split(' ', substr($line, $pos)); if ($rname ne "$filename,v") { - printf($BadName, $filename, substr($rname, 0, length($rname)-2)); + print STDERR sprintf($BadName, $filename, substr($rname, 0, length($rname)-2)); return(1); } if ($cvsversion{$filename} < $version) { - printf($BadVersion, $filename, $filename, $cvsversion{$filename}, - "newer", $version, $filename); + print STDERR sprintf($BadVersion, $filename, $filename, $cvsversion{$filename}, + "newer", $version, $filename); return(1); } if ($cvsversion{$filename} > $version) { - printf($BadVersion, $filename, $filename, $cvsversion{$filename}, - "older", $version, $filename); + print STDERR sprintf($BadVersion, $filename, $filename, $cvsversion{$filename}, + "older", $version, $filename); return(1); } return(0); } -############################################################# # -# Main Body +# Main Body +# + +$id = getpgrp(); # You *must* use a shell that does setpgrp()! + +# Check each file (except dot files) for an RCS "Id" keyword. # -############################################################ +$check_id = 0; -$id = getpgrp(); -#print("ARGV - ", join(":", @ARGV), "\n"); -#print("id - ", id, "\n"); +# Record the directory for later use by the log_accumulate stript. +# +$record_directory = 0; +# parse command line arguments # -# Suck in the Entries file +while (@ARGV) { + $arg = shift @ARGV; + + if ($arg eq '-d') { + $debug = 1; + print STDERR "Debug turned on...\n"; + } elsif ($arg eq '-c') { + $check_id = 1; + } elsif ($arg eq '-r') { + $record_directory = 1; + } else { + push(@files, $arg); + } +} + +$directory = shift @files; + +if ($debug != 0) { + print STDERR "dir - ", $directory, "\n"; + print STDERR "files - ", join(":", @files), "\n"; + print STDERR "id - ", $id, "\n"; +} + +# Suck in the CVS/Entries file # open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n"); while (<ENTRIES>) { @@ -139,25 +189,23 @@ while (<ENTRIES>) { $cvsversion{$filename} = $version; } -# # Now check each file name passed in, except for dot files. Dot files # are considered to be administrative files by this script. # if ($check_id != 0) { $failed = 0; - $directory = $ARGV[0]; - shift @ARGV; - foreach $arg (@ARGV) { - next if (index($arg, ".") == 0); + foreach $arg (@files) { + if (index($arg, ".") == 0) { + next; + } $failed += &check_version($arg); } if ($failed) { - print "\n"; + print STDERR "\n"; exit(1); } } -# # Record this directory as the last one checked. This will be used # by the log_accumulate script to determine when it is processing # the final directory of a multi-directory commit. diff --git a/gnu/usr.bin/cvs/contrib/cvs_acls.pl b/gnu/usr.bin/cvs/contrib/cvs_acls.pl index 2a3b0d9..bcb544d 100644 --- a/gnu/usr.bin/cvs/contrib/cvs_acls.pl +++ b/gnu/usr.bin/cvs/contrib/cvs_acls.pl @@ -1,6 +1,7 @@ -#!/usr/bin/perl -- # -*-Perl-*- +#! xPERL_PATHx +# -*-Perl-*- # -# $Id: cvs_acls.pl,v 1.2 1992/04/11 16:01:24 berliner Exp $ +# $Id: cvs_acls.pl,v 1.2 1995/07/10 02:01:33 kfogel Exp $ # # Access control lists for CVS. dgg@ksr.com (David G. Grubbs) # diff --git a/gnu/usr.bin/cvs/contrib/cvscheck.man b/gnu/usr.bin/cvs/contrib/cvscheck.man index 84d7db7..61a064a 100644 --- a/gnu/usr.bin/cvs/contrib/cvscheck.man +++ b/gnu/usr.bin/cvs/contrib/cvscheck.man @@ -1,4 +1,4 @@ -.\" $Id: cvscheck.man,v 1.1 1992/04/10 03:04:20 berliner Exp $ +.\" $Id: cvscheck.man,v 1.1.1.3 1995/08/28 16:20:24 jimb Exp $ .\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net> .TH CVSCHECK LOCAL "4 March 1991" FLUKE .SH NAME diff --git a/gnu/usr.bin/cvs/contrib/cvshelp.man b/gnu/usr.bin/cvs/contrib/cvshelp.man index ba6590a..2cfae1f 100644 --- a/gnu/usr.bin/cvs/contrib/cvshelp.man +++ b/gnu/usr.bin/cvs/contrib/cvshelp.man @@ -1,4 +1,4 @@ -.\" $Id: cvshelp.man,v 1.1 1992/04/10 03:04:21 berliner Exp $ +.\" $Id: cvshelp.man,v 1.1.1.3 1995/08/28 16:20:28 jimb Exp $ .\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net> .\" Full space in nroff; half space in troff .de SP diff --git a/gnu/usr.bin/cvs/contrib/descend.man b/gnu/usr.bin/cvs/contrib/descend.man index a38d4ca..5ac46f4 100644 --- a/gnu/usr.bin/cvs/contrib/descend.man +++ b/gnu/usr.bin/cvs/contrib/descend.man @@ -1,4 +1,4 @@ -.\" $Id: descend.man,v 1.1 1992/04/03 05:22:53 berliner Exp $ +.\" $Id: descend.man,v 1.1.1.3 1995/08/28 16:20:31 jimb Exp $ .TH DESCEND 1 "31 March 1992" .SH NAME descend \- walk directory tree and execute a command at each node diff --git a/gnu/usr.bin/cvs/contrib/log.pl b/gnu/usr.bin/cvs/contrib/log.pl index d68cc82..5e3bf48 100644 --- a/gnu/usr.bin/cvs/contrib/log.pl +++ b/gnu/usr.bin/cvs/contrib/log.pl @@ -1,25 +1,31 @@ -#! /local/bin/perl - -# Modified by woods@web.apc.org to add support for mailing 3/29/93 -# use '-m user' for each user to receive cvs log reports -# and use '-f logfile' for the logfile to append to +#! xPERL_PATHx +# -*-Perl-*- +# +#ident "$CVSid$" # -# Modified by berliner@Sun.COM to add support for CVS 1.3 2/27/92 +# XXX: FIXME: handle multiple '-f logfile' arguments # -# Date: Tue, 6 Aug 91 13:27 EDT -# From: samborn@sunrise.com (Kevin Samborn) +# XXX -- I HATE Perl! This *will* be re-written in shell/awk/sed soon! # -# I revised the perl script I sent you yesterday to use the info you -# send in on stdin. (I am appending the newer script to the end) + +# Usage: log.pl [[-m user] ...] [-s] -f logfile 'dirname file ...' +# +# -m user - for each user to receive cvs log reports +# (multiple -m's permitted) +# -s - to prevent "cvs status -v" messages +# -f logfile - for the logfile to append to (mandatory, +# but only one logfile can be specified). + +# here is what the output looks like: # -# now the output looks like this: +# From: woods@kuma.domain.top +# Subject: CVS update: testmodule # -# ************************************** -# Date: Tuesday, August 6, 1991 @ 13:17 -# Author: samborn +# Date: Wednesday November 23, 1994 @ 14:15 +# Author: woods # -# Update of /elmer/cvs/CVSROOT.adm -# In directory astro:/home/samborn/CVSROOT.adm +# Update of /local/src-CVS/testmodule +# In directory kuma:/home/kuma/woods/work.d/testmodule # # Modified Files: # test3 @@ -28,15 +34,24 @@ # Removed Files: # test4 # Log Message: -# wow, what a test -# -# File: test.3 Status: Up-to-date -# Version: 1.4 Thu Apr 29 14:47:07 EDT 1993 -# File: test6 Status: Up-to-date -# Version: 1.1 Thu Apr 29 14:47:33 EDT 1993 -# File: test4 Status: Up-to-date -# Version: 1.1 Thu Apr 29 14:47:46 EDT 1993 +# - wow, what a test +# +# (and for each file the "cvs status -v" output is appended unless -s is used) # +# ================================================================== +# File: test3 Status: Up-to-date +# +# Working revision: 1.41 Wed Nov 23 14:15:59 1994 +# Repository revision: 1.41 /local/src-CVS/cvs/testmodule/test3,v +# Sticky Options: -ko +# +# Existing Tags: +# local-v2 (revision: 1.7) +# local-v1 (revision: 1.1.1.2) +# CVS-1_4A2 (revision: 1.1.1.2) +# local-v0 (revision: 1.2) +# CVS-1_4A1 (revision: 1.1.1.1) +# CVS (branch: 1.1.1) $cvsroot = $ENV{'CVSROOT'}; @@ -44,6 +59,8 @@ $cvsroot = $ENV{'CVSROOT'}; # $) = $(; +$dostatus = 1; + # parse command line arguments # while (@ARGV) { @@ -54,6 +71,8 @@ while (@ARGV) { } elsif ($arg eq '-f') { ($logfile) && die "Too many '-f' args"; $logfile = shift @ARGV; + } elsif ($arg eq '-s') { + $dostatus = 0; } else { ($donefiles) && die "Too many arguments!\n"; $donefiles = 1; @@ -61,10 +80,13 @@ while (@ARGV) { } } -$srepos = shift @files; -$mailcmd = "| Mail -s 'CVS update: $srepos'"; +# the first argument is the module location relative to $CVSROOT +# +$modulepath = shift @files; + +$mailcmd = "| Mail -s 'CVS update: $modulepath'"; -# Some date and time arrays +# Initialise some date and time arrays # @mos = (January,February,March,April,May,June,July,August,September, October,November,December); @@ -72,13 +94,16 @@ $mailcmd = "| Mail -s 'CVS update: $srepos'"; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime; -# get login name +# get a login name for the guy doing the commit.... # $login = getlogin || (getpwuid($<))[0] || "nobody"; # open log file for appending # open(OUT, ">>" . $logfile) || die "Could not open(" . $logfile . "): $!\n"; + +# send mail, if there's anyone to send to! +# if ($users) { $mailcmd = "$mailcmd $users"; open(MAIL, $mailcmd) || die "Could not Exec($mailcmd): $!\n"; @@ -87,7 +112,7 @@ if ($users) { # print out the log Header # print OUT "\n"; -print OUT "**************************************\n"; +print OUT "****************************************\n"; print OUT "Date:\t$days[$wday] $mos[$mon] $mday, 19$year @ $hour:" . sprintf("%02d", $min) . "\n"; print OUT "Author:\t$login\n\n"; @@ -110,29 +135,27 @@ close(IN); print OUT "\n"; -# after log information, do an 'cvs -Qn status' on each file in the arguments. +# after log information, do an 'cvs -Qq status -v' on each file in the arguments. # -while (@files) { - $file = shift @files; - if ($file eq "-") { - print OUT "[input file was '-']\n"; - if (MAIL) { - print MAIL "[input file was '-']\n"; +if ($dostatus != 0) { + while (@files) { + $file = shift @files; + if ($file eq "-") { + print OUT "[input file was '-']\n"; + if (MAIL) { + print MAIL "[input file was '-']\n"; + } + last; } - last; - } - - open(RCS, "-|") || exec 'cvs', '-Qn', 'status', $file; - - while (<RCS>) { - if (/^[ \t]*Version/ || /^File:/) { + open(RCS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $file; + while (<RCS>) { print OUT; if (MAIL) { print MAIL; } } + close(RCS); } - close(RCS); } close(OUT); @@ -141,8 +164,6 @@ die "Write to $logfile failed" if $?; close(MAIL); die "Pipe to $mailcmd failed" if $?; +## must exit cleanly +## exit 0; - -### Local Variables: -### eval: (fundamental-mode) -### End: diff --git a/gnu/usr.bin/cvs/contrib/log_accum.pl b/gnu/usr.bin/cvs/contrib/log_accum.pl index 798e25f..b47f433 100644 --- a/gnu/usr.bin/cvs/contrib/log_accum.pl +++ b/gnu/usr.bin/cvs/contrib/log_accum.pl @@ -1,4 +1,5 @@ -#!/usr/local/bin/perl -w +#! xPERL_PATHx +# -*-Perl-*- # # Perl filter to handle the log messages from the checkin of files in # a directory. This script will group the lists of files by log @@ -10,27 +11,27 @@ # # Contributed by David Hampton <hampton@cisco.com> # +# hacked greatly by Greg A. Woods <woods@planix.com> + +# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [-f logfile] +# -d - turn on debugging +# -m mailto - send mail to "mailto" (multiple) +# -M modulename - set module name to "modulename" +# -f logfile - write commit messages to logfile too +# -s - *don't* run "cvs status -v" for each file -############################################################ -# -# Configurable options -# -############################################################ # -# Do cisco Systems, Inc. specific nonsense. +# Configurable options # -$cisco_systems = 1; + +$MAILER = "Mail"; # set this to something that takes "-s" # -# Recipient of all mail messages +# End user configurable options. # -$mailto = "sw-notification@cisco.com"; -############################################################ -# -# Constants +# Constants (don't change these!) # -############################################################ $STATE_NONE = 0; $STATE_CHANGED = 1; $STATE_ADDED = 2; @@ -38,82 +39,113 @@ $STATE_REMOVED = 3; $STATE_LOG = 4; $LAST_FILE = "/tmp/#cvs.lastdir"; + $CHANGED_FILE = "/tmp/#cvs.files.changed"; $ADDED_FILE = "/tmp/#cvs.files.added"; $REMOVED_FILE = "/tmp/#cvs.files.removed"; $LOG_FILE = "/tmp/#cvs.files.log"; -$FILE_PREFIX = "#cvs.files"; -$VERSION_FILE = "version"; -$TRUNKREV_FILE = "TrunkRev"; -$CHANGES_FILE = "Changes"; -$CHANGES_TEMP = "Changes.tmp"; +$FILE_PREFIX = "#cvs.files"; -############################################################ # -# Subroutines +# Subroutines # -############################################################ - -sub format_names { - local($dir, @files) = @_; - local(@lines); - $lines[0] = sprintf(" %-08s", $dir); - foreach $file (@files) { - if (length($lines[$#lines]) + length($file) > 60) { - $lines[++$#lines] = sprintf(" %8s", " "); - } - $lines[$#lines] .= " ".$file; - } - @lines; -} sub cleanup_tmpfiles { - local($all) = @_; local($wd, @files); $wd = `pwd`; - chdir("/tmp"); + chdir("/tmp") || die("Can't chdir('/tmp')\n"); opendir(DIR, "."); - if ($all == 1) { - push(@files, grep(/$id$/, readdir(DIR))); - } else { - push(@files, grep(/^$FILE_PREFIX.*$id$/, readdir(DIR))); - } + push(@files, grep(/^$FILE_PREFIX\..*\.$id$/, readdir(DIR))); closedir(DIR); foreach (@files) { unlink $_; } + unlink $LAST_FILE . "." . $id; + chdir($wd); } sub write_logfile { local($filename, @lines) = @_; - open(FILE, ">$filename") || die ("Cannot open log file $filename.\n"); - print(FILE join("\n", @lines), "\n"); + + open(FILE, ">$filename") || die("Cannot open log file $filename.\n"); + print FILE join("\n", @lines), "\n"; + close(FILE); +} + +sub append_to_logfile { + local($filename, @lines) = @_; + + open(FILE, ">$filename") || die("Cannot open log file $filename.\n"); + print FILE join("\n", @lines), "\n"; close(FILE); } -sub append_to_file { +sub format_names { + local($dir, @files) = @_; + local(@lines); + + $format = "\t%-" . sprintf("%d", length($dir)) . "s%s "; + + $lines[0] = sprintf($format, $dir, ":"); + + if ($debug) { + print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n"; + } + foreach $file (@files) { + if (length($lines[$#lines]) + length($file) > 65) { + $lines[++$#lines] = sprintf($format, " ", " "); + } + $lines[$#lines] .= $file . " "; + } + + @lines; +} + +sub format_lists { + local(@lines) = @_; + local(@text, @files, $lastdir); + + if ($debug) { + print STDERR "format_lists(): ", join(":", @lines), "\n"; + } + @text = (); + @files = (); + $lastdir = shift @lines; # first thing is always a directory + if ($lastdir !~ /.*\/$/) { + die("Damn, $lastdir doesn't look like a directory!\n"); + } + foreach $line (@lines) { + if ($line =~ /.*\/$/) { + push(@text, &format_names($lastdir, @files)); + $lastdir = $line; + @files = (); + } else { + push(@files, $line); + } + } + push(@text, &format_names($lastdir, @files)); + + @text; +} + +sub append_names_to_file { local($filename, $dir, @files) = @_; + if (@files) { - local(@lines) = &format_names($dir, @files); - open(FILE, ">>$filename") || die ("Cannot open file $filename.\n"); - print(FILE join("\n", @lines), "\n"); + open(FILE, ">>$filename") || die("Cannot open file $filename.\n"); + print FILE $dir, "\n"; + print FILE join("\n", @files), "\n"; close(FILE); } } -sub write_line { - local($filename, $line) = @_; - open(FILE, ">$filename") || die("Cannot open file $filename.\n"); - print(FILE $line, "\n"); - close(FILE); -} - sub read_line { local($line); local($filename) = @_; + open(FILE, "<$filename") || die("Cannot open file $filename.\n"); $line = <FILE>; close(FILE); @@ -121,23 +153,11 @@ sub read_line { $line; } -sub read_file { - local(@text); - local($filename, $leader) = @_; - open(FILE, "<$filename") || return (); - while (<FILE>) { - chop; - push(@text, sprintf(" %-10s %s", $leader, $_)); - $leader = ""; - } - close(FILE); - @text; -} - sub read_logfile { local(@text); local($filename, $leader) = @_; - open(FILE, "<$filename") || die ("Cannot open log file $filename.\n"); + + open(FILE, "<$filename"); while (<FILE>) { chop; push(@text, $leader.$_); @@ -146,110 +166,179 @@ sub read_logfile { @text; } -sub bump_version { - local($trunkrev, $editnum, $version); - - $trunkrev = &read_line("$ENV{'CVSROOT'}/$repository/$TRUNKREV_FILE"); - $editnum = &read_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE"); - &write_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE", $editnum+1); - $version = $trunkrev . "(" . $editnum . ")"; -} - sub build_header { - local($version) = @_; local($header); local($sec,$min,$hour,$mday,$mon,$year) = localtime(time); - $header = sprintf("%-8s %s %02d/%02d/%02d %02d:%02d:%02d", - $login, $version, $year%100, $mon+1, $mday, - $hour, $min, $sec); -} - -sub do_changes_file { - local($changes, $tmpchanges); - local(@text) = @_; - - $changes = "$ENV{'CVSROOT'}/$repository/$CHANGES_FILE"; - $tmpchanges = "$ENV{'CVSROOT'}/$repository/$CHANGES_TEMP"; - if (rename($changes, $tmpchanges) != 1) { - die("Cannot rename $changes to $tmpchanges.\n"); - } - open(CHANGES, ">$changes") || die("Cannot open $changes.\n"); - open(TMPCHANGES, "<$tmpchanges") || die("Cannot open $tmpchanges.\n"); - print(CHANGES join("\n", @text), "\n\n"); - print(CHANGES <TMPCHANGES>); - close(CHANGES); - close(TMPCHANGES); - unlink($tmpchanges); + $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\nChanges by:\t%s@%s\t%02d/%02d/%02d %02d:%02d:%02d", + $cvsroot, + $modulename, + $login, $hostdomain, + $year%100, $mon+1, $mday, + $hour, $min, $sec); } sub mail_notification { local($name, @text) = @_; - open(MAIL, "| mail -s \"Source Repository Modification\" $name"); - print(MAIL join("\n", @text)); + open(MAIL, "| $MAILER -s \"CVS Update: " . $modulename . "\" " . $name); + print MAIL join("\n", @text), "\n"; close(MAIL); } -############################################################# +sub write_commitlog { + local($logfile, @text) = @_; + + open(FILE, ">>$logfile"); + print FILE join("\n", @text), "\n"; + close(FILE); +} + # -# Main Body +# Main Body # -############################################################ -# # Initialize basic variables # -$id = getpgrp(); +$debug = 0; +$id = getpgrp(); # note, you *must* use a shell which does setpgrp() $state = $STATE_NONE; -$login = getlogin || (getpwuid($<))[0] || die("Unknown user $<.\n"); -@files = split(' ', $ARGV[0]); +$login = getlogin || (getpwuid($<))[0] || "nobody"; +chop($hostname = `hostname`); +chop($domainname = `domainname`); +$hostdomain = $hostname . $domainname; +$cvsroot = $ENV{'CVSROOT'}; +$do_status = 1; +$modulename = ""; + +# parse command line arguments (file list is seen as one arg) +# +while (@ARGV) { + $arg = shift @ARGV; + + if ($arg eq '-d') { + $debug = 1; + print STDERR "Debug turned on...\n"; + } elsif ($arg eq '-m') { + $mailto = "$mailto " . shift @ARGV; + } elsif ($arg eq '-M') { + $modulename = shift @ARGV; + } elsif ($arg eq '-s') { + $do_status = 0; + } elsif ($arg eq '-f') { + ($commitlog) && die("Too many '-f' args\n"); + $commitlog = shift @ARGV; + } else { + ($donefiles) && die("Too many arguments! Check usage.\n"); + $donefiles = 1; + @files = split(/ /, $arg); + } +} +($mailto) || die("No -m mail recipient specified\n"); + +# for now, the first "file" is the repository directory being committed, +# relative to the $CVSROOT location +# @path = split('/', $files[0]); -$repository = @path[0]; + +# XXX there are some ugly assumptions in here about module names and +# XXX directories relative to the $CVSROOT location -- really should +# XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since +# XXX we have to parse it backwards. +# +if ($modulename eq "") { + $modulename = $path[0]; # I.e. the module name == top-level dir +} if ($#path == 0) { $dir = "."; } else { - $dir = join('/', @path[1..$#path]); + $dir = join('/', @path); } -#print("ARGV - ", join(":", @ARGV), "\n"); -#print("files - ", join(":", @files), "\n"); -#print("path - ", join(":", @path), "\n"); -#print("dir - ", $dir, "\n"); -#print("id - ", $id, "\n"); +$dir = $dir . "/"; +if ($debug) { + print STDERR "module - ", $modulename, "\n"; + print STDERR "dir - ", $dir, "\n"; + print STDERR "path - ", join(":", @path), "\n"; + print STDERR "files - ", join(":", @files), "\n"; + print STDERR "id - ", $id, "\n"; +} + +# Check for a new directory first. This appears with files set as follows: # -# Check for a new directory first. This will always appear as a -# single item in the argument list, and an empty log message. +# files[0] - "path/name/newdir" +# files[1] - "-" +# files[2] - "New" +# files[3] - "directory" # -if ($ARGV[0] =~ /New directory/) { - $version = &bump_version if ($cisco_systems != 0); - $header = &build_header($version); +if ($files[2] =~ /New/ && $files[3] =~ /directory/) { + local(@text); + @text = (); - push(@text, $header); + push(@text, &build_header()); + push(@text, ""); + push(@text, $files[0]); push(@text, ""); - push(@text, " ".$ARGV[0]); - &do_changes_file(@text) if ($cisco_systems != 0); + + while (<STDIN>) { + chop; # Drop the newline + push(@text, $_); + } + &mail_notification($mailto, @text); + exit 0; } +# Check for an import command. This appears with files set as follows: +# +# files[0] - "path/name" +# files[1] - "-" +# files[2] - "Imported" +# files[3] - "sources" # +if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) { + local(@text); + + @text = (); + push(@text, &build_header()); + push(@text, ""); + push(@text, $files[0]); + push(@text, ""); + + while (<STDIN>) { + chop; # Drop the newline + push(@text, $_); + } + + &mail_notification($mailto, @text); + + exit 0; +} + # Iterate over the body of the message collecting information. # while (<STDIN>) { chop; # Drop the newline + + if (/^In directory/) { + push(@log_lines, $_); + push(@log_lines, ""); + next; + } + if (/^Modified Files/) { $state = $STATE_CHANGED; next; } if (/^Added Files/) { $state = $STATE_ADDED; next; } if (/^Removed Files/) { $state = $STATE_REMOVED; next; } if (/^Log Message/) { $state = $STATE_LOG; next; } - s/^[ \t\n]+//; # delete leading space - s/[ \t\n]+$//; # delete trailing space + + s/^[ \t\n]+//; # delete leading whitespace + s/[ \t\n]+$//; # delete trailing whitespace - push (@changed_files, split) if ($state == $STATE_CHANGED); - push (@added_files, split) if ($state == $STATE_ADDED); - push (@removed_files, split) if ($state == $STATE_REMOVED); - push (@log_lines, $_) if ($state == $STATE_LOG); + if ($state == $STATE_CHANGED) { push(@changed_files, split); } + if ($state == $STATE_ADDED) { push(@added_files, split); } + if ($state == $STATE_REMOVED) { push(@removed_files, split); } + if ($state == $STATE_LOG) { push(@log_lines, $_); } } -# # Strip leading and trailing blank lines from the log message. Also # compress multiple blank lines in the body of the message down to a # single blank line. @@ -268,64 +357,140 @@ for ($i = $#log_lines; $i > 0; $i--) { } } -# -# Find the log file that matches this log message +if ($debug) { + print STDERR "Searching for log file index..."; +} +# Find an index to a log file that matches this log message # for ($i = 0; ; $i++) { - last if (! -e "$LOG_FILE.$i.$id"); + local(@text); + + last if (! -e "$LOG_FILE.$i.$id"); # the next available one @text = &read_logfile("$LOG_FILE.$i.$id", ""); - last if ($#text == -1); - last if (join(" ", @log_lines) eq join(" ", @text)); + last if ($#text == -1); # nothing in this file, use it + last if (join(" ", @log_lines) eq join(" ", @text)); # it's the same log message as another +} +if ($debug) { + print STDERR " found log file at $i.$id, now writing tmp files.\n"; } -# # Spit out the information gathered in this pass. # +&append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files); +&append_names_to_file("$ADDED_FILE.$i.$id", $dir, @added_files); +&append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files); &write_logfile("$LOG_FILE.$i.$id", @log_lines); -&append_to_file("$ADDED_FILE.$i.$id", $dir, @added_files); -&append_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files); -&append_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files); -# # Check whether this is the last directory. If not, quit. # +if ($debug) { + print STDERR "Checking current dir against last dir.\n"; +} $_ = &read_line("$LAST_FILE.$id"); -exit 0 if (! grep(/$files[0]$/, $_)); +if ($_ ne $cvsroot . "/" . $files[0]) { + if ($debug) { + print STDERR sprintf("Current directory %s is not last directory %s.\n", $cvsroot . "/" .$files[0], $_); + } + exit 0; +} +if ($debug) { + print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0], $_); +} + +# +# End Of Commits! # + # This is it. The commits are all finished. Lump everything together # into a single message, fire a copy off to the mailing list, and drop # it on the end of the Changes file. # -# Get the full version number -# -$version = &bump_version if ($cisco_systems != 0); -$header = &build_header($version); # # Produce the final compilation of the log messages # @text = (); -push(@text, $header); +@status_txt = (); +push(@text, &build_header()); push(@text, ""); + for ($i = 0; ; $i++) { - last if (! -e "$LOG_FILE.$i.$id"); - push(@text, &read_file("$CHANGED_FILE.$i.$id", "Modified:")); - push(@text, &read_file("$ADDED_FILE.$i.$id", "Added:")); - push(@text, &read_file("$REMOVED_FILE.$i.$id", "Removed:")); - push(@text, " Log:"); - push(@text, &read_logfile("$LOG_FILE.$i.$id", " ")); - push(@text, ""); -} -if ($cisco_systems != 0) { - @ddts = grep(/^CSCdi/, split(' ', join(" ", @text))); - $text[0] .= " " . join(" ", @ddts); + last if (! -e "$LOG_FILE.$i.$id"); # we're done them all! + @lines = &read_logfile("$CHANGED_FILE.$i.$id", ""); + if ($#lines >= 0) { + push(@text, "Modified files:"); + push(@text, &format_lists(@lines)); + } + @lines = &read_logfile("$ADDED_FILE.$i.$id", ""); + if ($#lines >= 0) { + push(@text, "Added files:"); + push(@text, &format_lists(@lines)); + } + @lines = &read_logfile("$REMOVED_FILE.$i.$id", ""); + if ($#lines >= 0) { + push(@text, "Removed files:"); + push(@text, &format_lists(@lines)); + } + if ($#text >= 0) { + push(@text, ""); + } + @lines = &read_logfile("$LOG_FILE.$i.$id", "\t"); + if ($#lines >= 0) { + push(@text, "Log message:"); + push(@text, @lines); + push(@text, ""); + } + if ($do_status) { + local(@changed_files); + + @changed_files = (); + push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", "")); + push(@changed_files, &read_logfile("$ADDED_FILE.$i.$id", "")); + push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", "")); + + if ($debug) { + print STDERR "main: pre-sort changed_files = ", join(":", @changed_files), ".\n"; + } + sort(@changed_files); + if ($debug) { + print STDERR "main: post-sort changed_files = ", join(":", @changed_files), ".\n"; + } + + foreach $dofile (@changed_files) { + if ($dofile =~ /\/$/) { + next; # ignore the silly "dir" entries + } + if ($debug) { + print STDERR "main(): doing 'cvs -nQq status -v $dofile'\n"; + } + open(STATUS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $dofile; + while (<STATUS>) { + chop; + push(@status_txt, $_); + } + } + } } + +# Write to the commitlog file # -# Put the log message at the beginning of the Changes file and mail -# out the notification. +if ($commitlog) { + &write_commitlog($commitlog, @text); +} + +if ($#status_txt >= 0) { + push(@text, @status_txt); +} + +# Mailout the notification. # -&do_changes_file(@text) if ($cisco_systems != 0); &mail_notification($mailto, @text); -&cleanup_tmpfiles(1); + +# cleanup +# +if (! $debug) { + &cleanup_tmpfiles(); +} + exit 0; diff --git a/gnu/usr.bin/cvs/contrib/mfpipe.pl b/gnu/usr.bin/cvs/contrib/mfpipe.pl index 3e6357d..bae7a72 100644 --- a/gnu/usr.bin/cvs/contrib/mfpipe.pl +++ b/gnu/usr.bin/cvs/contrib/mfpipe.pl @@ -1,4 +1,5 @@ -#!/usr/bin/perl +#! xPERL_PATHx +# -*-Perl-*- # # From: clyne@niwot.scd.ucar.EDU (John Clyne) # Date: Fri, 28 Feb 92 09:54:21 MST @@ -11,7 +12,7 @@ # Especially if they regularly beat on the same directory. Anyway if you # think anyone would be interested here it is. # -# $Id: mfpipe.pl,v 1.1 1992/03/02 01:22:41 berliner Exp $ +# $Id: mfpipe.pl,v 1.2 1995/07/10 02:01:57 kfogel Exp $ # # # File: mfpipe diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog index fad2eb1..ac24f44 100644 --- a/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog +++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog @@ -1,3 +1,109 @@ +Wed Nov 22 11:01:50 1995 Joshua Cowan <jcowan@hermit.reslife.okstate.edu> + + * pcl-cvs.el (cvs-changelog-ours-p): use `user-full-name' if + `add-log-full-name' unbound, as not every uses the stuff in + add-log.el. Same with `add-log-mailing-address'. + (cvs-changelog-entries): change to `change-log-mode' unless + already in it. + +Sun Jul 9 20:57:11 1995 Karl Fogel <kfogel@floss.cyclic.com> + + * "/bin/rmdir" as default, not "/usr/local/bin/rmdir". + +Fri Jun 16 15:24:34 1995 Jim Kingdon (kingdon@cyclic.com) + + * pcl-cvs.elc, pcl-cvs-lucid.elc: Added. + + * Makefile.in: Rename from Makefile and set srcdir. + +Thu May 18 17:10:27 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + Automatically guess CVS log entries from ChangeLog contents. + * pcl-cvs.el (cvs-mode-changelog-commit): New command. + (cvs-changelog-full-paragraphs): New variable. + (cvs-changelog-name, cvs-narrow-changelog, + cvs-changelog-paragraph, cvs-changelog-subparagraph, + cvs-changelog-entry, cvs-changelog-ours-p, cvs-relative-path, + cvs-changelog-entries, cvs-changelog-insert-entries, cvs-union, + cvs-insert-changelog-entries, cvs-edit-delete-common-indentation): + New functions. + (cvs-mode-map): Bind 'C' to cvs-mode-changelog-commit. + (cvs-mode): Mention cvs-mode-changelog-commit in docstring. + + Give the info files names ending in ".info". + * Makefile (INFOFILES, install_info): Change pcl-cvs to + pcl-cvs.info. + (pcl-cvs.info): Target renamed from pcl-cvs. + (DISTFILES): pcl-cvs removed; we handle the info files explicitly + in the dist-dir target. + (dist-dir): Depend on pcl-cvs.info. Distribute pcl-cvs.info*. + * pcl-cvs.texinfo: Change @setfilename appropriately. + * INSTALL: Updated. + * .cvsignore: Correctly ignore the info files. + + * README: Note that pcl-cvs has been tested under 19.28, and that + the "cookie" naming conflict was resolved in 19.11. + + * Makefile (pcl-cvs-lucid.elc): Changed this target from + pcl-cvs-lucid.el. That's a source file, for goodness' sake! + +Tue May 9 13:56:50 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * Change references to "Cygnus's remote CVS" to "Cyclic CVS". + +Wed May 3 13:55:27 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el (cvs-parse-stderr): Handle colons after both + "rcsmerge" and "warning". + +Fri Apr 28 22:38:14 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * Makefile (ELFILES): Include pcl-cvs-startup.el. + (info, pcl-cvs): Call makeinfo appropriately for modern versions. + (pcl-cvs.aux): List dependency on pcl-cvs.texinfo. + (pcl-cvs.ps): New target. + (DVIPS): New variable. + (dist-dir): Renamed from dist, updated to accept DISTDIR value + passed from parent. + (DISTFILES): New varible. + (pcl-cvs.elc, pcl-cvs-lucid.elc): Add targets to elcfiles target. + +Tue Apr 25 21:33:49 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el: (cvs-parse-stderr): Recognize "conflicts" as well as + "overlaps" before "during merge." + +Thu Feb 16 12:17:20 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el (cvs-parse-stderr): Recognize "conflicts found in..." + messages attributed to "cvs server", as well as "cvs update". + +Sat Feb 4 01:47:01 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el: Deal with the 'P' action, produced by remote CVS. + (cvs-parse-stdout): Treat 'P' like 'U' --- file is updated. + +Tue Jan 31 23:31:39 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el (cvs-cvsroot-required): New variable. + (cvs-do-update): If cvs-cvsroot-required is not set, don't complain if + CVSROOT and cvs-cvsroot are both unset. + +Sun Jan 22 21:22:22 1995 Jim Blandy <jimb@totoro.bio.indiana.edu> + + * pcl-cvs.el (cvs-parse-stderr): + Some changes for Cygnus's Remote CVS. Treat + messages like "cvs server: Updating DIRECTORY" as we treat those like + "cvs update: Updating DIRECTORY". Ignore other messages starting with + "cvs server". + + * pcl-cvs.el (cvs-parse-stderr): Re-indent. + + * .cvsignore: Add ignore list for Texinfo litter. + + * Makefile (lispdir): Set appropriately for totoro. + * pcl-cvs.el (cvs-program, cvs-diff-program, cvs-rmdir-program): Same. + Tue Jun 1 00:00:03 1993 Per Cederqvist (ceder@lysator.liu.se) * Release 1.05. (This release was promised before the end of May, diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL index f3c4480..7679967 100644 --- a/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL +++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL @@ -51,22 +51,25 @@ Installation of the pcl-cvs program Installation of the on-line manual. =================================== - 1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing - `make info'. If you don't have the program `makeinfo' you can - get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as - `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version - there when you read this), or you could use the preformatted - info file `pcl-cvs.info' that is included in the distribution - (type `cp pcl-cvs.info pcl-cvs'). + 1. Move the info file `pcl-cvs.info' to your standard info + directory. This might be called something like + `/usr/gnu/emacs/info'. + + 2. Edit the file `dir' in the info directory and enter one line to + contain a pointer to the info file `pcl-cvs.info'. The line can, + for instance, look like this: - 2. Move the info file `pcl-cvs' to your standard info directory. - This might be called something like `/usr/gnu/emacs/info'. + * Pcl-cvs: (pcl-cvs.info). An Emacs front-end to CVS. - 3. Edit the file `dir' in the info directory and enter one line to - contain a pointer to the info file `pcl-cvs'. The line can, for - instance, look like this: - * Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS. +How to make the on-line manual from pcl-cvs.texinfo +=================================================== + + 1. Create the info file `pcl-cvs.info' from `pcl-cvs.texinfo' by + typing `make info'. If you don't have the program `makeinfo' you + can get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as + `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version + there when you read this). How to make typeset documentation from pcl-cvs.texinfo diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/README b/gnu/usr.bin/cvs/contrib/pcl-cvs/README index 779617f..a9b8106 100644 --- a/gnu/usr.bin/cvs/contrib/pcl-cvs/README +++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/README @@ -19,10 +19,11 @@ If you have been using a previous version of pcl-cvs (for instance 1.02 which is distributed with CVS 1.3) you should read through the file NEWS to see what has changed. -This release has been tested under Emacs 18.59, Emacs 19.10 and Lucid +This release has been tested under Emacs 18.59, Emacs 19.28 and Lucid Emacs 19.6. Emacs 19.10 unfortunately has a file named cookie.el that -collides with the cookie.el that is distributed in Elib. We are -trying to find a solution to that problem. In the mean time, there is +collides with the cookie.el that is distributed in Elib. This +conflict was resolved in 19.11. For earlier versions, there are instructions in Elib 0.07 for how to work around the problem. Per Cederqvist + (updated by Jim Blandy) diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el index 9bbbd38..d9c15d5 100644 --- a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el +++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el @@ -37,13 +37,13 @@ ;;; ------------------------------------------------------- ;;; START OF THINGS TO CHECK WHEN INSTALLING -(defvar cvs-program "/usr/gnu/bin/cvs" +(defvar cvs-program "/usr/local/bin/cvs" "*Full path to the cvs executable.") -(defvar cvs-diff-program "/usr/gnu/bin/diff" +(defvar cvs-diff-program "/usr/local/bin/diff" "*Full path to the diff program.") -(defvar cvs-rmdir-program "/usr/gnu/bin/rmdir" +(defvar cvs-rmdir-program "/bin/rmdir" "*Full path to the rmdir program. Typically /bin/rmdir.") ;; Uncomment the following line if you are running on 18.57 or earlier. @@ -64,6 +64,20 @@ Overrides the $CVSROOT variable by sending \" -d dir\" to all cvs commands. This switch is useful if you have multiple CVS repositories.") +(defvar cvs-cvsroot-required t + "*Specifies whether CVS needs to be told where the repository is. + +In CVS 1.3, if your CVSROOT environment variable is not set, and you +do not set the `cvs-cvsroot' lisp variable, CVS will have no idea +where to find the repository, and refuse to run. CVS 1.4 and later +store the repository path with the working directories, so most +operations don't need to be told where the repository is. + +If you work with multiple repositories with CVS 1.4, it's probably +advisable to leave your CVSROOT environment variable unset, set this +variable to nil, and let CVS figure out where the repository is for +itself.") + (defvar cvs-stdout-file nil "Name of the file that holds the output that CVS sends to stdout. This variable is buffer local.") @@ -214,7 +228,7 @@ the process when a cvs update process is running.") ;;; slash. ;;; file-name The file name. ;;; base-revision The revision that the working file was based on. -;;; Onlyy valid for MERGED and CONFLICT files. +;;; Only valid for MERGED and CONFLICT files. ;;; cvs-diff-buffer A buffer that contains a 'cvs diff file'. ;;; backup-diff-buffer A buffer that contains a 'diff file backup-file'. ;;; full-log The output from cvs, unparsed. @@ -450,7 +464,8 @@ Both LOCAL and DONT-CHANGE-DISC may be non-nil simultaneously. (if (not (file-exists-p cvs-program)) (error "%s: file not found (check setting of cvs-program)" cvs-program)) - (if (not (or (getenv "CVSROOT") cvs-cvsroot)) + (if (and cvs-cvsroot-required + (not (or (getenv "CVSROOT") cvs-cvsroot))) (error "Both cvs-cvsroot and environment variable CVSROOT unset.")) (let* ((this-dir (file-name-as-directory (expand-file-name directory))) (update-buffer (generate-new-buffer @@ -571,6 +586,7 @@ Full documentation is in the Texinfo file. These are the most useful commands: \\[cvs-mode-emerge] Run emerge on base revision/backup file. \\[cvs-mode-acknowledge] Delete line from buffer. \\[cvs-mode-ignore] Add file to the .cvsignore file. \\[cvs-mode-log] Run ``cvs log''. \\[cvs-mode-status] Run ``cvs status''. +\\[cvs-mode-changelog-commit] Like \\[cvs-mode-commit], but get default log text from ChangeLog. \\[cvs-mode-undo-local-changes] Revert the last checked in version - discard your changes to the file. Entry to this mode runs cvs-mode-hook. @@ -898,12 +914,12 @@ This function returns the last cons-cell in the list that is built." ;; End of RCVS stuff. ;; CVS is descending a subdirectory. - - ((looking-at "cvs update: Updating \\(.*\\)$") + ;; (The "server" case is there to support Cyclic CVS.) + ((looking-at "cvs \\(update\\|server\\): Updating \\(.*\\)$") (setq current-dir (cvs-get-current-dir root-dir - (buffer-substring (match-beginning 1) (match-end 1)))) + (buffer-substring (match-beginning 2) (match-end 2)))) (setcdr head (list (cvs-create-fileinfo 'DIRCHANGE current-dir nil (buffer-substring (match-beginning 0) @@ -1010,98 +1026,102 @@ second party") (buffer-substring start (point))))) (setq head (cdr head)))) - (t - - ;; CVS has decided to merge someone elses changes into this - ;; document. This leads to a lot of garbage being printed. - ;; First there is two lines that contains no information - ;; that we skip (but we check that we recognize them). - - (let ((complex-start (point)) - initial-revision filename) - - (cvs-skip-line stdout-buffer stderr-buffer "^RCS file: .*$") - (setq initial-revision - (cvs-skip-line stdout-buffer stderr-buffer - "^retrieving revision \\(.*\\)$" 1)) - (cvs-skip-line stdout-buffer stderr-buffer - "^retrieving revision .*$") + ;; Ignore other messages from Cyclic CVS. + ((looking-at "cvs server:") + (forward-line 1)) - ;; Get the file name from the next line. + (t - (setq - filename - (cvs-skip-line - stdout-buffer stderr-buffer - "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$" - 1)) + ;; CVS has decided to merge someone elses changes into this + ;; document. This leads to a lot of garbage being printed. + ;; First there is two lines that contains no information + ;; that we skip (but we check that we recognize them). - (cond - ;; Was it a conflict? - ((looking-at - ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). - "^\\(rcs\\)?merge\\( warning\\)?: overlaps during merge$") - - ;; Yes, this is a conflict. - (cvs-skip-line - stdout-buffer stderr-buffer - "^\\(rcs\\)?merge\\( warning\\)?: overlaps during merge$") + (let ((complex-start (point)) + initial-revision filename) + (cvs-skip-line stdout-buffer stderr-buffer "^RCS file: .*$") + (setq initial-revision + (cvs-skip-line stdout-buffer stderr-buffer + "^retrieving revision \\(.*\\)$" 1)) (cvs-skip-line stdout-buffer stderr-buffer - "^cvs update: conflicts found in ") + "^retrieving revision .*$") - (let ((fileinfo - (cvs-create-fileinfo - 'CONFLICT current-dir - filename - (buffer-substring complex-start (point))))) + ;; Get the file name from the next line. + + (setq + filename + (cvs-skip-line + stdout-buffer stderr-buffer + "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$" + 1)) - (cvs-set-fileinfo->base-revision fileinfo initial-revision) + (cond + ;; Was it a conflict? + ((looking-at + ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). + "^\\(rcs\\)?merge:?\\( warning\\)?: \\(overlaps\\|conflicts\\) during merge$") - (setcdr head (list fileinfo)) - (setq head (cdr head)))) + ;; Yes, this is a conflict. + (cvs-skip-line + stdout-buffer stderr-buffer + "^\\(rcs\\)?merge:?\\( warning\\)?: \\(overlaps\\|conflicts\\) during merge$") - ;; Was it a conflict, and was RCS compiled without DIFF3_BIN? + (cvs-skip-line stdout-buffer stderr-buffer + "^cvs \\(update\\|server\\): conflicts found in ") - ((looking-at - ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). - "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other probl\ -ems during merge$") + (let ((fileinfo + (cvs-create-fileinfo + 'CONFLICT current-dir + filename + (buffer-substring complex-start (point))))) - ;; Yes, this is a conflict. - (cvs-skip-line - stdout-buffer stderr-buffer - "^\\(rcs\\)?merge\\( warning\\)?: overlaps .*during merge$") + (cvs-set-fileinfo->base-revision fileinfo initial-revision) - (cvs-skip-line stdout-buffer stderr-buffer - "^cvs update: could not merge ") - (cvs-skip-line stdout-buffer stderr-buffer - "^cvs update: restoring .* from backup file ") + (setcdr head (list fileinfo)) + (setq head (cdr head)))) - (let ((fileinfo - (cvs-create-fileinfo - 'CONFLICT current-dir - filename - (buffer-substring complex-start (point))))) + ;; Was it a conflict, and was RCS compiled without DIFF3_BIN? - (setcdr head (list fileinfo)) - (setq head (cdr head)))) + ((looking-at + ;; Allow both RCS 5.5 and 5.6. (5.6 prints "rcs" and " warning"). + "^\\(rcs\\)?merge\\( warning\\)?: overlaps or other probl\ +ems during merge$") - (t - ;; Not a conflict; it must be a succesful merge. - (let ((fileinfo - (cvs-create-fileinfo - 'MERGED current-dir - filename - (buffer-substring complex-start (point))))) - (cvs-set-fileinfo->base-revision fileinfo initial-revision) - (setcdr head (list fileinfo)) - (setq head (cdr head))))))))))) + ;; Yes, this is a conflict. + (cvs-skip-line + stdout-buffer stderr-buffer + "^\\(rcs\\)?merge\\( warning\\)?: overlaps .*during merge$") + + (cvs-skip-line stdout-buffer stderr-buffer + "^cvs update: could not merge ") + (cvs-skip-line stdout-buffer stderr-buffer + "^cvs update: restoring .* from backup file ") + + (let ((fileinfo + (cvs-create-fileinfo + 'CONFLICT current-dir + filename + (buffer-substring complex-start (point))))) + + (setcdr head (list fileinfo)) + (setq head (cdr head)))) + + (t + ;; Not a conflict; it must be a succesful merge. + (let ((fileinfo + (cvs-create-fileinfo + 'MERGED current-dir + filename + (buffer-substring complex-start (point))))) + (cvs-set-fileinfo->base-revision fileinfo initial-revision) + (setcdr head (list fileinfo)) + (setq head (cdr head))))))))))) head) (defun cvs-parse-stdout (stdout-buffer stderr-buffer head root-dir) - "Parse the output from CVS that is written to stderr. + "Parse the output from CVS that is written to stdout. Args: STDOUT-BUFFER STDERR-BUFFER HEAD ROOT-DIR STDOUT-BUFFER is the buffer that holds the output to parse. STDERR-BUFFER holds the output that cvs sent to stderr. It is only @@ -1121,11 +1141,11 @@ This function doesn't return anything particular." ;; A: The file is "cvs add"ed, but not "cvs ci"ed. ;; R: The file is "cvs remove"ed, but not "cvs ci"ed. ;; C: Conflict - ;; U: The file is copied from the repository. + ;; U, P: The file is copied from the repository. ;; ?: Unknown file. - ((looking-at "\\([MARCU?]\\) \\(.*\\)$") + ((looking-at "\\([MARCUP?]\\) \\(.*\\)$") (let* ((c (char-after (match-beginning 1))) (full-path @@ -1137,12 +1157,15 @@ This function doesn't return anything particular." ((eq c ?R) 'REMOVED) ((eq c ?C) 'CONFLICT) ((eq c ?U) 'UPDATED) + ;; generated when Cyclic CVS sends a + ;; patch instead of the full file: + ((eq c ?P) 'UPDATED) ((eq c ??) 'UNKNOWN)) (substring (file-name-directory full-path) 0 -1) (file-name-nondirectory full-path) (buffer-substring (match-beginning 0) (match-end 0))))) ;; Updated files require no further action. - (if (eq c ?U) + (if (memq c '(?U ?P)) (cvs-set-fileinfo->handled fileinfo t)) ;; Link this last on the list. @@ -1227,6 +1250,7 @@ For use by the cookie package." (define-key cvs-mode-map "a" 'cvs-mode-add) (define-key cvs-mode-map "b" 'cvs-mode-diff-backup) (define-key cvs-mode-map "c" 'cvs-mode-commit) + (define-key cvs-mode-map "C" 'cvs-mode-changelog-commit) (define-key cvs-mode-map "d" 'cvs-mode-diff-cvs) (define-key cvs-mode-map "e" 'cvs-mode-emerge) (define-key cvs-mode-map "f" 'cvs-mode-find-file) @@ -2204,3 +2228,266 @@ If second optional argument REVISION is given, retrieve that revision instead." (progn (autoload 'pcl-cvs-fontify "pcl-cvs-lucid") (add-hook 'cvs-mode-hook 'pcl-cvs-fontify))) + + +(defvar cvs-changelog-full-paragraphs t + "If non-nil, include full ChangeLog paragraphs in the CVS log. +This may be set in the ``local variables'' section of a ChangeLog, to +indicate the policy for that ChangeLog. + +A ChangeLog paragraph is a bunch of log text containing no blank lines; +a paragraph usually describes a set of changes with a single purpose, +but perhaps spanning several functions in several files. Changes in +different paragraphs are unrelated. + +You could argue that the CVS log entry for a file should contain the +full ChangeLog paragraph mentioning the change to the file, even though +it may mention other files, because that gives you the full context you +need to understand the change. This is the behavior you get when this +variable is set to t. + +On the other hand, you could argue that the CVS log entry for a change +should contain only the text for the changes which occurred in that +file, because the CVS log is per-file. This is the behavior you get +when this variable is set to nil.") + +(defun cvs-changelog-name (directory) + "Return the name of the ChangeLog file that handles DIRECTORY. +This is in DIRECTORY or one of its parents. +Signal an error if we can't find an appropriate ChangeLog file." + (let ((dir (file-name-as-directory directory)) + file) + (while (and dir + (not (file-exists-p + (setq file (expand-file-name "ChangeLog" dir))))) + (let ((last dir)) + (setq dir (file-name-directory (directory-file-name dir))) + (if (equal last dir) + (setq dir nil)))) + (or dir + (error "Can't find ChangeLog for %s" directory)) + file)) + +(defun cvs-narrow-changelog () + "Narrow to the top page of the current buffer, a ChangeLog file. +Actually, the narrowed region doesn't include the date line. +A \"page\" in a ChangeLog file is the area between two dates." + (or (eq major-mode 'change-log-mode) + (error "cvs-narrow-changelog: current buffer isn't a ChangeLog")) + + (goto-char (point-min)) + + ;; Skip date line and subsequent blank lines. + (forward-line 1) + (if (looking-at "[ \t\n]*\n") + (goto-char (match-end 0))) + + (let ((start (point))) + (forward-page 1) + (narrow-to-region start (point)) + (goto-char (point-min)))) + +(defun cvs-changelog-paragraph () + "Return the bounds of the ChangeLog paragraph containing point. +If we are between paragraphs, return the previous paragraph." + (save-excursion + (beginning-of-line) + (if (looking-at "^[ \t]*$") + (skip-chars-backward " \t\n" (point-min))) + (list (progn + (if (re-search-backward "^[ \t]*\n" nil 'or-to-limit) + (goto-char (match-end 0))) + (point)) + (if (re-search-forward "^[ \t\n]*$" nil t) + (match-beginning 0) + (point))))) + +(defun cvs-changelog-subparagraph () + "Return the bounds of the ChangeLog subparagraph containing point. +A subparagraph is a block of non-blank lines beginning with an asterisk. +If we are between subparagraphs, return the previous subparagraph." + (save-excursion + (end-of-line) + (if (search-backward "*" nil t) + (list (progn (beginning-of-line) (point)) + (progn + (forward-line 1) + (if (re-search-forward "^[ \t]*[\n*]" nil t) + (match-beginning 0) + (point-max)))) + (list (point) (point))))) + +(defun cvs-changelog-entry () + "Return the bounds of the ChangeLog entry containing point. +The variable `cvs-changelog-full-paragraphs' decides whether an +\"entry\" is a paragraph or a subparagraph; see its documentation string +for more details." + (if cvs-changelog-full-paragraphs + (cvs-changelog-paragraph) + (cvs-changelog-subparagraph))) + +(defun cvs-changelog-ours-p () + "See if ChangeLog entry at point is for the current user, today. +Return non-nil iff it is." + ;; Code adapted from add-change-log-entry. + (looking-at (concat (regexp-quote (substring (current-time-string) + 0 10)) + ".* " + (regexp-quote (substring (current-time-string) -4)) + "[ \t]+" + (regexp-quote (if (boundp 'add-log-full-name) + add-log-full-name + user-full-name)) + " <" + (regexp-quote + (if (boundp 'add-log-mailing-address) + add-log-mailing-address + user-mail-address))))) + +(defun cvs-relative-path (base child) + "Return a directory path relative to BASE for CHILD. +If CHILD doesn't seem to be in a subdirectory of BASE, just return +the full path to CHILD." + (let ((base (file-name-as-directory (expand-file-name base))) + (child (expand-file-name child))) + (or (string= base (substring child 0 (length base))) + (error "cvs-relative-path: %s isn't in %s" child base)) + (substring child (length base)))) + +(defun cvs-changelog-entries (file) + "Return the ChangeLog entries for FILE, and the ChangeLog they came from. +The return value looks like this: + (LOGBUFFER (ENTRYSTART . ENTRYEND) ...) +where LOGBUFFER is the name of the ChangeLog buffer, and each +\(ENTRYSTART . ENTRYEND\) pair is a buffer region." + (save-excursion + (set-buffer (find-file-noselect + (cvs-changelog-name + (file-name-directory + (expand-file-name file))))) + (or (eq major-mode 'change-log-mode) + (change-log-mode)) + (goto-char (point-min)) + (if (looking-at "[ \t\n]*\n") + (goto-char (match-end 0))) + (if (not (cvs-changelog-ours-p)) + (list (current-buffer)) + (save-restriction + (cvs-narrow-changelog) + (goto-char (point-min)) + + ;; Search for the name of FILE relative to the ChangeLog. If that + ;; doesn't occur anywhere, they're not using full relative + ;; filenames in the ChangeLog, so just look for FILE; we'll accept + ;; some false positives. + (let ((pattern (cvs-relative-path + (file-name-directory buffer-file-name) file))) + (if (or (string= pattern "") + (not (save-excursion + (search-forward pattern nil t)))) + (setq pattern file)) + + (let (texts) + (while (search-forward pattern nil t) + (let ((entry (cvs-changelog-entry))) + (setq texts (cons entry texts)) + (goto-char (elt entry 1)))) + + (cons (current-buffer) texts))))))) + +(defun cvs-changelog-insert-entries (buffer regions) + "Insert those regions in BUFFER specified in REGIONS. +Sort REGIONS front-to-back first." + (let ((regions (sort regions 'car-less-than-car)) + (last)) + (while regions + (if (and last (< last (car (car regions)))) + (newline)) + (setq last (elt (car regions) 1)) + (apply 'insert-buffer-substring buffer (car regions)) + (setq regions (cdr regions))))) + +(defun cvs-union (set1 set2) + "Return the union of SET1 and SET2, according to `equal'." + (while set2 + (or (member (car set2) set1) + (setq set1 (cons (car set2) set1))) + (setq set2 (cdr set2))) + set1) + +(defun cvs-insert-changelog-entries (files) + "Given a list of files FILES, insert the ChangeLog entries for them." + (let ((buffer-entries nil)) + + ;; Add each buffer to buffer-entries, and associate it with the list + ;; of entries we want from that file. + (while files + (let* ((entries (cvs-changelog-entries (car files))) + (pair (assq (car entries) buffer-entries))) + (if pair + (setcdr pair (cvs-union (cdr pair) (cdr entries))) + (setq buffer-entries (cons entries buffer-entries)))) + (setq files (cdr files))) + + ;; Now map over each buffer in buffer-entries, sort the entries for + ;; each buffer, and extract them as strings. + (while buffer-entries + (cvs-changelog-insert-entries (car (car buffer-entries)) + (cdr (car buffer-entries))) + (if (and (cdr buffer-entries) (cdr (car buffer-entries))) + (newline)) + (setq buffer-entries (cdr buffer-entries))))) + +(defun cvs-edit-delete-common-indentation () + "Unindent the current buffer rigidly until at least one line is flush left." + (save-excursion + (let ((common 100000)) + (goto-char (point-min)) + (while (< (point) (point-max)) + (if (not (looking-at "^[ \t]*$")) + (setq common (min common (current-indentation)))) + (forward-line 1)) + (indent-rigidly (point-min) (point-max) (- common))))) + +(defun cvs-mode-changelog-commit () + + "Check in all marked files, or the current file. +Ask the user for a log message in a buffer. + +This is just like `\\[cvs-mode-commit]', except that it tries to provide +appropriate default log messages by looking at the ChangeLogs. The +idea is to write your ChangeLog entries first, and then use this +command to commit your changes. + +To select default log text, we: +- find the ChangeLogs for the files to be checked in, +- verify that the top entry in the ChangeLog is on the current date + and by the current user; if not, we don't provide any default text, +- search the ChangeLog entry for paragraphs containing the names of + the files we're checking in, and finally +- use those paragraphs as the log text." + + (interactive) + + (let* ((cvs-buf (current-buffer)) + (marked (cvs-filter (function cvs-committable) + (cvs-get-marked)))) + (if (null marked) + (error "Nothing to commit!") + (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer)) + (goto-char (point-min)) + + (erase-buffer) + (cvs-insert-changelog-entries + (mapcar (lambda (tin) + (let ((cookie (tin-cookie cvs-cookie-handle tin))) + (expand-file-name + (cvs-fileinfo->file-name cookie) + (cvs-fileinfo->dir cookie)))) + marked)) + (cvs-edit-delete-common-indentation) + + (cvs-edit-mode) + (make-local-variable 'cvs-commit-list) + (setq cvs-commit-list marked) + (message "Press C-c C-c when you are done editing.")))) diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo index 0b51f77..bb0a4fe 100644 --- a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo +++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo @@ -20,7 +20,7 @@ @comment along with pcl-cvs; see the file COPYING. If not, write to @comment the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -@setfilename pcl-cvs +@setfilename pcl-cvs.info @settitle Pcl-cvs - The Emacs Front-End to CVS @setchapternewpage on diff --git a/gnu/usr.bin/cvs/contrib/rcslock.pl b/gnu/usr.bin/cvs/contrib/rcslock.pl index db09b4b..01e349f 100644 --- a/gnu/usr.bin/cvs/contrib/rcslock.pl +++ b/gnu/usr.bin/cvs/contrib/rcslock.pl @@ -1,4 +1,5 @@ -#!/usr/bin/perl +#! xPERL_PATHx +# -*-Perl-*- # Author: John Rouillard (rouilj@cs.umb.edu) # Supported: Yeah right. (Well what do you expect for 2 hours work?) diff --git a/gnu/usr.bin/cvs/cvs/add.c b/gnu/usr.bin/cvs/cvs/add.c index e14c79c..f798a22 100644 --- a/gnu/usr.bin/cvs/cvs/add.c +++ b/gnu/usr.bin/cvs/cvs/add.c @@ -25,17 +25,18 @@ */ #include "cvs.h" +#include "save-cwd.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)add.c 1.55 94/10/22 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)add.c 1.55 94/10/22 $"; +USE(rcsid); #endif static int add_directory PROTO((char *repository, char *dir)); static int build_entry PROTO((char *repository, char *user, char *options, char *message, List * entries, char *tag)); -static char *add_usage[] = +static const char *const add_usage[] = { "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", @@ -46,7 +47,7 @@ static char *add_usage[] = int add (argc, argv) int argc; - char *argv[]; + char **argv; { char *message = NULL; char *user; @@ -62,6 +63,8 @@ add (argc, argv) if (argc == 1 || argc == -1) usage (add_usage); + wrap_setup (); + /* parse args */ optind = 1; while ((c = getopt (argc, argv, "k:m:")) != -1) @@ -91,12 +94,54 @@ add (argc, argv) /* find the repository associated with our current dir */ repository = Name_Repository ((char *) NULL, (char *) NULL); - entries = ParseEntries (0); + +#ifdef CLIENT_SUPPORT + if (client_active) + { + int i; + start_server (); + ign_setup (); + if (options) send_arg(options); + option_with_arg ("-m", message); + for (i = 0; i < argc; ++i) + /* FIXME: Does this erroneously call Create_Admin in error + conditions which are only detected once the server gets its + hands on things? */ + if (isdir (argv[i])) + { + char *tag; + char *date; + char *rcsdir = xmalloc (strlen (repository) + + strlen (argv[i]) + 10); + + /* before we do anything else, see if we have any + per-directory tags */ + ParseTag (&tag, &date); + + sprintf (rcsdir, "%s/%s", repository, argv[i]); + + Create_Admin (argv[i], argv[i], rcsdir, tag, date); + + if (tag) + free (tag); + if (date) + free (date); + free (rcsdir); + } + send_files (argc, argv, 0, 0); + if (fprintf (to_server, "add\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + + entries = Entries_Open (0); /* walk the arg list adding files/dirs */ for (i = 0; i < argc; i++) { int begin_err = err; + int begin_added_files = added_files; user = argv[i]; strip_trailing_slashes (user); @@ -122,7 +167,7 @@ add (argc, argv) error (0, 0, "nothing known about %s", user); err++; } - else if (!isdir (user)) + else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS)) { /* * See if a directory exists in the repository with @@ -143,15 +188,53 @@ add (argc, argv) /* There is a user file, so build the entry for it */ if (build_entry (repository, user, vers->options, message, entries, vers->tag) != 0) - err++; + err++; else { - added_files++; - if (!quiet) - error (0, 0, "scheduling file `%s' for addition", user); + added_files++; + if (!quiet) + { +#ifdef DEATH_SUPPORT + if (vers->tag) + error (0, 0, "\ +scheduling %s `%s' for addition on branch `%s'", + (wrap_name_has (user, WRAP_TOCVS) + ? "wrapper" + : "file"), + user, vers->tag); + else +#endif /* DEATH_SUPPORT */ + error (0, 0, "scheduling %s `%s' for addition", + (wrap_name_has (user, WRAP_TOCVS) + ? "wrapper" + : "file"), + user); + } } } } +#ifdef DEATH_SUPPORT + else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) + { + error (0, 0, "the directory `%s' cannot be added because a file of the", user); + error (1, 0, "same name already exists in the repository."); + } + else + { + if (vers->tag) + error (0, 0, "file `%s' will be added on branch `%s' from version %s", + user, vers->tag, vers->vn_rcs); + else + error (0, 0, "version %s of `%s' will be resurrected", + vers->vn_rcs, user); + Register (entries, user, "0", vers->ts_user, NULL, + vers->tag, NULL, NULL); + ++added_files; + } + } +#endif /* DEATH_SUPPORT */ else { /* @@ -206,6 +289,9 @@ add (argc, argv) free (tmp); /* XXX - bugs here; this really resurrect the head */ + /* Note that this depends on the Register above actually + having written Entries, or else it won't really + check the file out. */ if (update (2, argv + i - 1) == 0) { error (0, 0, "%s, version %s, resurrected", user, @@ -235,16 +321,23 @@ add (argc, argv) freevers_ts (&vers); /* passed all the checks. Go ahead and add it if its a directory */ - if (begin_err == err && isdir (user)) + if (begin_err == err + && isdir (user) + && !wrap_name_has (user, WRAP_TOCVS)) { err += add_directory (repository, user); continue; } +#ifdef SERVER_SUPPORT + if (server_active && begin_added_files != added_files) + server_checked_in (user, ".", repository); +#endif } if (added_files) error (0, 0, "use 'cvs commit' to add %s permanently", (added_files == 1) ? "this file" : "these files"); - dellist (&entries); + + Entries_Close (entries); if (message) free (message); @@ -264,7 +357,8 @@ add_directory (repository, dir) char *repository; char *dir; { - char cwd[PATH_MAX], rcsdir[PATH_MAX]; + char rcsdir[PATH_MAX]; + struct saved_cwd cwd; char message[PATH_MAX + 100]; char *tag, *date; @@ -274,9 +368,9 @@ add_directory (repository, dir) "directory %s not added; must be a direct sub-directory", dir); return (1); } - if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0) + if (strcmp (dir, CVSADM) == 0) { - error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM); + error (0, 0, "cannot add a `%s' directory", CVSADM); return (1); } @@ -284,20 +378,20 @@ add_directory (repository, dir) ParseTag (&tag, &date); /* now, remember where we were, so we can get back */ - if (getwd (cwd) == NULL) - { - error (0, 0, "cannot get working directory: %s", cwd); + if (save_cwd (&cwd)) return (1); - } if (chdir (dir) < 0) { error (0, errno, "cannot chdir to %s", dir); return (1); } - if (isfile (CVSADM) || isfile (OCVSADM)) +#ifdef SERVER_SUPPORT + if (!server_active && isfile (CVSADM)) +#else + if (isfile (CVSADM)) +#endif { - error (0, 0, - "%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM); + error (0, 0, "%s/%s already exists", dir, CVSADM); goto out; } @@ -344,14 +438,14 @@ add_directory (repository, dir) } #endif - omask = umask (2); - if (mkdir (rcsdir, 0777) < 0) + omask = umask (cvsumask); + if (CVS_MKDIR (rcsdir, 0777) < 0) { error (0, errno, "cannot mkdir %s", rcsdir); - (void) umask ((int) omask); + (void) umask (omask); goto out; } - (void) umask ((int) omask); + (void) umask (omask); /* * Set up an update list with a single title node for Update_Logfile @@ -367,7 +461,12 @@ add_directory (repository, dir) dellist (&ulist); } - Create_Admin (".", rcsdir, tag, date); +#ifdef SERVER_SUPPORT + if (!server_active) + Create_Admin (".", dir, rcsdir, tag, date); +#else + Create_Admin (".", dir, rcsdir, tag, date); +#endif if (tag) free (tag); if (date) @@ -375,8 +474,9 @@ add_directory (repository, dir) (void) printf ("%s", message); out: - if (chdir (cwd) < 0) - error (1, errno, "cannot chdir to %s", cwd); + if (restore_cwd (&cwd, NULL)) + exit (1); + free_cwd (&cwd); return (0); } @@ -397,6 +497,8 @@ build_entry (repository, user, options, message, entries, tag) char line[MAXLINELEN]; FILE *fp; +#ifndef DEATH_SUPPORT + /* when using the rcs death support, this case is not a problem. */ /* * There may be an old file with the same name in the Attic! This is, * perhaps, an awkward place to check for this, but other places are @@ -409,21 +511,13 @@ build_entry (repository, user, options, message, entries, tag) repository, CVSATTIC); return (1); } +#endif /* no DEATH_SUPPORT */ if (noexec) return (0); /* - * The options for the "add" command are store in the file CVS/user,p - * XXX - no they are not! - */ - (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT); - fp = open_file (fname, "w+"); - if (fclose (fp) == EOF) - error(1, errno, "cannot close %s", fname); - - /* - * And the requested log is read directly from the user and stored in the + * The requested log is read directly from the user and stored in the * file user,t. If the "message" argument is set, use it as the * initial creation log (which typically describes the file). */ @@ -436,8 +530,8 @@ build_entry (repository, user, options, message, entries, tag) /* * Create the entry now, since this allows the user to interrupt us above - * without needing to clean anything up (well, we could clean up the ,p - * and ,t files, but who cares). + * without needing to clean anything up (well, we could clean up the + * ,t file, but who cares). */ (void) sprintf (line, "Initial %s", user); Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); diff --git a/gnu/usr.bin/cvs/cvs/admin.c b/gnu/usr.bin/cvs/cvs/admin.c index 14a5b51..f12e143 100644 --- a/gnu/usr.bin/cvs/cvs/admin.c +++ b/gnu/usr.bin/cvs/cvs/admin.c @@ -12,10 +12,13 @@ */ #include "cvs.h" +#ifdef CVS_ADMIN_GROUP +#include <grp.h> +#endif #ifndef lint -static char rcsid[] = "$CVSid: @(#)admin.c 1.20 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)admin.c 1.20 94/09/30 $"; +USE(rcsid); #endif static Dtype admin_dirproc PROTO((char *dir, char *repos, char *update_dir)); @@ -23,7 +26,7 @@ static int admin_fileproc PROTO((char *file, char *update_dir, char *repository, List *entries, List *srcfiles)); -static char *admin_usage[] = +static const char *const admin_usage[] = { "Usage: %s %s rcs-options files...\n", NULL @@ -35,13 +38,42 @@ static char **av; int admin (argc, argv) int argc; - char *argv[]; + char **argv; { int err; - +#ifdef CVS_ADMIN_GROUP + struct group *grp; +#endif if (argc <= 1) usage (admin_usage); +#ifdef CVS_ADMIN_GROUP + grp = getgrnam(CVS_ADMIN_GROUP); + /* skip usage right check if group CVS_ADMIN_GROUP does not exist */ + if (grp != NULL) + { + char *me = getcaller(); + char **grnam = grp->gr_mem; + int denied = 1; + + while (*grnam) + { + if (strcmp(*grnam, me) == 0) + { + denied = 0; + break; + } + grnam++; + } + + if (denied) + error (1, 0, "usage is restricted to members of the group %s", + CVS_ADMIN_GROUP); + } +#endif + + wrap_setup (); + /* skip all optional arguments to see if we have any file names */ for (ac = 1; ac < argc; ac++) if (argv[ac][0] != '-') @@ -53,9 +85,36 @@ admin (argc, argv) if (ac == 0 || argc == 0) usage (admin_usage); +#ifdef CLIENT_SUPPORT + if (client_active) + { + int i; + + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + for (i = 0; i <= ac; ++i) /* XXX send -ko too with i = 0 */ + send_arg (av[i]); + +#if 0 + /* FIXME: We shouldn't have to send current files, but I'm not sure + whether it works. So send the files -- + it's slower but it works. */ + send_file_names (argc, argv); +#else + send_files (argc, argv, 0, 0); +#endif + if (fprintf (to_server, "admin\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + /* start the recursion processor */ - err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc, - (int (*) ()) NULL, argc, argv, 0, + err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc, + (DIRLEAVEPROC) NULL, argc, argv, 0, W_LOCAL, 0, 1, (char *) NULL, 1, 0); return (err); } diff --git a/gnu/usr.bin/cvs/cvs/checkin.c b/gnu/usr.bin/cvs/cvs/checkin.c index 1f7691b..ef3b81a 100644 --- a/gnu/usr.bin/cvs/cvs/checkin.c +++ b/gnu/usr.bin/cvs/cvs/checkin.c @@ -18,14 +18,16 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)checkin.c 1.48 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)checkin.c 1.48 94/10/07 $"; +USE(rcsid); #endif int -Checkin (type, file, repository, rcs, rev, tag, options, message, entries) +Checkin (type, file, update_dir, repository, + rcs, rev, tag, options, message, entries) int type; char *file; + char *update_dir; char *repository; char *rcs; char *rev; @@ -37,8 +39,17 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) char fname[PATH_MAX]; Vers_TS *vers; int set_time; + char *fullname; - (void) printf ("Checking in %s;\n", file); + char *tocvsPath = NULL; + + fullname = xmalloc (strlen (update_dir) + strlen (file) + 10); + if (update_dir[0] == '\0') + strcpy (fullname, file); + else + sprintf (fullname, "%s/%s", update_dir, file); + + (void) printf ("Checking in %s;\n", fullname); (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); /* @@ -46,13 +57,28 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) * modification times, then place a copy back in the original file name * for the checkin and checkout. */ + + tocvsPath = wrap_tocvs_process_file (fullname); + if (!noexec) - copy_file (file, fname); + { + if (tocvsPath) + { + copy_file (tocvsPath, fname); + if (unlink_file_dir (file) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", file); + copy_file (tocvsPath, file); + } + else + { + copy_file (file, fname); + } + } run_setup ("%s%s -f %s%s", Rcsbin, RCS_CI, rev ? "-r" : "", rev ? rev : ""); - run_args ("-m%s", (*message == '\0' || strcmp(message, "\n") == 0) ? - "*** empty log message ***\n" : message); + run_args ("-m%s", make_message_rcslegal (message)); run_arg (rcs); switch (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) @@ -83,11 +109,14 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) } else { - (void) unlink_file (fname); + if (unlink_file (fname) < 0) + error (0, errno, "cannot remove %s", fname); /* sync up with the time from the RCS file */ set_time = 1; } + wrap_fromcvs_process_file (file); + /* * If we want read-only files, muck the permissions here, before * getting the file time-stamp. @@ -95,13 +124,15 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) if (cvswrite == FALSE) xchmod (file, 0); +#ifndef DEATH_SUPPORT + /* With death_support, files added with tags go into branches immediately. */ + /* for added files with symbolic tags, need to add the tag too */ if (type == 'A' && tag && !isdigit (*tag)) { - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, tag, rev); - run_arg (rcs); - (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + (void) RCS_settag(rcs, tag, rev); } +#endif /* No DEATH_SUPPORT */ /* re-register with the new data */ vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL, @@ -112,11 +143,21 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) vers->options, vers->tag, vers->date, (char *) 0); history_write (type, (char *) 0, vers->vn_rcs, file, repository); freevers_ts (&vers); + + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + break; case -1: /* fork failed */ + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + if (!noexec) - error (1, errno, "could not check in %s -- fork failed", file); + error (1, errno, "could not check in %s -- fork failed", + fullname); return (1); default: /* ci failed */ @@ -125,10 +166,14 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) * The checkin failed, for some unknown reason, so we restore the * original user file, print an error, and return an error */ + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + if (!noexec) { rename_file (fname, file); - error (0, 0, "could not check in %s", file); + error (0, 0, "could not check in %s", fullname); } return (1); } @@ -140,10 +185,20 @@ Checkin (type, file, repository, rcs, rev, tag, options, message, entries) */ if (rev) { - run_setup ("%s%s -q -u", Rcsbin, RCS); - run_arg (rcs); - (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL); + (void) RCS_unlock (rcs, NULL, 1); } +#ifdef SERVER_SUPPORT + if (server_active) + { + if (set_time) + /* Need to update the checked out file on the client side. */ + server_updated (file, update_dir, repository, SERVER_UPDATED, + NULL, NULL); + else + server_checked_in (file, update_dir, repository); + } +#endif + return (0); } diff --git a/gnu/usr.bin/cvs/cvs/checkout.c b/gnu/usr.bin/cvs/cvs/checkout.c index a5a79ad..bb126dd 100644 --- a/gnu/usr.bin/cvs/cvs/checkout.c +++ b/gnu/usr.bin/cvs/cvs/checkout.c @@ -36,31 +36,30 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)checkout.c 1.78 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)checkout.c 1.78 94/10/07 $"; +USE(rcsid); #endif static char *findslash PROTO((char *start, char *p)); static int build_dirs_and_chdir PROTO((char *dir, char *prepath, char *realdir, int sticky)); -static int checkout_proc PROTO((int *pargc, char *argv[], char *where, +static int checkout_proc PROTO((int *pargc, char **argv, char *where, char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, char *msg)); +static int safe_location PROTO((void)); -static char *checkout_usage[] = +static const char *const checkout_usage[] = { - "Usage:\n %s %s [-ANPQcflnpqs] [-r rev | -D date] [-d dir] [-k kopt] modules...\n", + "Usage:\n %s %s [-ANPcflnps] [-r rev | -D date] [-d dir] [-k kopt] modules...\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-P\tPrune empty directories.\n", - "\t-Q\tReally quiet.\n", "\t-c\t\"cat\" the module database.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", "\t-n\tDo not run module program (if any).\n", "\t-p\tCheck out files to standard output.\n", - "\t-q\tSomewhat quiet.\n", "\t-s\tLike -c, but include module status.\n", "\t-r rev\tCheck out revision or tag. (implies -P)\n", "\t-D date\tCheck out revisions as of date. (implies -P)\n", @@ -70,18 +69,17 @@ static char *checkout_usage[] = NULL }; -static char *export_usage[] = +static const char *const export_usage[] = { - "Usage: %s %s [-NPQflnq] [-r rev | -D date] [-d dir] module...\n", + "Usage: %s %s [-NPfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", "\t-N\tDon't shorten module paths if -d specified.\n", - "\t-Q\tReally quiet.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", "\t-n\tDo not run module program (if any).\n", - "\t-q\tSomewhat quiet.\n", "\t-r rev\tCheck out revision or tag.\n", "\t-D date\tCheck out revisions as of date.\n", "\t-d dir\tCheck out into dir instead of module name.\n", + "\t-k kopt\tUse RCS kopt -k option on checkout.\n", NULL }; @@ -99,7 +97,7 @@ static char *preload_update_dir = NULL; int checkout (argc, argv) int argc; - char *argv[]; + char **argv; { int i; int c; @@ -109,21 +107,25 @@ checkout (argc, argv) int local = 0; int shorten = -1; char *where = NULL; - char *valid_options, **valid_usage; + char *valid_options; + const char *const *valid_usage; + enum mtype m_type; /* * A smaller subset of options are allowed for the export command, which * is essentially like checkout, except that it hard-codes certain - * options to be on (like -kv) and takes care to remove the CVS directory - * when it has done its duty + * options to be default (like -kv) and takes care to remove the CVS + * directory when it has done its duty */ if (strcmp (command_name, "export") == 0) { - valid_options = "Nnd:flRQqr:D:"; + m_type = EXPORT; + valid_options = "Nnk:d:flRQqr:D:"; valid_usage = export_usage; } else { + m_type = CHECKOUT; valid_options = "ANnk:d:flRpQqcsr:D:j:P"; valid_usage = checkout_usage; } @@ -132,6 +134,7 @@ checkout (argc, argv) usage (valid_usage); ign_setup (); + wrap_setup (); optind = 1; while ((c = getopt (argc, argv, valid_options)) != -1) @@ -153,10 +156,15 @@ checkout (argc, argv) run_module_prog = 0; break; case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'l': local = 1; @@ -230,9 +238,102 @@ checkout (argc, argv) } if (tag && isdigit (tag[0])) error (1, 0, "tag `%s' must be a symbolic tag", tag); - options = RCS_check_kflag ("v");/* -kv must be on */ +/* + * mhy 950615: -kv doesn't work for binaries with RCS keywords. + * Instead use the default provided in the RCS file (-ko for binaries). + */ +#if 0 + if (!options) + options = RCS_check_kflag ("v");/* -kv is default */ +#endif + } + + if (!safe_location()) { + error(1, 0, "Cannot check out files into the repository itself"); } +#ifdef CLIENT_SUPPORT + if (client_active) + { + int expand_modules; + + start_server (); + + ign_setup (); + + /* We have to expand names here because the "expand-modules" + directive to the server has the side-effect of having the + server send the check-in and update programs for the + various modules/dirs requested. If we turn this off and + simply request the names of the modules and directories (as + below in !expand_modules), those files (CVS/Checking.prog + or CVS/Update.prog) don't get created. Grrr. */ + + expand_modules = (!cat && !status && !pipeout + && supported_request ("expand-modules")); + + if (expand_modules) + { + /* This is done here because we need to read responses + from the server before we send the command checkout or + export files. */ + + client_expand_modules (argc, argv, local); + } + + if (!run_module_prog) send_arg ("-n"); + if (local) send_arg ("-l"); + if (pipeout) send_arg ("-p"); + if (!force_tag_match) send_arg ("-f"); + if (aflag) + send_arg("-A"); + if (!shorten) + send_arg("-N"); + if (checkout_prune_dirs && strcmp (command_name, "export") != 0) + send_arg("-P"); + client_prune_dirs = checkout_prune_dirs; + if (cat) + send_arg("-c"); + if (where != NULL) + { + option_with_arg ("-d", where); + } + if (status) + send_arg("-s"); + if (strcmp (command_name, "export") != 0 + && options != NULL + && options[0] != '\0') + send_arg (options); + option_with_arg ("-r", tag); + if (date) + client_senddate (date); + if (join_rev1 != NULL) + option_with_arg ("-j", join_rev1); + if (join_rev2 != NULL) + option_with_arg ("-j", join_rev2); + + if (expand_modules) + { + client_send_expansions (local); + } + else + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + client_nonexpanded_setup (); + } + + if (fprintf + (to_server, + strcmp (command_name, "export") == 0 ? "export\n" : "co\n") + < 0) + error (1, errno, "writing to server"); + + return get_responses_and_close (); + } +#endif + if (cat || status) { cat_module (status); @@ -249,23 +350,29 @@ checkout (argc, argv) { char repository[PATH_MAX]; - (void) mkdir (where, 0777); + (void) CVS_MKDIR (where, 0777); if (chdir (where) < 0) error (1, errno, "cannot chdir to %s", where); preload_update_dir = xstrdup (where); where = (char *) NULL; - if (!isfile (CVSADM) && !isfile (OCVSADM)) + if (!isfile (CVSADM)) { (void) sprintf (repository, "%s/%s/%s", CVSroot, CVSROOTADM, CVSNULLREPOS); if (!isfile (repository)) - (void) mkdir (repository, 0777); + { + mode_t omask; + omask = umask (cvsumask); + (void) CVS_MKDIR (repository, 0777); + (void) umask (omask); + } /* I'm not sure whether this check is redundant. */ if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", repository, (char *) NULL, (char *) NULL); + Create_Admin (".", where, repository, + (char *) NULL, (char *) NULL); if (!noexec) { FILE *fp; @@ -273,6 +380,10 @@ checkout (argc, argv) fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (preload_update_dir, repository); +#endif } } } @@ -302,13 +413,34 @@ checkout (argc, argv) } for (i = 0; i < argc; i++) - err += do_module (db, argv[i], CHECKOUT, "Updating", checkout_proc, + err += do_module (db, argv[i], m_type, "Updating", checkout_proc, where, shorten, local, run_module_prog, (char *) NULL); close_module (db); return (err); } +static int +safe_location () +{ + char current[PATH_MAX]; + char hardpath[PATH_MAX+5]; + int x; + + x = readlink(CVSroot, hardpath, sizeof hardpath - 1); + if (x == -1) + { + strcpy(hardpath, CVSroot); + } + hardpath[x] = '\0'; + getwd (current); + if (strncmp(current, hardpath, strlen(hardpath)) == 0) + { + return (0); + } + return (1); +} + /* * process_module calls us back here so we do the actual checkout stuff */ @@ -317,7 +449,7 @@ static int checkout_proc (pargc, argv, where, mwhere, mfile, shorten, local_specified, omodule, msg) int *pargc; - char *argv[]; + char **argv; char *where; char *mwhere; char *mfile; @@ -528,7 +660,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, free (prepath); /* set up the repository (or make sure the old one matches) */ - if (!isfile (CVSADM) && !isfile (OCVSADM)) + if (!isfile (CVSADM)) { FILE *fp; @@ -538,10 +670,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", repository, (char *) NULL, (char *) NULL); + Create_Admin (".", where, repository, + (char *) NULL, (char *) NULL); fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (where, repository); +#endif } else { @@ -549,7 +686,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", repository, tag, date); + Create_Admin (".", where, repository, tag, date); } } else @@ -558,7 +695,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* get the contents of the previously existing repository */ repos = Name_Repository ((char *) NULL, preload_update_dir); - if (strcmp (repository, repos) != 0) + if (fncmp (repository, repos) != 0) { error (0, 0, "existing repository %s does not match %s", repos, repository); @@ -620,7 +757,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, List *entries; /* we are only doing files, so register them */ - entries = ParseEntries (0); + entries = Entries_Open (0); for (i = 1; i < *pargc; i++) { char line[MAXLINELEN]; @@ -639,7 +776,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, } freevers_ts (&vers); } - dellist (&entries); + + Entries_Close (entries); } /* Don't log "export", just regular "checkouts" */ @@ -698,32 +836,35 @@ build_dirs_and_chdir (dir, prepath, realdir, sticky) { *slash = '\0'; *slash2 = '\0'; - (void) mkdir (cp, 0777); + (void) CVS_MKDIR (cp, 0777); if (chdir (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); return (1); } - if (!isfile (CVSADM) && !isfile (OCVSADM) && - strcmp (command_name, "export") != 0) + if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) { (void) sprintf (repository, "%s/%s", prepath, path2); /* I'm not sure whether this check is redundant. */ if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", repository, sticky ? (char *) NULL : tag, + Create_Admin (".", cp, repository, sticky ? (char *) NULL : tag, sticky ? (char *) NULL : date); if (!noexec) { fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (path, repository); +#endif } } *slash = '/'; *slash2 = '/'; } - (void) mkdir (cp, 0777); + (void) CVS_MKDIR (cp, 0777); if (chdir (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); diff --git a/gnu/usr.bin/cvs/cvs/classify.c b/gnu/usr.bin/cvs/cvs/classify.c index ff9305d..23fafca 100644 --- a/gnu/usr.bin/cvs/cvs/classify.c +++ b/gnu/usr.bin/cvs/cvs/classify.c @@ -10,11 +10,17 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)classify.c 1.17 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)classify.c 1.17 94/10/07 $"; +USE(rcsid); #endif +#ifdef SERVER_SUPPORT +static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers, + List * entries, + char *repository, char *update_dir)); +#else static void sticky_ck PROTO((char *file, int aflag, Vers_TS * vers, List * entries)); +#endif /* * Classify the state of a file @@ -73,6 +79,25 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, ret = T_UNKNOWN; } } +#ifdef DEATH_SUPPORT + else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) + { + if (vers->ts_user == NULL) + /* + * Logically seems to me this should be T_UPTODATE. + * But the joining code in update.c seems to expect + * T_CHECKOUT, and that is what has traditionally been + * returned for this case. + */ + ret = T_CHECKOUT; + else + { + error (0, 0, "use `cvs add' to create an entry for %s", + fullname); + ret = T_UNKNOWN; + } + } +#endif else { /* there is an rcs file */ @@ -133,8 +158,28 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, if (vers->vn_rcs == NULL) /* There is no RCS file, added file */ ret = T_ADDED; +#ifdef DEATH_SUPPORT + else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) + /* we are resurrecting. */ + ret = T_ADDED; +#endif /* DEATH_SUPPORT */ else { +#ifdef DEATH_SUPPORT + if (vers->srcfile->flags & INATTIC + && vers->srcfile->flags & VALID) + { + /* This file has been added on some branch other than + the one we are looking at. In the branch we are + looking at, the file was already valid. */ + if (!really_quiet) + error (0, 0, + "conflict: %s has been added, but already exists", + fullname); + } + else + { +#endif /* DEATH_SUPPORT */ /* * There is an RCS file, so someone else must have checked * one in behind our back; conflict @@ -143,6 +188,9 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, error (0, 0, "conflict: %s created independently by second party", fullname); +#ifdef DEATH_SUPPORT + } +#endif ret = T_CONFLICT; } } @@ -282,7 +330,12 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, ret = T_CHECKOUT; else { +#ifdef SERVER_SUPPORT + sticky_ck (file, aflag, vers, entries, + repository, update_dir); +#else sticky_ck (file, aflag, vers, entries); +#endif ret = T_UPTODATE; } } @@ -309,7 +362,12 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, ret = T_NEEDS_MERGE; #else ret = T_MODIFIED; +#ifdef SERVER_SUPPORT + sticky_ck (file, aflag, vers, entries, + repository, update_dir); +#else sticky_ck (file, aflag, vers, entries); +#endif /* SERVER_SUPPORT */ #endif } else @@ -352,7 +410,17 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, /* * The user file is still unmodified, so just get it as well */ +#ifdef SERVER_SUPPORT + if (strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) != 0 + || (vers->srcfile != NULL + && (vers->srcfile->flags & INATTIC) != 0)) + ret = T_CHECKOUT; + else + ret = T_PATCH; +#else ret = T_CHECKOUT; +#endif } else { @@ -360,9 +428,21 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, repository, update_dir)) /* really modified, needs to merge */ ret = T_NEEDS_MERGE; +#ifdef SERVER_SUPPORT + else if ((strcmp (vers->entdata->options ? + vers->entdata->options : "", vers->options) + != 0) + || (vers->srcfile != NULL + && (vers->srcfile->flags & INATTIC) != 0)) + /* not really modified, check it out */ + ret = T_CHECKOUT; + else + ret = T_PATCH; +#else else /* not really modified, check it out */ ret = T_CHECKOUT; +#endif } } } @@ -380,11 +460,19 @@ Classify_File (file, tag, date, options, force_tag_match, aflag, repository, } static void +#ifdef SERVER_SUPPORT +sticky_ck (file, aflag, vers, entries, repository, update_dir) +#else sticky_ck (file, aflag, vers, entries) +#endif char *file; int aflag; Vers_TS *vers; List *entries; +#ifdef SERVER_SUPPORT + char *repository; + char *update_dir; +#endif { if (aflag || vers->tag || vers->date) { @@ -398,6 +486,19 @@ sticky_ck (file, aflag, vers, entries) { Register (entries, file, vers->vn_user, vers->ts_rcs, vers->options, vers->tag, vers->date, vers->ts_conflict); + +#ifdef SERVER_SUPPORT + if (server_active) + { + /* We need to update the entries line on the client side. + It is possible we will later update it again via + server_updated or some such, but that is OK. */ + server_update_entries + (file, update_dir, repository, + strcmp (vers->ts_rcs, vers->ts_user) == 0 ? + SERVER_UPDATED : SERVER_MERGED); + } +#endif } } } diff --git a/gnu/usr.bin/cvs/cvs/commit.c b/gnu/usr.bin/cvs/cvs/commit.c index cb36e9f..888804e 100644 --- a/gnu/usr.bin/cvs/cvs/commit.c +++ b/gnu/usr.bin/cvs/cvs/commit.c @@ -15,10 +15,11 @@ */ #include "cvs.h" +#include "getline.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)commit.c 1.101 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)commit.c 1.101 94/10/07 $"; +USE(rcsid); #endif static Dtype check_direntproc PROTO((char *dir, char *repos, char *update_dir)); @@ -26,16 +27,17 @@ static int check_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); static int checkaddfile PROTO((char *file, char *repository, char *tag, - List *srcfiles)); + char *options, List *srcfiles)); static Dtype commit_direntproc PROTO((char *dir, char *repos, char *update_dir)); static int commit_dirleaveproc PROTO((char *dir, int err, char *update_dir)); static int commit_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); static int commit_filesdoneproc PROTO((int err, char *repository, char *update_dir)); -static int finaladd PROTO((char *file, char *revision, char *tag, char *options, - char *repository, List *entries)); +static int finaladd PROTO((char *file, char *revision, char *tag, + char *options, char *update_dir, + char *repository, List *entries)); static int findmaxrev PROTO((Node * p, void *closure)); -static int fsortcmp PROTO((Node * p, Node * q)); +static int fsortcmp PROTO((const Node * p, const Node * q)); static int lock_RCS PROTO((char *user, char *rcs, char *rev, char *repository)); static int lock_filesdoneproc PROTO((int err, char *repository, char *update_dir)); static int lockrcsfile PROTO((char *file, char *repository, char *rev)); @@ -64,7 +66,7 @@ struct master_lists List *cilist; /* list with commit_info structs */ }; -static int force_ci; +static int force_ci = 0; static int got_message; static int run_module_prog = 1; static int aflag; @@ -75,7 +77,7 @@ static List *mulist; static List *locklist; static char *message; -static char *commit_usage[] = +static const char *const commit_usage[] = { "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n", "\t-n\tDo not run the module program (if any).\n", @@ -91,7 +93,7 @@ static char *commit_usage[] = int commit (argc, argv) int argc; - char *argv[]; + char **argv; { int c; int err = 0; @@ -188,7 +190,7 @@ commit (argc, argv) if (message) error (1, 0, "cannot specify both a message and a log file"); - if ((logfd = open (logfile, O_RDONLY)) < 0) + if ((logfd = open (logfile, O_RDONLY | OPEN_BINARY)) < 0) error (1, errno, "cannot open log file %s", logfile); if (fstat(logfd, &statbuf) < 0) @@ -203,17 +205,60 @@ commit (argc, argv) message[n] = '\0'; } +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* + * Do this now; don't ask for a log message if we can't talk to the + * server. But if there is a syntax error in the options, give + * an error message without connecting. + */ + start_server (); + + ign_setup (); + + /* + * We do this once, not once for each directory as in normal CVS. + * The protocol is designed this way. This is a feature. + * + * We could provide the lists of changed, modified, etc. files, + * however. Our failure to do so is just laziness, not design. + */ + if (use_editor) + do_editor (".", &message, (char *)NULL, (List *)NULL); + + /* We always send some sort of message, even if empty. */ + option_with_arg ("-m", message); + + if (local) + send_arg("-l"); + if (force_ci) + send_arg("-f"); + if (!run_module_prog) + send_arg("-n"); + option_with_arg ("-r", tag); + + send_files (argc, argv, local, 0); + + if (fprintf (to_server, "ci\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + /* XXX - this is not the perfect check for this */ if (argc <= 0) write_dirtag = tag; + wrap_setup (); + /* * Run the recursion processor to find all the dirs to lock and lock all * the dirs */ locklist = getlist (); - err = start_recursion ((int (*) ()) NULL, lock_filesdoneproc, - (Dtype (*) ()) NULL, (int (*) ()) NULL, argc, + err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, argc, argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0, 0); sortlist (locklist, fsortcmp); @@ -229,7 +274,7 @@ commit (argc, argv) * Run the recursion processor to verify the files are all up-to-date */ err = start_recursion (check_fileproc, check_filesdoneproc, - check_direntproc, (int (*) ()) NULL, argc, + check_direntproc, (DIRLEAVEPROC) NULL, argc, argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1, 0); if (err) @@ -261,7 +306,8 @@ commit (argc, argv) */ static int fsortcmp (p, q) - Node *p, *q; + const Node *p; + const Node *q; { return (strcmp (p->key, q->key)); } @@ -395,14 +441,17 @@ check_fileproc (file, update_dir, repository, entries, srcfiles) switch (status) { case T_CHECKOUT: +#ifdef SERVER_SUPPORT + case T_PATCH: +#endif case T_NEEDS_MERGE: case T_CONFLICT: + case T_REMOVE_ENTRY: if (update_dir[0] == '\0') error (0, 0, "Up-to-date check failed for `%s'", file); else error (0, 0, "Up-to-date check failed for `%s/%s'", update_dir, file); - error (0, 0, "Up-to-date check failed for `%s'", file); freevers_ts (&vers); return (1); case T_MODIFIED: @@ -463,9 +512,19 @@ check_fileproc (file, update_dir, repository, entries, srcfiles) * If the timestamp on the file is the same as the * timestamp stored in the Entries file, we block the commit. */ +#ifdef SERVER_SUPPORT + if (server_active) + retcode = vers->ts_conflict[0] != '='; + else { + filestamp = time_stamp (file); + retcode = strcmp (vers->ts_conflict, filestamp); + free (filestamp); + } +#else filestamp = time_stamp (file); retcode = strcmp (vers->ts_conflict, filestamp); free (filestamp); +#endif if (retcode == 0) { if (update_dir[0] == '\0') @@ -484,10 +543,10 @@ check_fileproc (file, update_dir, repository, entries, srcfiles) * If the timestamps differ, look for Conflict indicators * in the file to see if we should block the commit anyway */ - run_setup ("%s -s", GREP); + run_setup ("%s", GREP); run_arg (RCS_MERGE_PAT); run_arg (file); - retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY); if (retcode == -1) { @@ -532,7 +591,13 @@ check_fileproc (file, update_dir, repository, entries, srcfiles) { char rcs[PATH_MAX]; +#ifdef DEATH_SUPPORT + /* Don't look in the attic; if it exists there we will + move it back out in checkaddfile. */ + sprintf(rcs, "%s/%s%s", repository, file, RCSEXT); +#else locate_rcs (file, repository, rcs); +#endif if (isreadable (rcs)) { if (update_dir[0] == '\0') @@ -676,7 +741,7 @@ precommit_proc (repository, filter) char *filter; { /* see if the filter is there, only if it's a full path */ - if (filter[0] == '/') + if (isabsolute (filter)) { char *s, *cp; @@ -797,12 +862,33 @@ commit_fileproc (file, update_dir, repository, entries, srcfiles) } else if (ci->status == T_ADDED) { - if (checkaddfile (file, repository, ci->tag, srcfiles) != 0) + if (checkaddfile (file, repository, ci->tag, ci->options, + srcfiles) != 0) { fixaddfile (file, repository); err = 1; goto out; } + +#ifdef DEATH_SUPPORT + /* adding files with a tag, now means adding them on a branch. + Since the branch test was done in check_fileproc for + modified files, we need to stub it in again here. */ + + if (ci->tag) { + locate_rcs (file, repository, rcs); + ci->rev = RCS_whatbranch (file, ci->tag, srcfiles); + err = Checkin ('A', file, update_dir, repository, rcs, ci->rev, + ci->tag, ci->options, message, entries); + if (err != 0) + { + unlockrcs (file, repository); + fixbranch (file, repository, sbranch); + } + + ci->status = T_UPTODATE; + } +#endif /* DEATH_SUPPORT */ } /* @@ -825,14 +911,15 @@ commit_fileproc (file, update_dir, repository, entries, srcfiles) /* XXX - an added file with symbolic -r should add tag as well */ err = finaladd (file, ci->rev ? ci->rev : xrev, ci->tag, ci->options, - repository, entries); + update_dir, repository, entries); if (xrev) free (xrev); } else if (ci->status == T_MODIFIED) { locate_rcs (file, repository, rcs); - err = Checkin ('M', file, repository, rcs, ci->rev, ci->tag, + err = Checkin ('M', file, update_dir, repository, + rcs, ci->rev, ci->tag, ci->options, message, entries); if (err != 0) { @@ -841,8 +928,19 @@ commit_fileproc (file, update_dir, repository, entries, srcfiles) } } else if (ci->status == T_REMOVED) + { err = remove_file (file, repository, ci->tag, message, entries, srcfiles); +#ifdef SERVER_SUPPORT + if (server_active) { + server_scratch_entry_only (); + server_updated (file, update_dir, repository, + /* Doesn't matter, it won't get checked. */ + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); + } +#endif + } out: if (err != 0) @@ -888,18 +986,23 @@ commit_filesdoneproc (err, repository, update_dir) if (err == 0 && run_module_prog) { - char *cp; FILE *fp; - char line[MAXLINELEN]; - char *repository; - /* It is not an error if Checkin.prog does not exist. */ if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL) { - if (fgets (line, sizeof (line), fp) != NULL) + char *line; + int line_length; + size_t line_chars_allocated; + char *repository; + + line = NULL; + line_chars_allocated = 0; + line_length = getline (&line, &line_chars_allocated, fp); + if (line_length > 0) { - if ((cp = strrchr (line, '\n')) != NULL) - *cp = '\0'; + /* Remove any trailing newline. */ + if (line[line_length - 1] == '\n') + line[--line_length] = '\0'; repository = Name_Repository ((char *) NULL, update_dir); run_setup ("%s %s", line, repository); (void) printf ("%s %s: Executing '", program_name, @@ -909,7 +1012,21 @@ commit_filesdoneproc (err, repository, update_dir) (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); free (repository); } - (void) fclose (fp); + else + { + if (ferror (fp)) + error (0, errno, "warning: error reading %s", + CVSADM_CIPROG); + } + if (line != NULL) + free (line); + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", CVSADM_CIPROG); + } + else + { + if (! existence_error (errno)) + error (0, errno, "warning: cannot open %s", CVSADM_CIPROG); } } @@ -968,7 +1085,14 @@ commit_dirleaveproc (dir, err, update_dir) { /* update the per-directory tag info */ if (err == 0 && write_dirtag != NULL) + { WriteTag ((char *) NULL, write_dirtag, (char *) NULL); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_sticky (update_dir, Name_Repository (dir, update_dir), + write_dirtag, (char *) NULL); +#endif + } return (err); } @@ -1017,16 +1141,34 @@ remove_file (file, repository, tag, message, entries, srcfiles) char rcs[PATH_MAX]; char *tmp; +#ifdef DEATH_SUPPORT + int branch; + char *lockflag; + char *corev; + char *rev; + char *prev_rev; + Node *p; + RCSNode *rcsfile; + + corev = NULL; + rev = NULL; + prev_rev = NULL; + lockflag = 0; +#endif /* DEATH_SUPPORT */ + retcode = 0; locate_rcs (file, repository, rcs); +#ifdef DEATH_SUPPORT + branch = 0; + if (tag && !(branch = RCS_isbranch (file, tag, srcfiles))) +#else if (tag) +#endif { /* a symbolic tag is specified; just remove the tag from the file */ - run_setup ("%s%s -q -N%s", Rcsbin, RCS, tag); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0) + if ((retcode = RCS_deltag (rcs, tag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -1036,7 +1178,126 @@ remove_file (file, repository, tag, message, entries, srcfiles) Scratch_Entry (entries, file); return (0); } + +#ifdef DEATH_SUPPORT + /* we are removing the file from either the head or a branch */ + /* commit a new, dead revision. */ + + /* Print message indicating that file is going to be removed. */ + (void) printf ("Removing %s;\n", file); + + rev = NULL; + lockflag = "-l"; + if (branch) + { + char *branchname; + + rev = RCS_whatbranch (file, tag, srcfiles); + if (rev == NULL) + { + error (0, 0, "cannot find branch \"%s\".", tag); + return (1); + } + + p = findnode (srcfiles, file); + if (p == NULL) + { + error (0, 0, "boy, I'm confused."); + return (1); + } + rcsfile = (RCSNode *) p->data; + branchname = RCS_getbranch (rcsfile, rev, 1); + if (branchname == NULL) + { + /* no revision exists on this branch. use the previous + revision but do not lock. */ + corev = RCS_gettag (rcsfile, tag, 1, 0); + prev_rev = xstrdup(rev); + lockflag = ""; + } else + { + corev = xstrdup (rev); + prev_rev = xstrdup(branchname); + free (branchname); + } + + } else /* Not a branch */ + { + + /* Get current head revision of file. */ + p = findnode (srcfiles, file); + if (p == NULL) + { + error (0, 0, "could not find parsed rcsfile %s", file); + return (1); + } + rcsfile = (RCSNode *) p->data; + prev_rev = RCS_head (rcsfile); + } + + /* if removing without a tag or a branch, then make sure the default + branch is the trunk. */ + if (!tag && !branch) + { + if (RCS_setbranch (rcs, NULL) != 0) + { + error (0, 0, "cannot change branch to default for %s", + rcs); + return (1); + } + } + +#ifdef SERVER_SUPPORT + if (server_active) { + /* If this is the server, there will be a file sitting in the + temp directory which is the kludgy way in which server.c + tells time_stamp that the file is no longer around. Remove + it so we can create temp files with that name (ignore errors). */ + unlink_file (file); + } +#endif + + /* check something out. Generally this is the head. If we have a + particular rev, then name it. except when creating a branch, + lock the rev we're checking out. */ + run_setup ("%s%s %s %s%s %s", Rcsbin, RCS_CO, + lockflag, + rev ? "-r" : "", + rev ? corev : "", rcs); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) + != 0) { + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to check out `%s'", rcs); + return (1); + } + + if (corev != NULL) + free (corev); + +#ifdef DEATH_STATE + run_setup ("%s%s -f -sdead %s%s", Rcsbin, RCS_CI, rev ? "-r" : "", +#else + run_setup ("%s%s -K %s%s", Rcsbin, RCS_CI, rev ? "-r" : "", +#endif + rev ? rev : ""); + run_args ("-m%s", make_message_rcslegal (message)); + run_arg (rcs); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) + != 0) { + if (!quiet) + error (0, retcode == -1 ? errno : 0, + "failed to commit dead revision for `%s'", rcs); + return (1); + } + + if (rev != NULL) + free (rev); + + if (!branch) +#else /* No DEATH_SUPPORT */ else +#endif /* No DEATH_SUPPORT */ { /* this was the head; really move it into the Attic */ tmp = xmalloc(strlen(repository) + @@ -1046,31 +1307,56 @@ remove_file (file, repository, tag, message, entries, srcfiles) strlen(file) + sizeof(RCSEXT) + 1); (void) sprintf (tmp, "%s/%s", repository, CVSATTIC); - omask = umask (2); - (void) mkdir (tmp, 0777); + omask = umask (cvsumask); + (void) CVS_MKDIR (tmp, 0777); (void) umask (omask); (void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); +#ifdef DEATH_SUPPORT + if (strcmp (rcs, tmp) != 0 + && rename (rcs, tmp) == -1 + && (isreadable (rcs) || !isreadable (tmp))) + { + free(tmp); + return (1); + } + free(tmp); + } + + /* Print message that file was removed. */ + (void) printf ("%s <-- %s\n", rcs, file); + (void) printf ("new revision: delete; "); + (void) printf ("previous revision: %s\n", prev_rev); + (void) printf ("done\n"); + free(prev_rev); + + Scratch_Entry (entries, file); + return (0); +#else /* No DEATH_SUPPORT */ if ((strcmp (rcs, tmp) == 0 || rename (rcs, tmp) != -1) || (!isreadable (rcs) && isreadable (tmp))) { Scratch_Entry (entries, file); + /* FIXME: should free tmp. */ return (0); } + /* FIXME: should free tmp. */ } return (1); +#endif /* No DEATH_SUPPORT */ } /* * Do the actual checkin for added files */ static int -finaladd (file, rev, tag, options, repository, entries) +finaladd (file, rev, tag, options, update_dir, repository, entries) char *file; char *rev; char *tag; char *options; + char *update_dir; char *repository; List *entries; { @@ -1079,12 +1365,10 @@ finaladd (file, rev, tag, options, repository, entries) char rcs[PATH_MAX]; locate_rcs (file, repository, rcs); - ret = Checkin ('A', file, repository, rcs, rev, tag, options, + ret = Checkin ('A', file, update_dir, repository, rcs, rev, tag, options, message, entries); if (ret == 0) { - (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_OPT); - (void) unlink_file (tmp); (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG); (void) unlink_file (tmp); } @@ -1105,10 +1389,8 @@ unlockrcs (file, repository) int retcode = 0; locate_rcs (file, repository, rcs); - run_setup ("%s%s -q -u", Rcsbin, RCS); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not unlock %s", rcs); } @@ -1150,9 +1432,7 @@ fixbranch (file, repository, branch) if (branch != NULL && branch[0] != '\0') { locate_rcs (file, repository, rcs); - run_setup ("%s%s -q -b%s", Rcsbin, RCS, branch); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + if ((retcode = RCS_setbranch (rcs, branch)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "cannot restore branch to %s for %s", branch, rcs); } @@ -1165,24 +1445,26 @@ fixbranch (file, repository, branch) */ static int -checkaddfile (file, repository, tag, srcfiles) +checkaddfile (file, repository, tag, options, srcfiles) char *file; char *repository; char *tag; + char *options; List *srcfiles; { - FILE *fp; - char *cp; char rcs[PATH_MAX]; char fname[PATH_MAX]; mode_t omask; int retcode = 0; +#ifdef DEATH_SUPPORT + int newfile = 0; +#endif if (tag) { (void) sprintf(rcs, "%s/%s", repository, CVSATTIC); - omask = umask (2); - if (mkdir (rcs, 0777) != 0 && errno != EEXIST) + omask = umask (cvsumask); + if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST) error (1, errno, "cannot make directory `%s'", rcs);; (void) umask (omask); (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); @@ -1190,18 +1472,177 @@ checkaddfile (file, repository, tag, srcfiles) else locate_rcs (file, repository, rcs); - run_setup ("%s%s -i", Rcsbin, RCS); - run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); - (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT); - fp = open_file (fname, "r"); - while (fgets (fname, sizeof (fname), fp) != NULL) +#ifdef DEATH_SUPPORT + if (isreadable(rcs)) + { + /* file has existed in the past. Prepare to resurrect. */ + char oldfile[PATH_MAX]; + char *rev; + Node *p; + RCSNode *rcsfile; + + if (tag == NULL) + { + /* we are adding on the trunk, so move the file out of the + Attic. */ + strcpy (oldfile, rcs); + sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); + + if (strcmp (oldfile, rcs) == 0 + || rename (oldfile, rcs) != 0 + || isreadable (oldfile) + || !isreadable (rcs)) + { + error (0, 0, "failed to move `%s' out of the attic.", + file); + return (1); + } + } + + p = findnode (srcfiles, file); + if (p == NULL) + { + error (0, 0, "could not find parsed rcsfile %s", file); + return (1); + } + + rcsfile = (RCSNode *) p->data; + rev = RCS_getversion (rcsfile, tag, NULL, 1, 0); + /* and lock it */ + if (lock_RCS (file, rcs, rev, repository)) { + error (0, 0, "cannot lock `%s'.", rcs); + free (rev); + return (1); + } + + free (rev); + } else { + /* this is the first time we have ever seen this file; create + an rcs file. */ + run_setup ("%s%s -i", Rcsbin, RCS); + + (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); + /* If the file does not exist, no big deal. In particular, the + server does not (yet at least) create CVSEXT_LOG files. */ + if (isfile (fname)) + run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); + + /* Set RCS keyword expansion options. */ + if (options && options[0] == '-' && options[1] == 'k') + run_arg (options); + run_arg (rcs); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + { + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not create %s", rcs); + return (1); + } + newfile = 1; + } + + /* when adding a file for the first time, and using a tag, we need + to create a dead revision on the trunk. */ + if (tag && newfile) + { + char tmp[PATH_MAX]; + + /* move the new file out of the way. */ + (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); + rename_file (file, fname); + copy_file (DEVNULL, file); + + /* commit a dead revision. */ + (void) sprintf (tmp, "-mfile %s was initially added on branch %s.", file, tag); +#ifdef DEATH_STATE + run_setup ("%s%s -q -f -sdead", Rcsbin, RCS_CI); +#else + run_setup ("%s%s -q -K", Rcsbin, RCS_CI); +#endif + run_arg (tmp); + run_arg (rcs); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + { + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not create initial dead revision %s", rcs); + return (1); + } + + /* put the new file back where it was */ + rename_file (fname, file); + + /* and lock it once again. */ + if (lock_RCS (file, rcs, NULL, repository)) { + error (0, 0, "cannot lock `%s'.", rcs); + return (1); + } + } + + if (tag != NULL) { - if ((cp = strrchr (fname, '\n')) != NULL) - *cp = '\0'; - if (*fname) - run_arg (fname); + /* when adding with a tag, we need to stub a branch, if it + doesn't already exist. */ + Node *p; + RCSNode *rcsfile; + + rcsfile = RCS_parse (file, repository); + if (rcsfile == NULL) + { + error (0, 0, "could not read %s", rcs); + return (1); + } + + if (!RCS_nodeisbranch (tag, rcsfile)) { + /* branch does not exist. Stub it. */ + char *head; + char *magicrev; + + head = RCS_getversion (rcsfile, NULL, NULL, 0, 0); + magicrev = RCS_magicrev (rcsfile, head); + if ((retcode = RCS_settag(rcs, tag, magicrev)) != 0) + { + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not stub branch %s for %s", tag, rcs); + return (1); + } + + freercsnode (&rcsfile); + + /* reparse the file, then add it to our list. */ + rcsfile = RCS_parse (file, repository); + if (rcsfile == NULL) + { + error (0, 0, "could not reparse %s", rcs); + return (1); + } + + free (head); + free (magicrev); + } + else + { + /* lock the branch. (stubbed branches need not be locked.) */ + if (lock_RCS (file, rcs, NULL, repository)) { + error (0, 0, "cannot lock `%s'.", rcs); + return (1); + } + } + + /* add (replace) this rcs file to our list */ + p = findnode (srcfiles, file); + + if (p != NULL) + freercsnode((RCSNode **) &p->data); + + delnode(p); + + RCS_addnode (file, rcsfile, srcfiles); } - (void) fclose (fp); +#else /* No DEATH_SUPPORT */ + run_setup ("%s%s -i", Rcsbin, RCS); + run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); + /* Set RCS keyword expansion options. */ + if (options && options[0] == '-' && options[1] == 'k') + run_arg (options); run_arg (rcs); if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) { @@ -1209,6 +1650,7 @@ checkaddfile (file, repository, tag, srcfiles) "could not create %s", rcs); return (1); } +#endif /* No DEATH_SUPPORT */ fix_rcs_modes (rcs, file); return (0); @@ -1272,9 +1714,7 @@ lock_RCS (user, rcs, rev, repository) freercsnode (&rcsfile); if (branch != NULL) { - run_setup ("%s%s -q -b", Rcsbin, RCS); - run_arg (rcs); - if (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0) + if (RCS_setbranch (rcs, NULL) != 0) { error (0, 0, "cannot change branch to default for %s", rcs); @@ -1283,16 +1723,12 @@ lock_RCS (user, rcs, rev, repository) return (1); } } - run_setup ("%s%s -q -l", Rcsbin, RCS); - run_arg (rcs); - err = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + err = RCS_lock(rcs, NULL, 0); } } else { - run_setup ("%s%s -q -l%s", Rcsbin, RCS, rev ? rev : ""); - run_arg (rcs); - (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL); + (void) RCS_lock(rcs, rev, 1); } if (err == 0) diff --git a/gnu/usr.bin/cvs/cvs/create_adm.c b/gnu/usr.bin/cvs/cvs/create_adm.c index 55eb649..1fe8185 100644 --- a/gnu/usr.bin/cvs/cvs/create_adm.c +++ b/gnu/usr.bin/cvs/cvs/create_adm.c @@ -14,13 +14,16 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)create_adm.c 1.28 94/09/23 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)create_adm.c 1.28 94/09/23 $"; +USE(rcsid); #endif +/* update_dir includes dir as its last component. */ + void -Create_Admin (dir, repository, tag, date) +Create_Admin (dir, update_dir, repository, tag, date) char *dir; + char *update_dir; char *repository; char *tag; char *date; @@ -29,6 +32,18 @@ Create_Admin (dir, repository, tag, date) char *cp; char tmp[PATH_MAX]; +#ifdef SERVER_SUPPORT + if (trace) + { + char wd[PATH_MAX]; + getwd (wd); + fprintf (stderr, "%c-> Create_Admin (%s, %s, %s, %s, %s) in %s\n", + (server_active) ? 'S' : ' ', + dir, update_dir, repository, tag ? tag : "", + date ? date : "", wd); + } +#endif + if (noexec) return; @@ -36,24 +51,9 @@ Create_Admin (dir, repository, tag, date) (void) sprintf (tmp, "%s/%s", dir, CVSADM); else (void) strcpy (tmp, CVSADM); - if (isfile (tmp)) - error (1, 0, "there is a version here already"); - else - { - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, OCVSADM); - else - (void) strcpy (tmp, OCVSADM); - - if (isfile (tmp)) - error (1, 0, "there is a version here already"); - } + error (1, 0, "there is a version in %s already", update_dir); - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM); - else - (void) strcpy (tmp, CVSADM); make_directory (tmp); #ifdef CVSADM_ROOT @@ -65,7 +65,14 @@ Create_Admin (dir, repository, tag, date) (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP); else (void) strcpy (tmp, CVSADM_REP); - fout = open_file (tmp, "w+"); + fout = fopen (tmp, "w+"); + if (fout == NULL) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot open %s", tmp); + else + error (1, errno, "cannot open %s/%s", update_dir, CVSADM_REP); + } cp = repository; strip_path (cp); @@ -84,20 +91,54 @@ Create_Admin (dir, repository, tag, date) } #endif - if (fprintf (fout, "%s\n", cp) == EOF) - error (1, errno, "write to %s failed", tmp); + if (fprintf (fout, "%s\n", cp) < 0) + { + if (update_dir[0] == '\0') + error (1, errno, "write to %s failed", tmp); + else + error (1, errno, "write to %s/%s failed", update_dir, CVSADM_REP); + } if (fclose (fout) == EOF) - error (1, errno, "cannot close %s", tmp); + { + if (update_dir[0] == '\0') + error (1, errno, "cannot close %s", tmp); + else + error (1, errno, "cannot close %s/%s", update_dir, CVSADM_REP); + } /* now, do the Entries file */ if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); else (void) strcpy (tmp, CVSADM_ENT); - fout = open_file (tmp, "w+"); + fout = fopen (tmp, "w+"); + if (fout == NULL) + { + if (update_dir[0] == '\0') + error (1, errno, "cannot open %s", tmp); + else + error (1, errno, "cannot open %s/%s", update_dir, CVSADM_ENT); + } if (fclose (fout) == EOF) - error (1, errno, "cannot close %s", tmp); + { + if (update_dir[0] == '\0') + error (1, errno, "cannot close %s", tmp); + else + error (1, errno, "cannot close %s/%s", update_dir, CVSADM_ENT); + } /* Create a new CVS/Tag file */ WriteTag (dir, tag, date); + +#ifdef SERVER_SUPPORT + if (server_active) + server_set_sticky (update_dir, repository, tag, date); + + if (trace) + { + fprintf (stderr, "%c<- Create_Admin\n", + (server_active) ? 'S' : ' '); + } +#endif + } diff --git a/gnu/usr.bin/cvs/cvs/cvs.1 b/gnu/usr.bin/cvs/cvs/cvs.1 index bd9e034..5a70301 100644 --- a/gnu/usr.bin/cvs/cvs/cvs.1 +++ b/gnu/usr.bin/cvs/cvs/cvs.1 @@ -2,7 +2,7 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: cvs.1,v 1.16 1994/10/03 21:26:10 berliner Exp $ +.Id $Id: cvs.1,v 1.7 1995/11/15 01:02:41 woods Exp $ .TH CVS 1 "\*(Dt" .\" Full space in nroff; half space in troff .de SP @@ -128,16 +128,28 @@ command). There are only two situations where you may omit .IR cvs_command : .` "cvs \-H" +or +.` "cvs --help" elicits a list of available commands, and -.` "cvs \-v " +.` "cvs \-v" +or +.` "cvs --version" displays version information on \fBcvs\fP itself. .SP .SH "CVS OPTIONS" +As of release 1.6, +.B cvs +supports +.SM GNU +style long options as well as short options. Only +a few long options are currently supported, these are listed in +brackets after the short options whose functions they duplicate. +.SP Use these options to control the overall .B cvs program: .TP -.B \-H +.B \-H [ --help ] Display usage information about the specified .I cvs_command (but do not actually execute the command). If you don't specify a @@ -186,6 +198,11 @@ and the .SM EDITOR environment variables. .TP +.B \-f +Do not read the +.B cvs +startup file (\fI~/.cvsrc\fP). +.TP .B \-l Do not log the .I cvs_command @@ -213,7 +230,7 @@ Same effect as if the .SM CVSREAD environment variable is set. .TP -.B \-v +.B \-v [ --version ] Displays version and copyright information for .BR cvs . .TP @@ -222,6 +239,16 @@ Makes new working files read-write (default). Overrides the setting of the .SM CVSREAD environment variable. +.TP +\fB\-z\fP \fIcompression\-level\fP +When transferring files across the network use +.B gzip +with compression level \fIcompression\-level\fP to compress and +de-compress data as it is transferred. Requires the presence of +the +.SM GNU +.B gzip +program in the current search path at both ends of the link. .SH "USAGE" Except when requesting general help with .` "cvs \-H", @@ -237,6 +264,29 @@ However, many options are available across several commands. You can display a usage summary for each command by specifying the .B \-H option with the command. +.SH "CVS STARTUP FILE" +Normally, when CVS starts up, it reads the +.I .cvsrc +file from the home directory of the user reading it. This startup +procedure can be turned off with the +.B \-f +flag. +.SP +The +.I .cvsrc +file lists CVS commands with a list of arguments, one command per +line. For example, the following line in \fI.cvsrc\fP: +.SP +diff \-c +.SP +will mean that the +.` "cvs diff" +command will always be passed the \-c option in addition to any +other options that are specified in the command line (in this case +it will have the effect of producing context sensitive diffs for +all executions of +.` "cvs diff" +). .SH "CVS COMMAND SUMMARY" Here are brief descriptions of all the .B cvs @@ -343,7 +393,8 @@ file. (Does not change repository or working directory.) .TP .B tag -Specify a symbolic tag for files in the repository. Tags the revisions +Specify a symbolic tag for files in the repository. By default, tags +the revisions that were last synchronized with your working directory. (Changes repository directly; uses working directory without changing it.) .TP @@ -436,9 +487,9 @@ Alter the default processing of keywords; all the .B \-k options described in -.BR rcs ( 1 ) +.BR co ( 1 ) are available. The \fB\-k\fP option is available with the -.BR add ", " checkout ", " diff ", " +.BR add ", " checkout ", " diff ", " export ", " .BR rdiff ", and " update commands. Your \fIkflag\fP specification is ``sticky'' when you use it to create a private copy of a source file; that is, when you use @@ -446,6 +497,15 @@ this option with the \fBcheckout\fP or \fBupdate\fP commands, \fBcvs\fP associates your selected \fIkflag\fP with the file, and continues to use it with future \fBupdate\fP commands on the same file until you specify otherwise. +.SP +Some of the more useful \fIkflag\fPs are \-ko and \-kb (for binary files, +only compatible with +.SM RCS +version 5.7 or later), and \-kv which is useful for an +.B export +where you wish to retain keyword information after an +.B import +at some other site. .TP .B \-l Local; run only in current working directory, rather than recurring through @@ -508,23 +568,6 @@ rather than writing them in the current directory. Available with the .BR checkout " and " update commands. .TP -.B \-Q -Causes the command to be -.I really -quiet; the command will generate output only for serious problems. -Available with the following commands: -.BR checkout ", " import ", " export ", " -.BR rdiff ", " rtag ", " -.BR tag ", and " update . -.TP -.B \-q -Causes the command to be somewhat quiet; informational messages, such -as reports of recursion through subdirectories, are suppressed. -Available with the following commands: -.BR checkout ", " import ", " export ", " -.BR rtag ", " -.BR tag ", and " update . -.TP \fB\-r\fP \fItag\fP Use the revision specified by the .I tag @@ -553,9 +596,9 @@ can be either a symbolic or numeric tag, in fashion. Specifying the .B \-q -option along with the +global option along with the .B \-r -option is often useful, to suppress the warning messages when the +command option is often useful, to suppress the warning messages when the .SM RCS file does not contain the specified tag. .B \-r @@ -787,7 +830,7 @@ sure that it will be a sub-directory, and that will show the relative path leading to each file as it is extracted into your private work area (unless you specify the .B \-Q -option). +global option). .SP Running .` "cvs checkout" @@ -805,11 +848,11 @@ The permitted with .` "cvs checkout" include the standard command options -.BR \-P ", " \-Q ", " \-f ", " +.BR \-P ", " \-f ", " .BI \-k " kflag" \&, .BR \-l ", " \-n ", " \-p ", " -.BR \-q ", " \-r +.BR \-r .IR tag ", and" .BI \-D " date"\c \&. @@ -1068,7 +1111,7 @@ differ from the corresponding revision in the source repository .I you have changed), or that differ from the revision specified. .TP -\fBexport\fP [\-\fBf\|lNnQq\fP] \fB\-r\fP \fIrev\fP\||\|\fB\-D\fP \fIdate\fP [\fB\-d\fP \fIdir\fP] \fImodule\fP.\|.\|. +\fBexport\fP [\-\fBf\|lNnQq\fP] \fB\-r\fP \fIrev\fP\||\|\fB\-D\fP \fIdate\fP [\fB\-d\fP \fIdir\fP] [\fB\-k\fP \fIkflag\fP] \fImodule\fP.\|.\|. .I Requires: repository. .br @@ -1098,7 +1141,7 @@ These have the same meanings as the same options in .SP The .B \-kv -option is always set when +option is useful when .B export is used. This causes any @@ -1106,7 +1149,10 @@ This causes any keywords to be expanded such that an .B import done at some other site will not lose the keyword revision information. -There is no way to override this. +Other \fIkflag\fPs may be used with +.` "cvs export" +and are described in +.BR co ( 1 ). .TP \fBhistory\fP [\fB\-\fP\fIreport\fP] [\fB\-\fP\fIflags\fP] [\fB\-\fP\fIoptions args\fP] [\fIfiles\fP.\|.\|.] .I Requires: @@ -1310,9 +1356,9 @@ least one \fIreleasetag\fP to identify the files at the leaves created each time you execute .` "cvs import". .SP -Three of the standard +One of the standard .B cvs -command options are available: \fB\-Q\fP, \fB\-q\fP, and \fB\-m\fP +command options is available: \fB\-m\fP \fImessage\fP. If you do not specify a logging message with \fB\-m\fP, your editor is invoked (as with \fBcommit\fP) to allow you to enter one. @@ -1411,8 +1457,8 @@ version \fIvn\fP (the expansion format changed with .SM RCS version 5). .SP -The standard option \fIflags\fP \fB\-f\fP, \fB\-l\fP, \fB\-Q\fP, and -\fB\-q\fP are available with this command. There are also several +The standard option \fIflags\fP \fB\-f\fP, and \fB\-l\fP +are available with this command. There are also several special options flags: .SP If you use the @@ -1543,6 +1589,17 @@ should not be removed, but the option is available as a means to remove completely obsolete symbolic names if necessary (as might be the case for an Alpha release, say). .SP +.` "cvs rtag" +will not move a tag that already exists. With the \fB\-F\fP option, +however, +.` "cvs rtag" +will re-locate any instance of \fIsymbolic_tag\fP that already exists +on that file to the new repository versions. Without the \fB\-F\fP +option, attempting to use +.` "cvs rtag" +to apply a tag that already exists on that file will produce an error +message. +.SP The \fB-b\fP option makes the tag a ``branch'' tag, allowing concurrent, isolated development. This is most useful for creating a patch to a previously released software @@ -1564,9 +1621,6 @@ is specified; a typical use is to send electronic mail to a group of interested parties. If you want to bypass that program, use the standard \fB\-n\fP option. .SP -The standard options \fB\-Q\fP and \fB\-q\fP are also available with -this command. -.SP Use the .B \-a option to have @@ -1578,7 +1632,7 @@ The tag is removed from these files, which makes it convenient to re-use a symbolic tag as development continues (and files get removed from the up-coming distribution). .TP -\fBstatus\fP [\fB\-lR\fP] [\fB\-v\fP] [\fIfiles\fP\|.\|.\|.] +\fBstatus\fP [\fB\-lRqQ\fP] [\fB\-v\fP] [\fIfiles\fP\|.\|.\|.] .I Requires: working directory, repository. .br @@ -1609,7 +1663,7 @@ option causes the symbolic tags for the .SM RCS file to be displayed as well. .TP -\fBtag\fP [\fB\-lQqR\fP] [\fB\-b\fP] [\fB\-d\fP] \fIsymbolic_tag\fP [\fIfiles\fP\|.\|.\|.\|] +\fBtag\fP [\fB\-lQqR\fP] [\fB\-F\fP] [\fB\-b\fP] [\fB\-d\fP] [\fB\-r\fP \fItag\fP | \fB\-D\fP \fIdate\fP] [\fB\-f\fP] \fIsymbolic_tag\fP [\fIfiles\fP\|.\|.\|.\|] .I Requires: working directory, repository. .br @@ -1621,9 +1675,7 @@ repository. .br Use this command to assign symbolic tags to the nearest repository versions to your working sources. The tags are applied immediately to -the repository, as with \fBrtag\fP, but the versions are supplied -implicitly by the \fBcvs\fP records of your working files' history -rather than applied explicitly. +the repository, as with \fBrtag\fP. .SP One use for tags is to record a ``snapshot'' of the current sources when the software freeze date of a project arrives. As bugs are fixed @@ -1633,13 +1685,28 @@ of the release need be re-tagged. The symbolic tags are meant to permanently record which revisions of which files were used in creating a software distribution. The -.B checkout +.BR checkout , +.B export and .B update commands allow you to extract an exact copy of a tagged release at any time in the future, regardless of whether files have been changed, added, or removed since the release was tagged. .SP +You can use the standard \fB\-r\fP and \fB\-D\fP options to tag only those +files that already contain a certain tag. This method would be used +to rename a tag: tag only the files identified by the old tag, then delete the +old tag, leaving the new tag on exactly the same files as the old tag. +.SP +Specifying the \fB\-f\fP flag in addition to the \fB\-r\fP or \fB\-D\fP +flags will tag those files named on the command line even if they do not +contain the old tag or did not exist on the specified date. +.SP +By default (without a \fB\-r\fP or \fB\-D\fP flag) +the versions to be tagged are supplied +implicitly by the \fBcvs\fP records of your working files' history +rather than applied explicitly. +.SP If you use .` "cvs tag \-d \fIsymbolic_tag\fP\|.\|.\|.", the @@ -1649,6 +1716,17 @@ instead of being added. \fIWarning\fP: Be very certain of your ground before you delete a tag; doing this effectively discards some historical information, which may later turn out to have been valuable. .SP +.` "cvs tag" +will not move a tag that already exists. With the \fB\-F\fP option, +however, +.` "cvs tag" +will re-locate any instance of \fIsymbolic_tag\fP that already exists +on that file to the new repository versions. Without the \fB\-F\fP +option, attempting to use +.` "cvs tag" +to apply a tag that already exists on that file will produce an error +message. +.SP The \fB-b\fP option makes the tag a ``branch'' tag, allowing concurrent, isolated development. This is most useful for creating a patch to a previously released software @@ -1834,7 +1912,7 @@ Use to avoid ignoring any files at all. .SP The standard \fBcvs\fP command options \fB\-f\fP, \fB\-k\fP, -\fB\-l\fP, \fB\-P\fP, \fB\-p\fP, \fB\-Q\fP, \fB\-q\fP, and \fB\-r\fP +\fB\-l\fP, \fB\-P\fP, \fB\-p\fP, and \fB\-r\fP are also available with \fBupdate\fP. .RE .SH "FILES" @@ -1844,6 +1922,27 @@ supporting files, see .BR cvs ( 5 ). .LP .I +Files in home directories: +.TP +\&.cvsrc +The +.B cvs +initialisation file. Lines in this file can be used to specify default +options for each +.B cvs +command. For example the line +.` "diff \-c" +will ensure that +.` "cvs diff" +is always passed the +.B \-c +option in addition to any other options passed on the command line. +.TP +\&.cvswrappers +Specifies wrappers to be used in addition to those specified in the +CVSROOT/cvswrappers file in the repository. +.LP +.I Files in working directories: .TP CVS @@ -1862,6 +1961,21 @@ CVS/Entries.Static Flag: do not add more entries on .` "cvs update". .TP +CVS/Root +Pathname to the repository ( +.SM CVSROOT +) location at the time of checkout. This file is used instead +of the +.SM CVSROOT +environment variable if the environment variable is not +set. A warning message will be issued when the contents of this +file and the +.SM CVSROOT +environment variable differ. The file may be over-ridden by the +presence of the +.SM CVS_IGNORE_REMOTE_ROOT +environment variable. +.TP CVS/Repository Pathname to the corresponding directory in the source repository. .TP @@ -1896,28 +2010,44 @@ Records programs for filtering .` "cvs commit" requests. .TP +CVSROOT/cvswrappers,v +Records +.B cvs +wrapper commands to be used when checking files into and out of the +repository. Wrappers allow the file or directory to be processed +on the way in and out of CVS. The intended uses are many, one +possible use would be to reformat a C file before the file is checked +in, so all of the code in the repository looks the same. +.TP +CVSROOT/editinfo,v +Records programs for editing/validating +.` "cvs commit" +log entries. +.TP CVSROOT/history Log file of \fBcvs\fP transactions. .TP -CVSROOT/modules,v -Definitions for modules in this repository. -.TP CVSROOT/loginfo,v Records programs for piping .` "cvs commit" log entries. .TP +CVSROOT/modules,v +Definitions for modules in this repository. +.TP CVSROOT/rcsinfo,v Records pathnames to templates used during a .` "cvs commit" operation. .TP -CVSROOT/editinfo,v -Records programs for editing/validating -.` "cvs commit" -log entries. +CVSROOT/taginfo,v +Records programs for validating/logging +.` "cvs tag" +and +.` "cvs rtag" +operations. .TP -Attic +MODULE/Attic Directory for removed source files. .TP #cvs.lock @@ -1983,6 +2113,36 @@ If .SM EDITOR is not set either, the default is .BR /usr/ucb/vi . +.TP +.SM CVS_IGNORE_REMOTE_ROOT +If this variable is set then +.B cvs +will ignore all references to remote repositories in the CVS/Root file. +.TP +.SM CVS_RSH +.B cvs +uses the contents of this variable to determine the name of the +remote shell command to use when starting a +.B cvs +server. If this variable is not set then +.` "rsh" +is used. +.TP +.SM CVS_SERVER +.B cvs +uses the contents of this variable to determine the name of the +.B cvs +server command. If this variable is not set then +.` "cvs" +is used. +.TP +.SM CVSWRAPPERS +This variable is used by the +.` "cvswrappers" +script to determine the name of the wrapper file, in addition to the +wrappers defaults contained in the repository +.SM (CVSROOT/cvswrappers) +and the user's home directory (~/.cvswrappers). .SH "AUTHORS" .TP Dick Grune @@ -2011,6 +2171,8 @@ shell script (the ancestor of .BR ci ( 1 ), .BR co ( 1 ), .BR cvs ( 5 ), +.BR cvsbug ( 8 ), +.BR cvsinit ( 8 ), .BR diff ( 1 ), .BR grep ( 1 ), .BR mkmodules ( 1 ), diff --git a/gnu/usr.bin/cvs/cvs/cvs.5 b/gnu/usr.bin/cvs/cvs/cvs.5 index 9c477f3..cb4f455 100644 --- a/gnu/usr.bin/cvs/cvs/cvs.5 +++ b/gnu/usr.bin/cvs/cvs/cvs.5 @@ -10,19 +10,23 @@ cvs \- Concurrent Versions System support files .hy 0 .na .TP -.B $CVSROOT/CVSROOT/modules,v -.TP .B $CVSROOT/CVSROOT/commitinfo,v .TP -.B $CVSROOT/CVSROOT/loginfo,v +.B $CVSROOT/CVSROOT/cvsignore,v .TP -.B $CVSROOT/CVSROOT/rcsinfo,v +.B $CVSROOT/CVSROOT/cvswrappers,v .TP .B $CVSROOT/CVSROOT/editinfo,v .TP -.B $CVSROOT/CVSROOT/cvsignore,v -.TP .B $CVSROOT/CVSROOT/history +.TP +.B $CVSROOT/CVSROOT/loginfo,v +.TP +.B $CVSROOT/CVSROOT/modules,v +.TP +.B $CVSROOT/CVSROOT/rcsinfo,v +.TP +.B $CVSROOT/CVSROOT/taginfo,v .ad b .hy 1 .SH DESCRIPTION @@ -67,6 +71,14 @@ source repository from a particular person or group. Or, perhaps, to verify that the changed files conform to the site's standards for coding practice. .SP +You can use the `\|cvswrappers\|' file to record +.B cvs +wrapper commands to be used when checking files into and out of the +repository. Wrappers allow the file or directory to be processed +on the way in and out of CVS. The intended uses are many, one +possible use would be to reformat a C file before the file is checked +in, so all of the code in the repository looks the same. +.SP You can use the `\|loginfo\|' file to define programs to execute after any .BR commit , @@ -75,6 +87,14 @@ These logging programs might be used to append the log message to a file. Or send the log message through electronic mail to a group of developers. Or, perhaps, post the log message to a particular newsgroup. .SP +You can use the `\|taginfo\|' file to define programs to execute after +any +.BR tag or rtag +operation. These programs might be used to append a message to a file +listing the new tag name and the programmer who created it, or send mail +to a group of developers, or, perhaps, post a message to a particular +newsgroup. +.SP You can use the `\|rcsinfo\|' file to define forms for log messages. .SP You can use the `\|editinfo\|' file to define a program to execute for @@ -127,7 +147,7 @@ to get an editable copy of the file. You should define similar module entries for the other configuration files described here (except \&`\|history\|'). The -.BR cvsinit ( 1 ) +.BR cvsinit ( 8 ) script will setup a smilar `\|modules\|' file for you automatically. .SP The `\|modules\|' file may contain blank lines and comments (lines @@ -210,6 +230,10 @@ source repository. The `\|commitinfo\|', `\|loginfo\|', and to run whenever files in a module are checked out. \fIprog\fP runs with a single argument, the module name. .SP +`\|\fB\-e\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP +to run whenever files in a module are exported. \fIprog\fP runs +with a single argument, the module name. +.SP `\|\fB\-t\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP to run whenever files in a module are tagged. \fIprog\fP runs with two arguments: the module name and the symbolic tag specified to \fBrtag\fP. @@ -241,6 +265,23 @@ repository where the change is being made. The remaining arguments list the files that are being modified, added, or removed by this \fBcommit\fP invocation. .SP +For `\|taginfo\|', the rest of the +line is a command-line template to execute. +The arguments passed to the command are, in order, the +.I tagname , +.I operation +(i.e. +.B add +for `tag', +.B mov +for `tag -F', and +.B del +for `tag -d`), +.I repository , +and any remaining are pairs of +.B "filename revision" . +A non-zero exit of the filter program will cause the tag to be aborted. +.SP For `\|commitinfo\|', the rest of the line is a command-line template to execute. The template can include not only a program name, but whatever diff --git a/gnu/usr.bin/cvs/cvs/cvs.h b/gnu/usr.bin/cvs/cvs/cvs.h index 95c6a9b..34c9d8c 100644 --- a/gnu/usr.bin/cvs/cvs/cvs.h +++ b/gnu/usr.bin/cvs/cvs/cvs.h @@ -21,16 +21,20 @@ #ifdef _AIX #pragma alloca #else /* not _AIX */ +#ifdef ALLOCA_IN_STDLIB + /* then we need do nothing */ +#else char *alloca (); +#endif /* not ALLOCA_IN_STDLIB */ #endif /* not _AIX */ #endif /* not HAVE_ALLOCA_H */ #endif /* not __GNUC__ */ -#if __STDC__ -#define CONST const +/* Changed from if __STDC__ to ifdef __STDC__ because of Sun's acc compiler */ + +#ifdef __STDC__ #define PTR void * #else -#define CONST #define PTR char * #endif @@ -44,17 +48,29 @@ char *alloca (); #endif #if __GNUC__ == 2 -#define USE(var) static char sizeof##var = sizeof(sizeof##var) + sizeof(var); +#define USE(var) static const char sizeof##var = sizeof(sizeof##var) + sizeof(var) #else -#define USE(var) +#define USE(var) static const char standalone_semis_illegal_sigh #endif #include <stdio.h> +/* Under OS/2, <stdio.h> doesn't define popen()/pclose(). */ +#ifdef USE_OWN_POPEN +#include "popen.h" +#endif + +#ifdef STDC_HEADERS +#include <stdlib.h> +#else +extern void exit (); +extern char *getenv(); +#endif + #ifdef HAVE_UNISTD_H #include <unistd.h> -#endif +#endif #ifdef HAVE_STRING_H #include <string.h> @@ -62,7 +78,13 @@ char *alloca (); #include <strings.h> #endif -#include <fnmatch.h> /* This is supposed to be available on Posix systems */ +#ifdef SERVER_SUPPORT +/* If the system doesn't provide strerror, it won't be declared in + string.h. */ +char *strerror (); +#endif + +#include <fnmatch.h> /* This is supposed to be available on Posix systems */ #include <ctype.h> #include <pwd.h> @@ -79,6 +101,18 @@ extern int errno; #include "system.h" #include "hash.h" +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +#include "server.h" +#include "client.h" +#endif + +#ifdef AUTH_CLIENT_SUPPORT +extern int use_authenticating_server; +void connect_to_pserver(); +# ifndef CVS_AUTH_PORT +# define CVS_AUTH_PORT 2401 +# endif /* CVS_AUTH_PORT */ +#endif /* AUTH_CLIENT_SUPPORT */ #ifdef MY_NDBM #include "myndbm.h" @@ -90,6 +124,11 @@ extern int errno; #include "getopt.h" #include "wait.h" +/* Define to enable alternate death support (which uses the RCS state). */ +#define DEATH_STATE 1 + +#define DEATH_SUPPORT 1 + #include "rcs.h" @@ -111,16 +150,17 @@ extern int errno; /* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner - * + * * You may distribute under the terms of the GNU General Public License as * specified in the README file that comes with the CVS 1.4 kit. - * + * * Definitions for the CVS Administrative directory and the files it contains. * Here as #define's to make changing the names a simple task. */ #define CVSADM "CVS" #define CVSADM_ENT "CVS/Entries" #define CVSADM_ENTBAK "CVS/Entries.Backup" +#define CVSADM_ENTLOG "CVS/Entries.Log" #define CVSADM_ENTSTAT "CVS/Entries.Static" #define CVSADM_REP "CVS/Repository" #define CVSADM_ROOT "CVS/Root" @@ -129,14 +169,6 @@ extern int errno; #define CVSADM_TAG "CVS/Tag" /* - * The following are obsolete and are maintained here only so that they can be - * cleaned up during the transition - */ -#define OCVSADM "CVS.adm" /* for CVS 1.2 and earlier */ -#define CVSADM_FILE "CVS/Files" -#define CVSADM_MOD "CVS/Mod" - -/* * Definitions for the CVSROOT Administrative directory and the files it * contains. This directory is created as a sub-directory of the $CVSROOT * environment variable, and holds global administration information for the @@ -147,33 +179,36 @@ extern int errno; #define CVSROOTADM_LOGINFO "loginfo" #define CVSROOTADM_RCSINFO "rcsinfo" #define CVSROOTADM_COMMITINFO "commitinfo" +#define CVSROOTADM_TAGINFO "taginfo" #define CVSROOTADM_EDITINFO "editinfo" #define CVSROOTADM_HISTORY "history" #define CVSROOTADM_IGNORE "cvsignore" #define CVSROOTADM_CHECKOUTLIST "checkoutlist" +#define CVSROOTADM_WRAPPER "cvswrappers" #define CVSNULLREPOS "Emptydir" /* an empty directory */ /* support for the modules file (CVSROOTADM_MODULES) */ -#define CVSMODULE_OPTS "ad:i:lo:s:t:u:"/* options in modules file */ +#define CVSMODULE_OPTS "ad:i:lo:e:s:t:u:"/* options in modules file */ #define CVSMODULE_SPEC '&' /* special delimiter */ -/* - * The following are obsolete and are maintained here only so that they can be - * cleaned up during the transition - */ -#define OCVSROOTADM "CVSROOT.adm" /* for CVS 1.2 and earlier */ - /* Other CVS file names */ + +/* Files go in the attic if the head main branch revision is dead, + otherwise they go in the regular repository directories. The whole + concept of having an attic is sort of a relic from before death + support but on the other hand, it probably does help the speed of + some operations (such as main branch checkouts and updates). */ #define CVSATTIC "Attic" + #define CVSLCK "#cvs.lock" #define CVSTFL "#cvs.tfl" #define CVSRFL "#cvs.rfl" #define CVSWFL "#cvs.wfl" #define CVSRFLPAT "#cvs.rfl.*" /* wildcard expr to match read locks */ -#define CVSEXT_OPT ",p" #define CVSEXT_LOG ",t" #define CVSPREFIX ",," #define CVSDOTIGNORE ".cvsignore" +#define CVSDOTWRAPPER ".cvswrappers" /* miscellaneous CVS defines */ #define CVSEDITPREFIX "CVS: " @@ -181,7 +216,9 @@ extern int errno; #define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */ #define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */ #define BAKPREFIX ".#" /* when rcsmerge'ing */ +#ifndef DEVNULL #define DEVNULL "/dev/null" +#endif #define FALSE 0 #define TRUE 1 @@ -202,18 +239,23 @@ extern int errno; /* #define RCSBIN_DFLT Set by config.h */ #define EDITOR1_ENV "CVSEDITOR" /* which editor to use */ -#define EDITOR2_ENV "EDITOR" /* which editor to use */ +#define EDITOR2_ENV "VISUAL" /* which editor to use */ +#define EDITOR3_ENV "EDITOR" /* which editor to use */ /* #define EDITOR_DFLT Set by config.h */ #define CVSROOT_ENV "CVSROOT" /* source directory root */ #define CVSROOT_DFLT NULL /* No dflt; must set for checkout */ #define IGNORE_ENV "CVSIGNORE" /* More files to ignore */ +#define WRAPPER_ENV "CVSWRAPPERS" /* name of the wrapper file */ + +#define CVSUMASK_ENV "CVSUMASK" /* Effective umask for repository */ +/* #define CVSUMASK_DFLT Set by config.h */ /* * If the beginning of the Repository matches the following string, strip it * so that the output to the logfile does not contain a full pathname. - * + * * If the CVSROOT environment variable is set, it overrides this define. */ #define REPOS_STRIP "/master/" @@ -229,10 +271,23 @@ extern int errno; #define MAXLISTLEN 40000 /* For [A-Z]list holders */ #define MAXDATELEN 50 /* max length for a date */ +/* structure of a entry record */ +struct entnode +{ + char *user; + char *version; + char *timestamp; + char *options; + char *tag; + char *date; + char *conflict; +}; +typedef struct entnode Entnode; + /* The type of request that is being done in do_module() */ enum mtype { - CHECKOUT, TAG, PATCH + CHECKOUT, TAG, PATCH, EXPORT }; /* @@ -251,6 +306,9 @@ enum classify_type T_REMOVED, /* R (removed file) list */ T_REMOVE_ENTRY, /* W (removed entry) list */ T_UPTODATE, /* File is up-to-date */ +#ifdef SERVER_SUPPORT + T_PATCH, /* P Like C, but can patch */ +#endif T_TITLE /* title for node type */ }; typedef enum classify_type Ctype; @@ -258,7 +316,7 @@ typedef enum classify_type Ctype; /* * a struct vers_ts contains all the information about a file including the * user and rcs file names, and the version checked out and the head. - * + * * this is usually obtained from a call to Version_TS which takes a tag argument * for the RCS file if desired */ @@ -266,15 +324,16 @@ struct vers_ts { char *vn_user; /* rcs version user file derives from * it can have the following special - * values: - * empty = no user file + * values: + * empty = no user file * 0 = user file is new * -vers = user file to be removed */ char *vn_rcs; /* the version for the rcs file * (tag version?) */ + char *vn_tag; /* the symbolic tag name */ char *ts_user; /* the timestamp for the user file */ char *ts_rcs; /* the user timestamp from entries */ - char *options; /* opts from Entries file + char *options; /* opts from Entries file * (keyword expansion) */ char *ts_conflict; /* Holds time_stamp of conflict */ char *tag; /* tag stored in the Entries file */ @@ -285,7 +344,7 @@ struct vers_ts typedef struct vers_ts Vers_TS; /* - * structure used for list-private storage by ParseEntries() and + * structure used for list-private storage by Entries_Open() and * Version_TS(). */ struct stickydirtag @@ -296,15 +355,6 @@ struct stickydirtag char *options; }; -/* flags for run_exec(), the fast system() for CVS */ -#define RUN_NORMAL 0x0000 /* no special behaviour */ -#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */ -#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */ -#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */ -#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */ -#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */ -#define RUN_TTY (char *)0 /* for the benefit of lint */ - /* Flags for find_{names,dirs} routines */ #define W_LOCAL 0x01 /* look for files locally */ #define W_REPOS 0x02 /* look for files in the repository */ @@ -320,7 +370,7 @@ enum direnter_type }; typedef enum direnter_type Dtype; -extern char *program_name, *command_name; +extern char *program_name, *program_path, *command_name; extern char *Rcsbin, *Editor, *CVSroot; #ifdef CVSADM_ROOT extern char *CVSADM_Root; @@ -330,17 +380,29 @@ extern char *CurDir; extern int really_quiet, quiet; extern int use_editor; extern int cvswrite; +extern mode_t cvsumask; extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ extern int logoff; /* Don't write history entry */ +extern char hostname[]; + /* Externs that are included directly in the CVS sources */ +int RCS_settag PROTO((const char *, const char *, const char *)); +int RCS_deltag PROTO((const char *, const char *, int)); +int RCS_setbranch PROTO((const char *, const char *)); +int RCS_lock PROTO((const char *, const char *, int)); +int RCS_unlock PROTO((const char *, const char *, int)); +int RCS_merge PROTO((const char *, const char *, const char *, const char *)); + +#include "error.h" + DBM *open_module PROTO((void)); -FILE *Fopen PROTO((char *name, char *mode)); -FILE *open_file PROTO((char *name, char *mode)); +FILE *open_file PROTO((const char *, const char *)); List *Find_Dirs PROTO((char *repository, int which)); -List *ParseEntries PROTO((int aflag)); +void Entries_Close PROTO((List *entries)); +List *Entries_Open PROTO((int aflag)); char *Make_Date PROTO((char *rawdate)); char *Name_Repository PROTO((char *dir, char *update_dir)); #ifdef CVSADM_ROOT @@ -354,29 +416,37 @@ char *getcaller PROTO((void)); char *time_stamp PROTO((char *file)); char *xmalloc PROTO((size_t bytes)); char *xrealloc PROTO((char *ptr, size_t bytes)); -char *xstrdup PROTO((char *str)); +char *xstrdup PROTO((const char *str)); +void strip_trailing_newlines PROTO((char *str)); int No_Difference PROTO((char *file, Vers_TS * vers, List * entries, char *repository, char *update_dir)); -int Parse_Info PROTO((char *infofile, char *repository, int PROTO((*callproc)) PROTO(()), int all)); +typedef int (*CALLPROC) PROTO((char *repository, char *value)); +int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); int Reader_Lock PROTO((char *xrepository)); -int SIG_register PROTO((int sig, RETSIGTYPE PROTO((*fn)) PROTO(()))); +typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(()); +int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); int Writer_Lock PROTO((List * list)); int ign_name PROTO((char *name)); -int isdir PROTO((char *file)); -int isfile PROTO((char *file)); -int islink PROTO((char *file)); -int isreadable PROTO((char *file)); -int iswritable PROTO((char *file)); +int isdir PROTO((const char *file)); +int isfile PROTO((const char *file)); +int islink PROTO((const char *file)); +int isreadable PROTO((const char *file)); +int iswritable PROTO((const char *file)); +int isaccessible PROTO((const char *file, const int mode)); +int isabsolute PROTO((const char *filename)); +char *last_component PROTO((char *path)); + int joining PROTO((void)); -int link_file PROTO((char *from, char *to)); -int numdots PROTO((char *s)); -int run_exec PROTO((char *stin, char *stout, char *sterr, int flags)); -int unlink_file PROTO((char *f)); +int numdots PROTO((const char *s)); +int unlink_file PROTO((const char *f)); +int unlink_file_dir PROTO((const char *f)); int update PROTO((int argc, char *argv[])); -int xcmp PROTO((char *file1, char *file2)); +int xcmp PROTO((const char *file1, const char *file2)); int yesno PROTO((void)); +void *valloc PROTO((size_t bytes)); time_t get_date PROTO((char *date, struct timeb *now)); -void Create_Admin PROTO((char *dir, char *repository, char *tag, char *date)); +void Create_Admin PROTO((char *dir, char *update_dir, + char *repository, char *tag, char *date)); void Lock_Cleanup PROTO((void)); void ParseTag PROTO((char **tagp, char **datep)); void Scratch_Entry PROTO((List * list, char *fname)); @@ -384,8 +454,8 @@ void WriteTag PROTO((char *dir, char *tag, char *date)); void cat_module PROTO((int status)); void check_entries PROTO((char *dir)); void close_module PROTO((DBM * db)); -void copy_file PROTO((char *from, char *to)); -void error PROTO((int status, int errnum, char *message,...)); +void copy_file PROTO((const char *from, const char *to)); +void (*error_set_cleanup PROTO((void (*) (void)))) PROTO ((void)); void fperror PROTO((FILE * fp, int status, int errnum, char *message,...)); void free_names PROTO((int *pargc, char *argv[])); void freevers_ts PROTO((Vers_TS ** versp)); @@ -395,20 +465,18 @@ void ign_setup PROTO((void)); void ign_dir_add PROTO((char *name)); int ignore_directory PROTO((char *name)); void line2argv PROTO((int *pargc, char *argv[], char *line)); -void make_directories PROTO((char *name)); -void make_directory PROTO((char *name)); -void rename_file PROTO((char *from, char *to)); -void run_arg PROTO((char *s)); -void run_args PROTO((char *fmt,...)); -void run_print PROTO((FILE * fp)); -void run_setup PROTO((char *fmt,...)); +void make_directories PROTO((const char *name)); +void make_directory PROTO((const char *name)); +void rename_file PROTO((const char *from, const char *to)); void strip_path PROTO((char *path)); void strip_trailing_slashes PROTO((char *path)); void update_delproc PROTO((Node * p)); -void usage PROTO((char **cpp)); +void usage PROTO((const char *const *cpp)); void xchmod PROTO((char *fname, int writable)); -int Checkin PROTO((int type, char *file, char *repository, char *rcs, char *rev, - char *tag, char *options, char *message, List *entries)); +char *xgetwd PROTO((void)); +int Checkin PROTO((int type, char *file, char *update_dir, + char *repository, char *rcs, char *rev, + char *tag, char *options, char *message, List *entries)); Ctype Classify_File PROTO((char *file, char *tag, char *date, char *options, int force_tag_match, int aflag, char *repository, List *entries, List *srcfiles, Vers_TS **versp, @@ -424,11 +492,21 @@ Vers_TS *Version_TS PROTO((char *repository, char *options, char *tag, int set_time, List * entries, List * xfiles)); void do_editor PROTO((char *dir, char **messagep, char *repository, List * changes)); + +typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where, + char *mwhere, char *mfile, int horten, int local_specified, + char *omodule, char *msg)); +typedef int (*FILEPROC) PROTO((char *file, char *update_dir, char *repository, + List * entries, List * srcfiles)); +typedef int (*FILESDONEPROC) PROTO((int err, char *repository, char *update_dir)); +typedef Dtype (*DIRENTPROC) PROTO((char *dir, char *repos, char *update_dir)); +typedef int (*DIRLEAVEPROC) PROTO((char *dir, int err, char *update_dir)); + int do_module PROTO((DBM * db, char *mname, enum mtype m_type, char *msg, - int PROTO((*callback_proc)) (), char *where, int shorten, - int local_specified, int run_module_prog, char *extra_arg)); -int do_recursion PROTO((int PROTO((*xfileproc)) (), int PROTO((*xfilesdoneproc)) (), - Dtype PROTO((*xdirentproc)) (), int PROTO((*xdirleaveproc)) (), + CALLBACKPROC callback_proc, char *where, int shorten, + int local_specified, int run_module_prog, char *extra_arg)); +int do_recursion PROTO((FILEPROC xfileproc, FILESDONEPROC xfilesdoneproc, + DIRENTPROC xdirentproc, DIRLEAVEPROC xdirleaveproc, Dtype xflags, int xwhich, int xaflag, int xreadlock, int xdosrcs)); int do_update PROTO((int argc, char *argv[], char *xoptions, char *xtag, @@ -437,11 +515,56 @@ int do_update PROTO((int argc, char *argv[], char *xoptions, char *xtag, char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir)); void history_write PROTO((int type, char *update_dir, char *revs, char *name, char *repository)); -int start_recursion PROTO((int PROTO((*fileproc)) (), int PROTO((*filesdoneproc)) (), - Dtype PROTO((*direntproc)) (), int PROTO((*dirleaveproc)) (), +int start_recursion PROTO((FILEPROC fileproc, FILESDONEPROC filesdoneproc, + DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc, int argc, char *argv[], int local, int which, int aflag, int readlock, char *update_preload, int dosrcs, int wd_is_repos)); void SIG_beginCrSect PROTO((void)); void SIG_endCrSect PROTO((void)); void read_cvsrc PROTO((int *argc, char ***argv)); + +char *make_message_rcslegal PROTO((char *message)); + +/* flags for run_exec(), the fast system() for CVS */ +#define RUN_NORMAL 0x0000 /* no special behaviour */ +#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */ +#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */ +#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */ +#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */ +#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */ +#define RUN_TTY (char *)0 /* for the benefit of lint */ + +void run_arg PROTO((const char *s)); +void run_print PROTO((FILE * fp)); +#ifdef HAVE_VPRINTF +void run_setup PROTO((const char *fmt,...)); +void run_args PROTO((const char *fmt,...)); +#else +void run_setup (); +void run_args (); +#endif +int run_exec PROTO((char *stin, char *stout, char *sterr, int flags)); + +/* other similar-minded stuff from run.c. */ +FILE *Popen PROTO((const char *, const char *)); +int piped_child PROTO((char **, int *, int *)); +void close_on_exec PROTO((int)); +int filter_stream_through_program PROTO((int, int, char **, pid_t *)); + +pid_t waitpid PROTO((pid_t, int *, int)); + +/* Wrappers. */ + +typedef enum { WRAP_MERGE, WRAP_COPY } WrapMergeMethod; +typedef enum { WRAP_TOCVS, WRAP_FROMCVS, WRAP_CONFLICT } WrapMergeHas; + +void wrap_setup PROTO((void)); +int wrap_name_has PROTO((const char *name,WrapMergeHas has)); +char *wrap_tocvs_process_file PROTO((const char *fileName)); +int wrap_merge_is_copy PROTO((const char *fileName)); +char *wrap_fromcvs_process_file PROTO((const char *fileName)); +/* Pathname expansion */ +char *expand_path PROTO((char *name)); +void wrap_add_file PROTO((const char *file,int temp)); +void wrap_add PROTO((char *line,int temp)); diff --git a/gnu/usr.bin/cvs/cvs/cvsrc.c b/gnu/usr.bin/cvs/cvs/cvsrc.c index fedd020..5882afc 100644 --- a/gnu/usr.bin/cvs/cvs/cvsrc.c +++ b/gnu/usr.bin/cvs/cvs/cvsrc.c @@ -10,10 +10,11 @@ #include "cvs.h" +#include "getline.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)cvsrc.c 1.9 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)cvsrc.c 1.9 94/09/30 $"; +USE(rcsid); #endif /* lint */ /* this file is to be found in the user's home directory */ @@ -25,7 +26,6 @@ char cvsrc[] = CVSRC_FILENAME; #define GROW 10 -extern char *getenv (); extern char *strtok (); void @@ -37,10 +37,13 @@ read_cvsrc (argc, argv) char *homeinit; FILE *cvsrcfile; - char linebuf [MAXLINELEN]; - + char *line; + int line_length; + size_t line_chars_allocated; + char *optstart; + int command_len; int found = 0; int i; @@ -73,7 +76,7 @@ read_cvsrc (argc, argv) /* if it can't be read, there's no point to continuing */ - if (access (homeinit, R_OK) != 0) + if (!isreadable (homeinit)) { free (homeinit); return; @@ -81,15 +84,20 @@ read_cvsrc (argc, argv) /* now scan the file until we find the line for the command in question */ + line = NULL; + line_chars_allocated = 0; + command_len = strlen (command_name); cvsrcfile = open_file (homeinit, "r"); - while (fgets (linebuf, MAXLINELEN, cvsrcfile)) + while ((line_length = getline (&line, &line_chars_allocated, cvsrcfile)) + >= 0) { /* skip over comment lines */ - if (linebuf[0] == '#') + if (line[0] == '#') continue; /* stop if we match the current command */ - if (!strncmp (linebuf, (*argv)[0], strlen ((*argv)[0]))) + if (!strncmp (line, command_name, command_len) + && isspace (*(line + command_len))) { found = 1; break; @@ -101,8 +109,8 @@ read_cvsrc (argc, argv) if (found) { /* skip over command in the options line */ - optstart = strtok(linebuf+strlen((*argv)[0]), "\t \n"); - + optstart = strtok (line + command_len, "\t \n"); + do { new_argv [new_argc] = xstrdup (optstart); @@ -121,9 +129,12 @@ read_cvsrc (argc, argv) } } - while (optstart = strtok (NULL, "\t \n")); + while ((optstart = strtok (NULL, "\t \n")) != NULL); } + if (line != NULL) + free (line); + /* now copy the remaining arguments */ for (i=1; i < *argc; i++) diff --git a/gnu/usr.bin/cvs/cvs/diff.c b/gnu/usr.bin/cvs/cvs/diff.c index 99d8c18..22fda75 100644 --- a/gnu/usr.bin/cvs/cvs/diff.c +++ b/gnu/usr.bin/cvs/cvs/diff.c @@ -17,8 +17,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)diff.c 1.61 94/10/22 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)diff.c 1.61 94/10/22 $"; +USE(rcsid); #endif static Dtype diff_dirproc PROTO((char *dir, char *pos_repos, char *update_dir)); @@ -33,12 +33,19 @@ static void diff_mark_errors PROTO((int err)); static char *diff_rev1, *diff_rev2; static char *diff_date1, *diff_date2; static char *use_rev1, *use_rev2; + +#ifdef SERVER_SUPPORT +/* Revision of the user file, if it is unchanged from something in the + repository and we want to use that fact. */ +static char *user_file_rev; +#endif + static char *options; static char opts[PATH_MAX]; static int diff_errors; static int empty_files = 0; -static char *diff_usage[] = +static const char *const diff_usage[] = { "Usage: %s %s [-lN] [rcsdiff-options]\n", #ifdef CVS_DIFFDATE @@ -58,7 +65,7 @@ static char *diff_usage[] = int diff (argc, argv) int argc; - char *argv[]; + char **argv; { char tmp[50]; int c, err = 0; @@ -73,6 +80,11 @@ diff (argc, argv) * intercept the -r arguments for doing revision diffs; and -l/-R for a * non-recursive/recursive diff. */ +#ifdef SERVER_SUPPORT + /* Need to be able to do this command more than once (according to + the protocol spec, even if the current client doesn't use it). */ + opts[0] = '\0'; +#endif optind = 1; while ((c = getopt (argc, argv, "abcdefhilnpqtuw0123456789BHNQRTC:D:F:I:L:V:k:r:")) != -1) @@ -150,10 +162,63 @@ diff (argc, argv) if (!options) options = xstrdup (""); +#ifdef CLIENT_SUPPORT + if (client_active) { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (empty_files) + send_arg("-N"); + send_option_string (opts); + if (diff_rev1) + option_with_arg ("-r", diff_rev1); + if (diff_date1) + client_senddate (diff_date1); + if (diff_rev2) + option_with_arg ("-r", diff_rev2); + if (diff_date2) + client_senddate (diff_date2); + +#if 0 +/* FIXME: We shouldn't have to send current files to diff two revs, but it + doesn't work yet and I haven't debugged it. So send the files -- + it's slower but it works. gnu@cygnus.com Apr94 */ + +/* Idea: often times the changed region of a file is relatively small. + It would be cool if the client could just divide the file into 4k + blocks or whatever and send hash values for the blocks. Send hash + values for blocks aligned with the beginning of the file and the + end of the file. Then the server can tell how much of the head and + tail of the file is unchanged. Well, hash collisions will screw + things up, but MD5 has 128 bits of hash value... */ + + /* Send the current files unless diffing two revs from the archive */ + if (diff_rev2 == NULL && diff_date2 == NULL) + send_files (argc, argv, local); + else + send_file_names (argc, argv); +#else + send_files (argc, argv, local, 0); +#endif + + if (fprintf (to_server, "diff\n") < 0) + error (1, errno, "writing to server"); + err = get_responses_and_close (); + free (options); + return (err); + } +#endif + which = W_LOCAL; if (diff_rev2 != NULL || diff_date2 != NULL) which |= W_REPOS | W_ATTIC; + wrap_setup (); + /* start the recursion processor */ err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc, diff_dirleaveproc, argc, argv, local, @@ -185,7 +250,12 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles) DIFF_NEITHER } empty_file = DIFF_NEITHER; char tmp[L_tmpnam+1]; + char *tocvsPath; + char fname[PATH_MAX]; +#ifdef SERVER_SUPPORT + user_file_rev = 0; +#endif vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL, file, 1, 0, entries, srcfiles); @@ -243,6 +313,15 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles) diff_mark_errors (err); return (err); } +#ifdef SERVER_SUPPORT + else if (!strcmp (vers->ts_user, vers->ts_rcs)) + { + /* The user file matches some revision in the repository + Diff against the repository (for remote CVS, we might not + have a copy of the user file around). */ + user_file_rev = vers->vn_user; + } +#endif } } @@ -252,6 +331,11 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles) return (0); } +#ifdef DEATH_SUPPORT + /* FIXME: Check whether use_rev1 and use_rev2 are dead and deal + accordingly. */ +#endif + /* Output an "Index:" line for patch to use */ (void) fflush (stdout); if (update_dir[0]) @@ -260,6 +344,19 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles) (void) printf ("Index: %s\n", file); (void) fflush (stdout); + tocvsPath = wrap_tocvs_process_file(file); + if (tocvsPath) + { + /* Backup the current version of the file to CVS/,,filename */ + sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, file); + if (unlink_file_dir (fname) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", file); + rename_file (file, fname); + /* Copy the wrapped file to the current directory then go to work */ + copy_file (tocvsPath, file); + } + if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) { (void) printf ("===================================================================\nRCS file: %s\n", @@ -320,6 +417,17 @@ diff_fileproc (file, update_dir, repository, entries, srcfiles) break; } + if (tocvsPath) + { + if (unlink_file_dir (file) < 0) + if (! existence_error (errno)) + error (1, errno, "cannot remove %s", file); + + rename_file (fname,file); + if (unlink_file (tocvsPath) < 0) + error (1, errno, "cannot remove %s", file); + } + if (empty_file == DIFF_REMOVED) (void) unlink (tmp); @@ -342,6 +450,8 @@ diff_mark_errors (err) /* * Print a warm fuzzy message when we enter a dir + * + * Don't try to diff directories that don't exist! -- DW */ /* ARGSUSED */ static Dtype @@ -351,6 +461,11 @@ diff_dirproc (dir, pos_repos, update_dir) char *update_dir; { /* XXX - check for dirs we don't want to process??? */ + + /* YES ... for instance dirs that don't exist!!! -- DW */ + if (!isdir (dir) ) + return (R_SKIP_ALL); + if (!quiet) error (0, 0, "Diffing %s", update_dir); return (R_PROCESS); @@ -414,8 +529,11 @@ diff_file_nodiff (file, repository, entries, srcfiles, vers) diff_date1, file, 1, 0, entries, srcfiles); if (xvers->vn_rcs == NULL) { - if (diff_rev1) - error (0, 0, "tag %s is not in file %s", diff_rev1, file); + /* Don't gripe if it doesn't exist, just ignore! */ + if (! isfile (file)) + /* null statement */ ; + else if (diff_rev1) + error (0, 0, "tag %s is not in file %s", diff_rev1, file); else error (0, 0, "no revision for date %s in file %s", diff_date1, file); @@ -436,7 +554,10 @@ diff_file_nodiff (file, repository, entries, srcfiles, vers) diff_date2, file, 1, 0, entries, srcfiles); if (xvers->vn_rcs == NULL) { - if (diff_rev1) + /* Don't gripe if it doesn't exist, just ignore! */ + if (! isfile (file)) + /* null statement */ ; + else if (diff_rev1) error (0, 0, "tag %s is not in file %s", diff_rev2, file); else error (0, 0, "no revision for date %s in file %s", @@ -455,6 +576,24 @@ diff_file_nodiff (file, repository, entries, srcfiles, vers) return (1); } } +#ifdef SERVER_SUPPORT + if (user_file_rev) + { + /* drop user_file_rev into first unused use_rev */ + if (!use_rev1) + use_rev1 = xstrdup (user_file_rev); + else if (!use_rev2) + use_rev2 = xstrdup (user_file_rev); + /* and if not, it wasn't needed anyhow */ + user_file_rev = 0; + } + + /* now, see if we really need to do the diff */ + if (use_rev1 && use_rev2) + { + return (strcmp (use_rev1, use_rev2) == 0); + } +#endif /* SERVER_SUPPORT */ if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0) { if (strcmp (vers->ts_rcs, vers->ts_user) == 0 && diff --git a/gnu/usr.bin/cvs/cvs/entries.c b/gnu/usr.bin/cvs/cvs/entries.c index 0dfd165..7ae3e58 100644 --- a/gnu/usr.bin/cvs/cvs/entries.c +++ b/gnu/usr.bin/cvs/cvs/entries.c @@ -12,53 +12,81 @@ */ #include "cvs.h" +#include "getline.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)entries.c 1.44 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)entries.c 1.44 94/10/07 $"; +USE(rcsid); #endif -static Node *AddEntryNode PROTO((List * list, char *name, char *version, - char *timestamp, char *options, char *tag, - char *date, char *conflict)); +static Node *AddEntryNode PROTO((List * list, Entnode *entnode)); + +static Entnode *fgetentent PROTO((FILE *)); +static int fputentent PROTO((FILE *, Entnode *)); static FILE *entfile; static char *entfilename; /* for error messages */ /* + * Construct an Entnode + */ +Entnode * +Entnode_Create(user, vn, ts, options, tag, date, ts_conflict) + const char *user; + const char *vn; + const char *ts; + const char *options; + const char *tag; + const char *date; + const char *ts_conflict; +{ + Entnode *ent; + + /* Note that timestamp and options must be non-NULL */ + ent = (Entnode *) xmalloc (sizeof (Entnode)); + ent->user = xstrdup (user); + ent->version = xstrdup (vn); + ent->timestamp = xstrdup (ts ? ts : ""); + ent->options = xstrdup (options ? options : ""); + ent->tag = xstrdup (tag); + ent->date = xstrdup (date); + ent->conflict = xstrdup (ts_conflict); + + return ent; +} + +/* + * Destruct an Entnode + */ +void +Entnode_Destroy (ent) + Entnode *ent; +{ + free (ent->user); + free (ent->version); + free (ent->timestamp); + free (ent->options); + if (ent->tag) + free (ent->tag); + if (ent->date) + free (ent->date); + if (ent->conflict) + free (ent->conflict); + free (ent); +} + +/* * Write out the line associated with a node of an entries file */ +static int write_ent_proc PROTO ((Node *, void *)); static int write_ent_proc (node, closure) Node *node; void *closure; { - Entnode *p; - - p = (Entnode *) node->data; - if (fprintf (entfile, "/%s/%s/%s", node->key, p->version, - p->timestamp) == EOF) - error (1, errno, "cannot write %s", entfilename); - if (p->conflict) - { - if (fprintf (entfile, "+%s", p->conflict) == EOF) - error (1, errno, "cannot write %s", entfilename); - } - if (fprintf (entfile, "/%s/", p->options) == EOF) + if (fputentent(entfile, (Entnode *) node->data)) error (1, errno, "cannot write %s", entfilename); - if (p->tag) - { - if (fprintf (entfile, "T%s\n", p->tag) == EOF) - error (1, errno, "cannot write %s", entfilename); - } - else if (p->date) - { - if (fprintf (entfile, "D%s\n", p->date) == EOF) - error (1, errno, "cannot write %s", entfilename); - } - else if (fprintf (entfile, "\n") == EOF) - error (1, errno, "cannot write %s", entfilename); return (0); } @@ -79,6 +107,9 @@ write_entries (list) /* now, atomically (on systems that support it) rename it */ rename_file (entfilename, CVSADM_ENT); + + /* now, remove the log file */ + unlink_file (CVSADM_ENTLOG); } /* @@ -92,12 +123,21 @@ Scratch_Entry (list, fname) Node *node; if (trace) +#ifdef SERVER_SUPPORT + (void) fprintf (stderr, "%c-> Scratch_Entry(%s)\n", + (server_active) ? 'S' : ' ', fname); +#else (void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname); +#endif /* hashlookup to see if it is there */ if ((node = findnode (list, fname)) != NULL) { delnode (node); /* delete the node */ +#ifdef SERVER_SUPPORT + if (server_active) + server_scratch (fname); +#endif if (!noexec) write_entries (list); /* re-write the file */ } @@ -118,45 +158,43 @@ Register (list, fname, vn, ts, options, tag, date, ts_conflict) char *date; char *ts_conflict; { - int should_write_file = !noexec; + Entnode *entnode; Node *node; +#ifdef SERVER_SUPPORT + if (server_active) + { + server_register (fname, vn, ts, options, tag, date, ts_conflict); + } +#endif + if (trace) { +#ifdef SERVER_SUPPORT + (void) fprintf (stderr, "%c-> Register(%s, %s, %s%s%s, %s, %s %s)\n", + (server_active) ? 'S' : ' ', + fname, vn, ts ? ts : "", + ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", + options, tag ? tag : "", date ? date : ""); +#else (void) fprintf (stderr, "-> Register(%s, %s, %s%s%s, %s, %s %s)\n", - fname, vn, ts, + fname, vn, ts ? ts : "", ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", - options, tag ? tag : "", date ? date : ""); + options, tag ? tag : "", date ? date : ""); +#endif } - /* was it already there? */ - if ((node = findnode (list, fname)) != NULL) - { - /* take it out */ - delnode (node); + entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict); + node = AddEntryNode (list, entnode); - /* add the new one and re-write the file */ - (void) AddEntryNode (list, fname, vn, ts, options, tag, - date, ts_conflict); - - if (should_write_file) - write_entries (list); - } - else + if (!noexec) { - /* add the new one */ - node = AddEntryNode (list, fname, vn, ts, options, tag, - date, ts_conflict); + entfile = open_file (CVSADM_ENTLOG, "a"); + + write_ent_proc (node, NULL); - if (should_write_file) - { - /* append it to the end */ - entfilename = CVSADM_ENT; - entfile = open_file (entfilename, "a"); - (void) write_ent_proc (node, NULL); - if (fclose (entfile) == EOF) - error (1, errno, "error closing %s", entfilename); - } + if (fclose (entfile) == EOF) + error (1, errno, "error closing %s", CVSADM_ENTLOG); } } @@ -179,24 +217,138 @@ freesdt (p) free ((char *) sdtp); } +static Entnode * +fgetentent(fpin) + FILE *fpin; +{ + Entnode *ent; + char *line; + size_t line_chars_allocated; + register char *cp; + char *user, *vn, *ts, *options; + char *tag_or_date, *tag, *date, *ts_conflict; + + line = NULL; + line_chars_allocated = 0; + + ent = NULL; + while (getline (&line, &line_chars_allocated, fpin) > 0) + { + if (line[0] != '/') + continue; + + user = line + 1; + if ((cp = strchr (user, '/')) == NULL) + continue; + *cp++ = '\0'; + vn = cp; + if ((cp = strchr (vn, '/')) == NULL) + continue; + *cp++ = '\0'; + ts = cp; + if ((cp = strchr (ts, '/')) == NULL) + continue; + *cp++ = '\0'; + options = cp; + if ((cp = strchr (options, '/')) == NULL) + continue; + *cp++ = '\0'; + tag_or_date = cp; + if ((cp = strchr (tag_or_date, '\n')) == NULL) + continue; + *cp = '\0'; + tag = (char *) NULL; + date = (char *) NULL; + if (*tag_or_date == 'T') + tag = tag_or_date + 1; + else if (*tag_or_date == 'D') + date = tag_or_date + 1; + + if ((ts_conflict = strchr (ts, '+'))) + *ts_conflict++ = '\0'; + + /* + * XXX - Convert timestamp from old format to new format. + * + * If the timestamp doesn't match the file's current + * mtime, we'd have to generate a string that doesn't + * match anyways, so cheat and base it on the existing + * string; it doesn't have to match the same mod time. + * + * For an unmodified file, write the correct timestamp. + */ + { + struct stat sb; + if (strlen (ts) > 30 && stat (user, &sb) == 0) + { + char *c = ctime (&sb.st_mtime); + + if (!strncmp (ts + 25, c, 24)) + ts = time_stamp (user); + else + { + ts += 24; + ts[0] = '*'; + } + } + } + + ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict); + break; + } + + free (line); + return ent; +} + +static int +fputentent(fp, p) + FILE *fp; + Entnode *p; +{ + if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0) + return 1; + if (p->conflict) + { + if (fprintf (fp, "+%s", p->conflict) < 0) + return 1; + } + if (fprintf (fp, "/%s/", p->options) < 0) + return 1; + + if (p->tag) + { + if (fprintf (fp, "T%s\n", p->tag) < 0) + return 1; + } + else if (p->date) + { + if (fprintf (fp, "D%s\n", p->date) < 0) + return 1; + } + else + { + if (fprintf (fp, "\n") < 0) + return 1; + } + + return 0; +} + + /* * Read the entries file into a list, hashing on the file name. */ List * -ParseEntries (aflag) +Entries_Open (aflag) int aflag; { List *entries; - char line[MAXLINELEN]; - char *cp, *user, *vn, *ts, *options; - char *tag_or_date, *tag, *date, *ts_conflict; + Entnode *ent; char *dirtag, *dirdate; - int lineno = 0; int do_rewrite = 0; FILE *fpin; - vn = ts = options = tag = date = ts_conflict = 0; - /* get a fresh list... */ entries = getlist (); @@ -220,89 +372,28 @@ ParseEntries (aflag) entries->list->delproc = freesdt; } - again: fpin = fopen (CVSADM_ENT, "r"); if (fpin == NULL) error (0, errno, "cannot open %s for reading", CVSADM_ENT); else { - while (fgets (line, sizeof (line), fpin) != NULL) + while ((ent = fgetentent (fpin)) != NULL) { - lineno++; - if (line[0] == '/') - { - user = line + 1; - if ((cp = strchr (user, '/')) == NULL) - continue; - *cp++ = '\0'; - vn = cp; - if ((cp = strchr (vn, '/')) == NULL) - continue; - *cp++ = '\0'; - ts = cp; - if ((cp = strchr (ts, '/')) == NULL) - continue; - *cp++ = '\0'; - options = cp; - if ((cp = strchr (options, '/')) == NULL) - continue; - *cp++ = '\0'; - tag_or_date = cp; - if ((cp = strchr (tag_or_date, '\n')) == NULL) - continue; - *cp = '\0'; - tag = (char *) NULL; - date = (char *) NULL; - if (*tag_or_date == 'T') - tag = tag_or_date + 1; - else if (*tag_or_date == 'D') - date = tag_or_date + 1; - - if (ts_conflict = strchr (ts, '+')) - *ts_conflict++ = '\0'; - - /* - * XXX - Convert timestamp from old format to new format. - * - * If the timestamp doesn't match the file's current - * mtime, we'd have to generate a string that doesn't - * match anyways, so cheat and base it on the existing - * string; it doesn't have to match the same mod time. - * - * For an unmodified file, write the correct timestamp. - */ - { - struct stat sb; - if (strlen (ts) > 30 && stat (user, &sb) == 0) - { - extern char *ctime (); - char *c = ctime (&sb.st_mtime); - - if (!strncmp (ts + 25, c, 24)) - ts = time_stamp (user); - else - { - ts += 24; - ts[0] = '*'; - } - do_rewrite = 1; - } - } + (void) AddEntryNode (entries, ent); + } - (void) AddEntryNode (entries, user, vn, ts, options, tag, - date, ts_conflict); - } - else - { - /* try conversion only on first line */ - if (lineno == 1) - { - (void) fclose (fpin); - check_entries ((char *) NULL); - goto again; - } - } + fclose (fpin); + } + + fpin = fopen (CVSADM_ENTLOG, "r"); + if (fpin != NULL) + { + while ((ent = fgetentent (fpin)) != NULL) + { + (void) AddEntryNode (entries, ent); } + do_rewrite = 1; + fclose (fpin); } if (do_rewrite && !noexec) @@ -318,92 +409,22 @@ ParseEntries (aflag) return (entries); } -/* - * Look at the entries file to determine if it is in the old entries format. - * If so, convert it to the new format. - */ void -check_entries (dir) - char *dir; +Entries_Close(list) + List *list; { - FILE *fpin, *fpout; - char tmp[MAXLINELEN]; - char line[MAXLINELEN]; - char entname[MAXLINELEN]; - char entbak[MAXLINELEN]; - char *cp, *user, *rev, *ts, *opt; - - if (dir != NULL) + if (list) { - (void) sprintf (entname, "%s/%s", dir, CVSADM_ENT); - (void) sprintf (entbak, "%s/%s", dir, CVSADM_ENTBAK); - } - else - { - (void) strcpy (entname, CVSADM_ENT); - (void) strcpy (entbak, CVSADM_ENTBAK); - } - - fpin = open_file (entname, "r"); - if (fgets (line, sizeof (line), fpin) == NULL) - { - (void) fclose (fpin); - return; - } - (void) fclose (fpin); - if (line[0] != '/') - { - rename_file (entname, entbak); - fpin = open_file (entbak, "r"); - fpout = open_file (entname, "w+"); - while (fgets (line, sizeof (line), fpin) != NULL) - { - if (line[0] == '/') - { - if (fputs (line, fpout) == EOF) - error (1, errno, "cannot write %s", CVSADM_ENT); - continue; - } - rev = line; - if ((ts = strchr (line, '|')) == NULL) - continue; - *ts++ = '\0'; - if ((user = strrchr (ts, ' ')) == NULL) - continue; - *user++ = '\0'; - if ((cp = strchr (user, '|')) == NULL) - continue; - *cp = '\0'; - opt = ""; -#ifdef HAVE_RCS5 -#ifdef HAD_RCS4 - opt = "-V4"; -#endif -#endif - if (fprintf (fpout, "/%s/%s/%s/%s/\n", user, rev, ts, opt) == EOF) - error (1, errno, "cannot write %s", CVSADM_ENT); + if (!noexec) + { + if (isfile (CVSADM_ENTLOG)) + write_entries (list); } - (void) fclose (fpin); - if (fclose (fpout) == EOF) - error (1, errno, "cannot close %s", entname); - - /* clean up any old Files or Mod files */ - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM_FILE); - else - (void) strcpy (tmp, CVSADM_FILE); - if (isfile (tmp)) - (void) unlink (tmp); - - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM_MOD); - else - (void) strcpy (tmp, CVSADM_MOD); - if (isfile (tmp)) - (void) unlink (tmp); + dellist(&list); } } + /* * Free up the memory associated with the data section of an ENTRIES type * node @@ -415,16 +436,7 @@ Entries_delproc (node) Entnode *p; p = (Entnode *) node->data; - free (p->version); - free (p->timestamp); - free (p->options); - if (p->tag) - free (p->tag); - if (p->date) - free (p->date); - if (p->conflict) - free (p->conflict); - free ((char *) p); + Entnode_Destroy(p); } /* @@ -432,18 +444,18 @@ Entries_delproc (node) * list */ static Node * -AddEntryNode (list, name, version, timestamp, options, tag, date, conflict) +AddEntryNode (list, entdata) List *list; - char *name; - char *version; - char *timestamp; - char *options; - char *tag; - char *date; - char *conflict; + Entnode *entdata; { Node *p; - Entnode *entdata; + + /* was it already there? */ + if ((p = findnode (list, entdata->user)) != NULL) + { + /* take it out */ + delnode (p); + } /* get a node and fill in the regular stuff */ p = getnode (); @@ -451,25 +463,14 @@ AddEntryNode (list, name, version, timestamp, options, tag, date, conflict) p->delproc = Entries_delproc; /* this one gets a key of the name for hashing */ - p->key = xstrdup (name); - - /* malloc the data parts and fill them in */ - p->data = xmalloc (sizeof (Entnode)); - entdata = (Entnode *) p->data; - entdata->version = xstrdup (version); - entdata->timestamp = xstrdup (timestamp); - entdata->options = xstrdup (options); - if (entdata->options == NULL) - entdata->options = xstrdup ("");/* must be non-NULL */ - entdata->conflict = xstrdup (conflict); - entdata->tag = xstrdup (tag); - entdata->date = xstrdup (date); + /* FIXME This results in duplicated data --- the hash package shouldn't + assume that the key is dynamically allocated. The user's free proc + should be responsible for freeing the key. */ + p->key = xstrdup (entdata->user); + p->data = (char *) entdata; /* put the node into the list */ - if (addnode (list, p) != 0) - error (0, 0, "Duplicate filename in entries file (%s) -- ignored", - name); - + addnode (list, p); return (p); } @@ -498,19 +499,19 @@ WriteTag (dir, tag, date) fout = open_file (tmp, "w+"); if (tag) { - if (fprintf (fout, "T%s\n", tag) == EOF) + if (fprintf (fout, "T%s\n", tag) < 0) error (1, errno, "write to %s failed", tmp); } else { - if (fprintf (fout, "D%s\n", date) == EOF) + if (fprintf (fout, "D%s\n", date) < 0) error (1, errno, "write to %s failed", tmp); } if (fclose (fout) == EOF) error (1, errno, "cannot close %s", tmp); } else - if (unlink_file (tmp) < 0 && errno != ENOENT) + if (unlink_file (tmp) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove %s", tmp); } @@ -523,8 +524,6 @@ ParseTag (tagp, datep) char **datep; { FILE *fp; - char line[MAXLINELEN]; - char *cp; if (tagp) *tagp = (char *) NULL; @@ -533,15 +532,24 @@ ParseTag (tagp, datep) fp = fopen (CVSADM_TAG, "r"); if (fp) { - if (fgets (line, sizeof (line), fp) != NULL) + char *line; + int line_length; + size_t line_chars_allocated; + + line = NULL; + line_chars_allocated = 0; + + if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0) { - if ((cp = strrchr (line, '\n')) != NULL) - *cp = '\0'; + /* Remove any trailing newline. */ + if (line[line_length - 1] == '\n') + line[--line_length] = '\0'; if (*line == 'T' && tagp) *tagp = xstrdup (line + 1); else if (*line == 'D' && datep) *datep = xstrdup (line + 1); } (void) fclose (fp); + free (line); } } diff --git a/gnu/usr.bin/cvs/cvs/find_names.c b/gnu/usr.bin/cvs/cvs/find_names.c index a77807b..82959b5 100644 --- a/gnu/usr.bin/cvs/cvs/find_names.c +++ b/gnu/usr.bin/cvs/cvs/find_names.c @@ -19,8 +19,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)find_names.c 1.45 94/10/22 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)find_names.c 1.45 94/10/22 $"; +USE(rcsid); #endif static int find_dirs PROTO((char *dir, List * list, int checkadm)); @@ -31,6 +31,7 @@ static List *filelist; /* * add the key from entry on entries list to the files list */ +static int add_entries_proc PROTO((Node *, void *)); static int add_entries_proc (node, closure) Node *node; @@ -49,9 +50,11 @@ add_entries_proc (node, closure) /* * compare two files list node (for sort) */ +static int fsortcmp PROTO ((const Node *, const Node *)); static int fsortcmp (p, q) - Node *p, *q; + const Node *p; + const Node *q; { return (strcmp (p->key, q->key)); } @@ -74,8 +77,7 @@ Find_Names (repository, which, aflag, optentries) if (which & W_LOCAL) { /* parse the entries file (if it exists) */ - entries = ParseEntries (aflag); - + entries = Entries_Open (aflag); if (entries != NULL) { /* walk the entries file adding elements to the files list */ @@ -85,7 +87,7 @@ Find_Names (repository, which, aflag, optentries) if (optentries != NULL) *optentries = entries; else - dellist (&entries); + Entries_Close (entries); } } @@ -258,12 +260,7 @@ find_dirs (dir, list, checkadm) /* check for new style */ (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM); if (!isdir (tmp)) - { - /* and old style */ - (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, OCVSADM); - if (!isdir (tmp)) - continue; - } + continue; } /* put it in the list */ diff --git a/gnu/usr.bin/cvs/cvs/history.c b/gnu/usr.bin/cvs/cvs/history.c index acec3cf..7a40b7b 100644 --- a/gnu/usr.bin/cvs/cvs/history.c +++ b/gnu/usr.bin/cvs/cvs/history.c @@ -179,8 +179,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)history.c 1.33 94/09/21 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)history.c 1.33 94/09/21 $"; +USE(rcsid); #endif static struct hrec @@ -201,7 +201,7 @@ static struct hrec static char *fill_hrec PROTO((char *line, struct hrec * hr)); static int accept_hrec PROTO((struct hrec * hr, struct hrec * lr)); static int select_hrec PROTO((struct hrec * hr)); -static int sort_order PROTO((CONST PTR l, CONST PTR r)); +static int sort_order PROTO((const PTR l, const PTR r)); static int within PROTO((char *find, char *string)); static time_t date_and_time PROTO((char *date_str)); static void expand_modules PROTO((void)); @@ -268,11 +268,9 @@ static char **mod_list; /* Ptr to array of ptrs to module names */ static int mod_max; /* Number of elements allocated */ static int mod_count; /* Number of elements used */ -static int histsize; -static char *histdata; static char *histfile; /* Ptr to the history file name */ -static char *history_usg[] = +static const char *const history_usg[] = { "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n", " Reports:\n", @@ -307,12 +305,12 @@ static char *history_usg[] = */ static int sort_order (l, r) - CONST PTR l; - CONST PTR r; + const PTR l; + const PTR r; { int i; - CONST struct hrec *left = (CONST struct hrec *) l; - CONST struct hrec *right = (CONST struct hrec *) r; + const struct hrec *left = (const struct hrec *) l; + const struct hrec *right = (const struct hrec *) r; if (user_sort) /* If Sort by username, compare users */ { @@ -531,6 +529,62 @@ history (argc, argv) else if (report_count > 1) error (1, 0, "Only one report type allowed from: \"-Tcomx\"."); +#ifdef CLIENT_SUPPORT + if (client_active) + { + struct file_list_str *f1; + char **mod; + + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (tag_report) + send_arg("-T"); + if (all_users) + send_arg("-a"); + if (modified) + send_arg("-c"); + if (last_entry) + send_arg("-l"); + if (v_checkout) + send_arg("-o"); + if (working) + send_arg("-w"); + if (histfile) + send_arg("-X"); + if (since_date) + option_with_arg ("-D", asctime (gmtime (&since_date))); + if (backto[0] != '\0') + option_with_arg ("-b", backto); + for (f1 = file_list; f1 < &file_list[file_count]; ++f1) + { + if (f1->l_file[0] == '*') + option_with_arg ("-p", f1->l_file + 1); + else + option_with_arg ("-f", f1->l_file); + } + if (module_report) + send_arg("-m"); + for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) + option_with_arg ("-n", *mod); + if (since_rev != NULL) + option_with_arg ("-r", since_rev); + if (since_tag != NULL) + option_with_arg ("-t", since_tag); + for (mod = user_list; mod < &user_list[user_count]; ++mod) + option_with_arg ("-u", *mod); + if (extract) + option_with_arg ("-x", rec_types); + option_with_arg ("-z", tz_name); + + if (fprintf (to_server, "history\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + if (all_users) save_user (""); @@ -621,7 +675,8 @@ history_write (type, update_dir, revs, name, repository) { char fname[PATH_MAX], workdir[PATH_MAX], homedir[PATH_MAX]; static char username[20]; /* !!! Should be global */ - FILE *fp; + int fd; + char *line; char *slash = "", *cp, *cp2, *repos; int i; static char *tilde = ""; @@ -638,8 +693,17 @@ history_write (type, update_dir, revs, name, repository) return; } - if (!(fp = Fopen (fname, "a"))) /* Some directory not there! */ + if (trace) +#ifdef SERVER_SUPPORT + fprintf (stderr, "%c-> fopen(%s,a)\n", + (server_active) ? 'S' : ' ', fname); +#else + fprintf (stderr, "-> fopen(%s,a)\n", fname); +#endif + if (noexec) return; + if ((fd = open (fname, O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0) + error (1, errno, "cannot open history file: %s", fname); repos = Short_Repository (repository); @@ -747,10 +811,23 @@ history_write (type, update_dir, revs, name, repository) (void) sprintf ((cp + 1), "*%x", i); } - if (fprintf (fp, "%c%08x|%s|%s|%s|%s|%s\n", type, time ((time_t *) NULL), - username, workdir, repos, revs ? revs : "", name) == EOF) + if (!revs) + revs = ""; + line = xmalloc (strlen (username) + strlen (workdir) + strlen (repos) + + strlen (revs) + strlen (name) + 100); + sprintf (line, "%c%08lx|%s|%s|%s|%s|%s\n", + type, (long) time ((time_t *) NULL), + username, workdir, repos, revs, name); + + /* Lessen some race conditions on non-Posix-compliant hosts. */ + if (lseek (fd, (off_t) 0, SEEK_END) == -1) + error (1, errno, "cannot seek to end of history file: %s", fname); + + if (write (fd, line, strlen (line)) < 0) error (1, errno, "cannot write to history file: %s", fname); - (void) fclose (fp); + free (line); + if (close (fd) != 0) + error (1, errno, "cannot close history file: %s", fname); } /* @@ -931,8 +1008,7 @@ read_hrecs (fname) /* Exactly enough space for lines data */ if (!(i = st_buf.st_size)) error (1, 0, "history file is empty"); - histdata = cp = xmalloc (i + 2); - histsize = i; + cp = xmalloc (i + 2); if (read (fd, cp, i) != i) error (1, errno, "cannot read log file"); diff --git a/gnu/usr.bin/cvs/cvs/ignore.c b/gnu/usr.bin/cvs/cvs/ignore.c index 0005f40..f70fe78 100644 --- a/gnu/usr.bin/cvs/cvs/ignore.c +++ b/gnu/usr.bin/cvs/cvs/ignore.c @@ -5,8 +5,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)ignore.c 1.16 94/09/24 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)ignore.c 1.16 94/09/24 $"; +USE(rcsid); #endif /* @@ -27,7 +27,7 @@ static int ign_size; /* This many slots available (plus static int ign_hold; /* Index where first "temporary" item * is held */ -char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS* .del-* *.a *.o *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej"; +const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS* .del-* *.a *.o *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej"; #define IGN_GROW 16 /* grow the list by 16 elements at a * time */ @@ -41,7 +41,6 @@ char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinf void ign_setup () { - extern char *getenv (); struct passwd *pw; char file[PATH_MAX]; char *tmp; @@ -51,17 +50,26 @@ ign_setup () ign_add (tmp, 0); free (tmp); - /* Then add entries found in repository, if it exists */ - (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_IGNORE); - if (isfile (file)) +#ifdef CLIENT_SUPPORT + /* Chances are we should have some way to provide this feature + client/server, but I'm not sure how (surely not by introducing + another network turnaround to each operation--perhaps by + putting a file in the CVS directory on checkout, or with some + sort of "slave cvsroot" on the client). */ + if (!client_active) +#endif + { + /* Then add entries found in repository, if it exists */ + (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, + CVSROOTADM_IGNORE); ign_add_file (file, 0); + } /* Then add entries found in home dir, (if user has one) and file exists */ if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) { (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTIGNORE); - if (isfile (file)) - ign_add_file (file, 0); + ign_add_file (file, 0); } /* Then add entries found in CVSIGNORE environment variable. */ @@ -118,11 +126,17 @@ ign_add_file (file, hold) } /* load the file */ - if (!(fp = fopen (file, "r"))) + fp = fopen (file, "r"); + if (fp == NULL) + { + if (! existence_error (errno)) + error (0, errno, "cannot open %s", file); return; + } while (fgets (line, sizeof (line), fp)) ign_add (line, hold); - (void) fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", file); } /* Parse a line of space-separated wildcards and add them to the list. */ diff --git a/gnu/usr.bin/cvs/cvs/import.c b/gnu/usr.bin/cvs/cvs/import.c index 2d2cdb8..9980763 100644 --- a/gnu/usr.bin/cvs/cvs/import.c +++ b/gnu/usr.bin/cvs/cvs/import.c @@ -17,10 +17,11 @@ */ #include "cvs.h" +#include "save-cwd.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)import.c 1.63 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)import.c 1.63 94/09/30 $"; +USE(rcsid); #endif #define FILE_HOLDER ".#cvsxxx" @@ -40,8 +41,6 @@ static int process_import_file PROTO((char *message, char *vfile, char *vtag, static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int targc, char *targv[], int inattic)); static void add_log PROTO((int ch, char *fname)); -static int str2expmode PROTO((char const* expstring)); -static int strn2expmode PROTO((char const* expstring, size_t n)); static int repos_len; static char vhead[50]; @@ -52,37 +51,23 @@ static int conflicts; static int use_file_modtime; static char *keyword_opt = NULL; -static char *import_usage[] = +static const char *const import_usage[] = { - "Usage: %s %s [-Qq] [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n", - " repository vendor-tag release-tags...\n", - "\t-Q\tReally quiet.\n", - "\t-q\tSomewhat quiet.\n", + "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n", + " [-W spec] repository vendor-tag release-tags...\n", "\t-d\tUse the file's modification time as the time of import.\n", "\t-k sub\tSet default RCS keyword substitution mode.\n", "\t-I ign\tMore files to ignore (! to reset).\n", "\t-b bra\tVendor branch id.\n", "\t-m msg\tLog message.\n", + "\t-W spec\tWrappers specification line.\n", NULL }; -static char *keyword_usage[] = -{ - "%s %s: invalid RCS keyword expansion mode\n", - "Valid expansion modes include:\n", - " -kkv\tGenerate keywords using the default form.\n", - " -kkvl\tLike -kkv, except locker's name inserted.\n", - " -kk\tGenerate only keyword names in keyword strings.\n", - " -kv\tGenerate only keyword values in keyword strings.\n", - " -ko\tGenerate the old keyword string (no changes from checked in file).\n", - NULL, -}; - - int import (argc, argv) int argc; - char *argv[]; + char **argv; { char *message = NULL; char tmpfile[L_tmpnam+1]; @@ -95,18 +80,24 @@ import (argc, argv) usage (import_usage); ign_setup (); + wrap_setup (); (void) strcpy (vbranch, CVSBRANCH); optind = 1; - while ((c = getopt (argc, argv, "Qqdb:m:I:k:")) != -1) + while ((c = getopt (argc, argv, "Qqdb:m:I:k:W:")) != -1) { switch (c) { case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'd': use_file_modtime = 1; @@ -126,10 +117,14 @@ import (argc, argv) ign_add (optarg, 0); break; case 'k': - if (str2expmode(optarg) != -1) - keyword_opt = optarg; - else - usage (keyword_usage); + /* RCS_check_kflag returns strings of the form -kxx. We + only use it for validation, so we can free the value + as soon as it is returned. */ + free (RCS_check_kflag(optarg)); + keyword_opt = optarg; + break; + case 'W': + wrap_add (optarg, 0); break; case '?': default: @@ -146,7 +141,7 @@ import (argc, argv) RCS_check_tag (argv[i]); /* XXX - this should be a module, not just a pathname */ - if (argv[0][0] != '/') + if (! isabsolute (argv[0])) { if (CVSroot == NULL) { @@ -177,6 +172,17 @@ import (argc, argv) (void) strcpy (vhead, vbranch); cp = strrchr (vhead, '.'); *cp = '\0'; + +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* Do this now; don't ask for a log message if we can't talk to the + server. But if there is a syntax error in the options, give + an error message without connecting. */ + start_server (); + } +#endif + if (use_editor) { do_editor ((char *) NULL, &message, repository, @@ -196,11 +202,45 @@ import (argc, argv) message = nm; } +#ifdef CLIENT_SUPPORT + if (client_active) + { + int err; + + ign_setup (); + + if (use_file_modtime) + send_arg("-d"); + + if (vbranch[0] != '\0') + option_with_arg ("-b", vbranch); + if (message) + option_with_arg ("-m", message); + if (keyword_opt != NULL) + option_with_arg ("-k", keyword_opt); + + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + } + + logfp = stdin; + client_import_setup (repository); + err = import_descend (message, argv[1], argc - 2, argv + 2); + client_import_done (); + if (fprintf (to_server, "import\n") < 0) + error (1, errno, "writing to server"); + err += get_responses_and_close (); + return err; + } +#endif + /* * Make all newly created directories writable. Should really use a more * sophisticated security mechanism here. */ - (void) umask (2); + (void) umask (cvsumask); make_directories (repository); /* Create the logfile that will be logged upon completion */ @@ -254,6 +294,10 @@ import (argc, argv) dellist (&ulist); (void) fclose (logfp); + /* Make sure the temporary file goes away, even on systems that don't let + you delete a file that's in use. */ + unlink (tmpfile); + if (message) free (message); @@ -273,10 +317,11 @@ import_descend (message, vtag, targc, targv) DIR *dirp; struct dirent *dp; int err = 0; - int has_dirs = 0; + List *dirlist = NULL; /* first, load up any per-directory ignore lists */ ign_add_file (CVSDOTIGNORE, 1); + wrap_add_file (CVSDOTWRAPPER, 1); if ((dirp = opendir (".")) == NULL) { @@ -290,49 +335,75 @@ import_descend (message, vtag, targc, targv) continue; if (ign_name (dp->d_name)) { +#ifdef SERVER_SUPPORT + /* CVS directories are created by server.c because it doesn't + special-case import. So don't print a message about them. + Do print a message about other ignored files (although + most of these will get ignored on the client side). */ + if (server_active && strcmp (dp->d_name, CVSADM) == 0) + continue; +#endif add_log ('I', dp->d_name); continue; } - if (isdir (dp->d_name)) + + if ( +#ifdef DT_DIR + (dp->d_type == DT_DIR + || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name))) +#else + isdir (dp->d_name) +#endif + && !wrap_name_has (dp->d_name, WRAP_TOCVS) + ) + { + Node *n; + + if (dirlist == NULL) + dirlist = getlist(); + + n = getnode(); + n->key = xstrdup (dp->d_name); + addnode(dirlist, n); + } + else if ( +#ifdef DT_DIR + dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && +#endif + islink (dp->d_name)) { - has_dirs = 1; + add_log ('L', dp->d_name); + err++; } else { - if (islink (dp->d_name)) - { - add_log ('L', dp->d_name); - err++; - } +#ifdef CLIENT_SUPPORT + if (client_active) + err += client_process_import_file (message, dp->d_name, + vtag, targc, targv, + repository); else - { +#endif err += process_import_file (message, dp->d_name, vtag, targc, targv); - } } } (void) closedir (dirp); } - if (has_dirs) + + if (dirlist != NULL) { - if ((dirp = opendir (".")) == NULL) - err++; - else + Node *head, *p; + + head = dirlist->list; + for (p = head->next; p != head; p = p->next) { - while ((dp = readdir (dirp)) != NULL) - { - if (!strcmp(".", dp->d_name) || !strcmp("..", dp->d_name)) - continue; - if (!isdir (dp->d_name) || ign_name (dp->d_name)) - continue; - err += import_descend_dir (message, dp->d_name, - vtag, targc, targv); - /* need to re-load .cvsignore after each dir traversal */ - ign_add_file (CVSDOTIGNORE, 1); - } - (void) closedir (dirp); + err += import_descend_dir (message, p->key, vtag, targc, targv); } + + dellist(&dirlist); } + return (err); } @@ -392,10 +463,16 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) int letter; int ierrno; char *tmpdir; + char *tocvsPath; vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile, 1, 0, (List *) NULL, (List *) NULL); +#ifdef DEATH_SUPPORT + if (vers->vn_rcs != NULL + && !RCS_isdead(vers->srcfile, vers->vn_rcs)) +#else if (vers->vn_rcs != NULL) +#endif { char xtmpfile[PATH_MAX]; int different; @@ -435,7 +512,13 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) (void) unlink_file (xtmpfile); return (1); } + + tocvsPath = wrap_tocvs_process_file (vfile); different = xcmp (xtmpfile, vfile); + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + (void) unlink_file (xtmpfile); if (!different) { @@ -488,7 +571,8 @@ add_rev (message, rcs, vfile, vers) char *vers; { int locked, status, ierrno; - int retcode = 0; + char *tocvsPath; + struct stat vfile_stat; if (noexec) return (0); @@ -496,39 +580,43 @@ add_rev (message, rcs, vfile, vers) locked = 0; if (vers != NULL) { - run_setup ("%s%s -q -l%s", Rcsbin, RCS, vbranch); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, DEVNULL, DEVNULL, RUN_NORMAL)) == 0) - locked = 1; - else if (retcode == -1) + /* Before RCS_lock existed, we were directing stdout, as well as + stderr, from the RCS command, to DEVNULL. I wouldn't guess that + was necessary, but I don't know for sure. */ + if (RCS_lock (rcs, vbranch, 1) != 0) { error (0, errno, "fork failed"); return (1); } + locked = 1; } - if (link_file (vfile, FILE_HOLDER) < 0) - { - if (errno == EEXIST) - { - (void) unlink_file (FILE_HOLDER); - (void) link_file (vfile, FILE_HOLDER); - } - else - { - ierrno = errno; - fperror (logfp, 0, ierrno, "ERROR: cannot create link to %s", vfile); - error (0, ierrno, "ERROR: cannot create link to %s", vfile); - return (1); - } - } - run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch); - run_args ("-m%s", message); + tocvsPath = wrap_tocvs_process_file (vfile); + + /* We used to deposit the revision with -r; RCS would delete the + working file, but we'd keep a hard link to it, and rename it + back after running RCS (ooh, atomicity). However, that + strategy doesn't work on operating systems without hard links + (like Windows NT). Instead, let's deposit it using -u, and + restore its permission bits afterwards. This also means the + file always exists under its own name. */ + if (! tocvsPath) + stat (vfile, &vfile_stat); + + run_setup ("%s%s -q -f %s%s", Rcsbin, RCS_CI, + (tocvsPath ? "-r" : "-u"), + vbranch); + run_args ("-m%s", make_message_rcslegal (message)); if (use_file_modtime) run_arg ("-d"); + run_arg (tocvsPath == NULL ? vfile : tocvsPath); run_arg (rcs); status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); ierrno = errno; - rename_file (FILE_HOLDER, vfile); + + /* Restore the permissions on vfile. */ + if (! tocvsPath) + chmod (vfile, vfile_stat.st_mode); + if (status) { if (!noexec) @@ -538,9 +626,7 @@ add_rev (message, rcs, vfile, vers) } if (locked) { - run_setup ("%s%s -q -u%s", Rcsbin, RCS, vbranch); - run_arg (rcs); - (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + (void) RCS_unlock(rcs, vbranch, 0); } return (1); } @@ -568,9 +654,7 @@ add_tags (rcs, vfile, vtag, targc, targv) if (noexec) return (0); - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, vtag, vbranch); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0) { ierrno = errno; fperror (logfp, 0, retcode == -1 ? ierrno : 0, @@ -583,9 +667,7 @@ add_tags (rcs, vfile, vtag, targc, targv) 1, 0, (List *) NULL, (List *) NULL); for (i = 0; i < targc; i++) { - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, targv[i], vers->vn_rcs); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0) { ierrno = errno; fperror (logfp, 0, retcode == -1 ? ierrno : 0, @@ -606,7 +688,7 @@ struct compair char *suffix, *comlead; }; -struct compair comtable[] = +static const struct compair comtable[] = { /* @@ -616,83 +698,89 @@ struct compair comtable[] = * suffix during initial ci (see InitAdmin()). Comment leaders are needed for * languages without multiline comments; for others they are optional. */ - "a", "-- ", /* Ada */ - "ada", "-- ", - "adb", "-- ", - "asm", ";; ", /* assembler (MS-DOS) */ - "ads", "-- ", /* Ada */ - "bat", ":: ", /* batch (MS-DOS) */ - "body", "-- ", /* Ada */ - "c", " * ", /* C */ - "c++", "// ", /* C++ in all its infinite guises */ - "cc", "// ", - "cpp", "// ", - "cxx", "// ", - "cl", ";;; ", /* Common Lisp */ - "cmd", ":: ", /* command (OS/2) */ - "cmf", "c ", /* CM Fortran */ - "cs", " * ", /* C* */ - "csh", "# ", /* shell */ - "e", "# ", /* efl */ - "epsf", "% ", /* encapsulated postscript */ - "epsi", "% ", /* encapsulated postscript */ - "el", "; ", /* Emacs Lisp */ - "f", "c ", /* Fortran */ - "for", "c ", - "h", " * ", /* C-header */ - "hh", "// ", /* C++ header */ - "hpp", "// ", - "hxx", "// ", - "in", "# ", /* for Makefile.in */ - "l", " * ", /* lex (conflict between lex and + {"a", "-- "}, /* Ada */ + {"ada", "-- "}, + {"adb", "-- "}, + {"asm", ";; "}, /* assembler (MS-DOS) */ + {"ads", "-- "}, /* Ada */ + {"bat", ":: "}, /* batch (MS-DOS) */ + {"body", "-- "}, /* Ada */ + {"c", " * "}, /* C */ + {"c++", "// "}, /* C++ in all its infinite guises */ + {"cc", "// "}, + {"cpp", "// "}, + {"cxx", "// "}, + {"m", "// "}, /* Objective-C */ + {"cl", ";;; "}, /* Common Lisp */ + {"cmd", ":: "}, /* command (OS/2) */ + {"cmf", "c "}, /* CM Fortran */ + {"cs", " * "}, /* C* */ + {"csh", "# "}, /* shell */ + {"e", "# "}, /* efl */ + {"epsf", "% "}, /* encapsulated postscript */ + {"epsi", "% "}, /* encapsulated postscript */ + {"el", "; "}, /* Emacs Lisp */ + {"f", "c "}, /* Fortran */ + {"for", "c "}, + {"h", " * "}, /* C-header */ + {"hh", "// "}, /* C++ header */ + {"hpp", "// "}, + {"hxx", "// "}, + {"in", "# "}, /* for Makefile.in */ + {"l", " * "}, /* lex (conflict between lex and * franzlisp) */ - "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11, + {"mac", ";; "}, /* macro (DEC-10, MS-DOS, PDP-11, * VMS, etc) */ - "me", ".\\\" ", /* me-macros t/nroff */ - "ml", "; ", /* mocklisp */ - "mm", ".\\\" ", /* mm-macros t/nroff */ - "ms", ".\\\" ", /* ms-macros t/nroff */ - "man", ".\\\" ", /* man-macros t/nroff */ - "1", ".\\\" ", /* feeble attempt at man pages... */ - "2", ".\\\" ", - "3", ".\\\" ", - "4", ".\\\" ", - "5", ".\\\" ", - "6", ".\\\" ", - "7", ".\\\" ", - "8", ".\\\" ", - "9", ".\\\" ", - "p", " * ", /* pascal */ - "pas", " * ", - "pl", "# ", /* perl (conflict with Prolog) */ - "ps", "% ", /* postscript */ - "r", "# ", /* ratfor */ - "red", "% ", /* psl/rlisp */ + {"me", ".\\\" "}, /* me-macros t/nroff */ + {"ml", "; "}, /* mocklisp */ + {"mm", ".\\\" "}, /* mm-macros t/nroff */ + {"ms", ".\\\" "}, /* ms-macros t/nroff */ + {"man", ".\\\" "}, /* man-macros t/nroff */ + {"1", ".\\\" "}, /* feeble attempt at man pages... */ + {"2", ".\\\" "}, + {"3", ".\\\" "}, + {"4", ".\\\" "}, + {"5", ".\\\" "}, + {"6", ".\\\" "}, + {"7", ".\\\" "}, + {"8", ".\\\" "}, + {"9", ".\\\" "}, + {"p", " * "}, /* pascal */ + {"pas", " * "}, + {"pl", "# "}, /* perl (conflict with Prolog) */ + {"ps", "% "}, /* postscript */ + {"psw", "% "}, /* postscript wrap */ + {"pswm", "% "}, /* postscript wrap */ + {"r", "# "}, /* ratfor */ + {"red", "% "}, /* psl/rlisp */ #ifdef sparc - "s", "! ", /* assembler */ + {"s", "! "}, /* assembler */ #endif #ifdef mc68000 - "s", "| ", /* assembler */ + {"s", "| "}, /* assembler */ #endif #ifdef pdp11 - "s", "/ ", /* assembler */ + {"s", "/ "}, /* assembler */ #endif #ifdef vax - "s", "# ", /* assembler */ + {"s", "# "}, /* assembler */ #endif #ifdef __ksr__ - "s", "# ", /* assembler */ - "S", "# ", /* Macro assembler */ + {"s", "# "}, /* assembler */ + {"S", "# "}, /* Macro assembler */ +#endif + {"sh", "# "}, /* shell */ + {"sl", "% "}, /* psl */ + {"spec", "-- "}, /* Ada */ + {"tex", "% "}, /* tex */ + {"y", " * "}, /* yacc */ + {"ye", " * "}, /* yacc-efl */ + {"yr", " * "}, /* yacc-ratfor */ +#ifdef SYSTEM_COMMENT_TABLE + SYSTEM_COMMENT_TABLE #endif - "sh", "# ", /* shell */ - "sl", "% ", /* psl */ - "spec", "-- ", /* Ada */ - "tex", "% ", /* tex */ - "y", " * ", /* yacc */ - "ye", " * ", /* yacc-efl */ - "yr", " * ", /* yacc-ratfor */ - "", "# ", /* default for empty suffix */ - NULL, "# " /* default for unknown suffix; */ + {"", "# "}, /* default for empty suffix */ + {NULL, "# "} /* default for unknown suffix; */ /* must always be last */ }; @@ -748,44 +836,65 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) char altdate2[50]; #endif char *author, *buf; - int i, mode, ierrno, err = 0; + int i, ierrno, err = 0; + mode_t mode; + char *tocvsPath; + char *userfile; if (noexec) return (0); - fprcs = open_file (rcs, "w+"); - fpuser = open_file (user, "r"); +#ifdef LINES_CRLF_TERMINATED + /* There exits a port of RCS to such a system that stores files with + straight newlines. If we ever reach this point on such a system, + we'll need to decide what to do with the open_file call below. */ + abort (); +#endif + tocvsPath = wrap_tocvs_process_file (user); + userfile = (tocvsPath == NULL ? user : tocvsPath); + fpuser = fopen (userfile, "r"); + if (fpuser == NULL) { + /* not fatal, continue import */ + fperror (logfp, 0, errno, "ERROR: cannot read file %s", userfile); + error (0, errno, "ERROR: cannot read file %s", userfile); + goto read_error; + } + fprcs = fopen (rcs, "w+"); + if (fprcs == NULL) { + ierrno = errno; + goto write_error_noclose; + } /* * putadmin() */ - if (fprintf (fprcs, "head %s;\n", vhead) == EOF || - fprintf (fprcs, "branch %s;\n", vbranch) == EOF || - fprintf (fprcs, "access ;\n") == EOF || - fprintf (fprcs, "symbols ") == EOF) + if (fprintf (fprcs, "head %s;\n", vhead) < 0 || + fprintf (fprcs, "branch %s;\n", vbranch) < 0 || + fprintf (fprcs, "access ;\n") < 0 || + fprintf (fprcs, "symbols ") < 0) { goto write_error; } for (i = targc - 1; i >= 0; i--) /* RCS writes the symbols backwards */ - if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) == EOF) + if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) < 0) goto write_error; - if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) == EOF || - fprintf (fprcs, "locks ; strict;\n") == EOF || + if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) < 0 || + fprintf (fprcs, "locks ; strict;\n") < 0 || /* XXX - make sure @@ processing works in the RCS file */ - fprintf (fprcs, "comment @%s@;\n", get_comment (user)) == EOF) + fprintf (fprcs, "comment @%s@;\n", get_comment (user)) < 0) { goto write_error; } if (keyword_opt != NULL) - if (fprintf (fprcs, "expand @%s@;\n", keyword_opt) == EOF) + if (fprintf (fprcs, "expand @%s@;\n", keyword_opt) < 0) { goto write_error; } - if (fprintf (fprcs, "\n") == EOF) + if (fprintf (fprcs, "\n") < 0) goto write_error; /* @@ -822,28 +931,28 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) #endif author = getcaller (); - if (fprintf (fprcs, "\n%s\n", vhead) == EOF || + if (fprintf (fprcs, "\n%s\n", vhead) < 0 || fprintf (fprcs, "date %s; author %s; state Exp;\n", - altdate1, author) == EOF || - fprintf (fprcs, "branches %s.1;\n", vbranch) == EOF || - fprintf (fprcs, "next ;\n") == EOF || - fprintf (fprcs, "\n%s.1\n", vbranch) == EOF || + altdate1, author) < 0 || + fprintf (fprcs, "branches %s.1;\n", vbranch) < 0 || + fprintf (fprcs, "next ;\n") < 0 || + fprintf (fprcs, "\n%s.1\n", vbranch) < 0 || fprintf (fprcs, "date %s; author %s; state Exp;\n", - altdate2, author) == EOF || - fprintf (fprcs, "branches ;\n") == EOF || - fprintf (fprcs, "next ;\n\n") == EOF || + altdate2, author) < 0 || + fprintf (fprcs, "branches ;\n") < 0 || + fprintf (fprcs, "next ;\n\n") < 0 || /* * putdesc() */ - fprintf (fprcs, "\ndesc\n") == EOF || - fprintf (fprcs, "@@\n\n\n") == EOF || + fprintf (fprcs, "\ndesc\n") < 0 || + fprintf (fprcs, "@@\n\n\n") < 0 || /* * putdelta() */ - fprintf (fprcs, "\n%s\n", vhead) == EOF || - fprintf (fprcs, "log\n") == EOF || - fprintf (fprcs, "@Initial revision\n@\n") == EOF || - fprintf (fprcs, "text\n@") == EOF) + fprintf (fprcs, "\n%s\n", vhead) < 0 || + fprintf (fprcs, "log\n") < 0 || + fprintf (fprcs, "@Initial revision\n@\n") < 0 || + fprintf (fprcs, "text\n@") < 0) { goto write_error; } @@ -856,19 +965,19 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) buf = xmalloc ((int) size); if (fread (buf, (int) size, 1, fpuser) != 1) error (1, errno, "cannot read file %s for copying", user); - if (expand_at_signs (buf, size, fprcs) == EOF) + if (expand_at_signs (buf, size, fprcs) < 0) { free (buf); goto write_error; } free (buf); } - if (fprintf (fprcs, "@\n\n") == EOF || - fprintf (fprcs, "\n%s.1\n", vbranch) == EOF || - fprintf (fprcs, "log\n@") == EOF || - expand_at_signs (message, (off_t) strlen (message), fprcs) == EOF || - fprintf (fprcs, "@\ntext\n") == EOF || - fprintf (fprcs, "@@\n") == EOF) + if (fprintf (fprcs, "@\n\n") < 0 || + fprintf (fprcs, "\n%s.1\n", vbranch) < 0 || + fprintf (fprcs, "log\n@") < 0 || + expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 || + fprintf (fprcs, "@\ntext\n") < 0 || + fprintf (fprcs, "@@\n") < 0) { goto write_error; } @@ -880,11 +989,16 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) (void) fclose (fpuser); /* - * Fix the modes on the RCS files. They must maintain the same modes as - * the original user file, except that all write permissions must be + * Fix the modes on the RCS files. The user modes of the original + * user file are propagated to the group and other modes as allowed + * by the repository umask, except that all write permissions are * turned off. */ - mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH); + mode = (sb.st_mode | + (sb.st_mode & S_IRWXU) >> 3 | + (sb.st_mode & S_IRWXU) >> 6) & + ~cvsumask & + ~(S_IWRITE | S_IWGRP | S_IWOTH); if (chmod (rcs, mode) < 0) { ierrno = errno; @@ -893,6 +1007,9 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) error (0, ierrno, "WARNING: cannot change mode of file %s", rcs); err++; } + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); return (err); write_error: @@ -908,11 +1025,18 @@ write_error_noclose: fperror (logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } +read_error: + if (tocvsPath) + if (unlink_file_dir (tocvsPath) < 0) + error (0, errno, "cannot remove %s", tocvsPath); + return (err + 1); } /* - * Sigh.. need to expand @ signs into double @ signs + * Write SIZE bytes at BUF to FP, expanding @ signs into double @ + * signs. If an error occurs, return a negative value and set errno + * to indicate the error. If not, return a nonnegative value. */ static int expand_at_signs (buf, size, fp) @@ -922,11 +1046,15 @@ expand_at_signs (buf, size, fp) { char *cp, *end; + errno = 0; for (cp = buf, end = buf + size; cp < end; cp++) { if (*cp == '@') - (void) putc ('@', fp); - if (putc (*cp, fp) == EOF) + { + if (putc ('@', fp) == EOF && errno != 0) + return EOF; + } + if (putc (*cp, fp) == EOF && errno != 0) return (EOF); } return (1); @@ -974,16 +1102,15 @@ import_descend_dir (message, dir, vtag, targc, targv) int targc; char *targv[]; { - char cwd[PATH_MAX]; + struct saved_cwd cwd; char *cp; int ierrno, err; if (islink (dir)) return (0); - if (getwd (cwd) == NULL) + if (save_cwd (&cwd)) { - fperror (logfp, 0, 0, "ERROR: cannot get working directory: %s", cwd); - error (0, 0, "ERROR: cannot get working directory: %s", cwd); + fperror (logfp, 0, 0, "ERROR: cannot get working directory"); return (1); } if (repository[0] == '\0') @@ -993,8 +1120,20 @@ import_descend_dir (message, dir, vtag, targc, targv) (void) strcat (repository, "/"); (void) strcat (repository, dir); } +#ifdef CLIENT_SUPPORT + if (!quiet && !client_active) +#else if (!quiet) +#endif +#ifdef SERVER_SUPPORT + /* Needs to go on stdout, not stderr, to avoid being interspersed + with the add_log messages. */ + printf ("%s %s: Importing %s\n", + program_name, command_name, repository); +#else error (0, 0, "Importing %s", repository); +#endif + if (chdir (dir) < 0) { ierrno = errno; @@ -1003,7 +1142,11 @@ import_descend_dir (message, dir, vtag, targc, targv) err = 1; goto out; } +#ifdef CLIENT_SUPPORT + if (!client_active && !isdir (repository)) +#else if (!isdir (repository)) +#endif { if (isfile (repository)) { @@ -1014,7 +1157,7 @@ import_descend_dir (message, dir, vtag, targc, targv) err = 1; goto out; } - if (noexec == 0 && mkdir (repository, 0777) < 0) + if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0) { ierrno = errno; fperror (logfp, 0, ierrno, @@ -1031,40 +1174,8 @@ import_descend_dir (message, dir, vtag, targc, targv) *cp = '\0'; else repository[0] = '\0'; - if (chdir (cwd) < 0) - error (1, errno, "cannot chdir to %s", cwd); + if (restore_cwd (&cwd, NULL)) + exit (1); + free_cwd (&cwd); return (err); } - -/* the following code is taken from code in rcs/src/rcssyn.c, and returns a - * positive value if 'expstring' contains a valid RCS expansion token for - * the -k option. If an invalid expansion is named, then return -1. - */ - -char const *const expand_names[] = { - /* These must agree with *_EXPAND in rcs/src/rcsbase.h. */ - "kv","kvl","k","v","o", - 0 -}; - -static int -str2expmode(s) - char const *s; -/* Yield expand mode corresponding to S, or -1 if bad. */ -{ - return strn2expmode(s, strlen(s)); -} - -static int -strn2expmode(s, n) - char const *s; - size_t n; -{ - char const *const *p; - - for (p = expand_names; *p; ++p) - if (memcmp(*p,s,n) == 0 && !(*p)[n]) - return p - expand_names; - return -1; -} - diff --git a/gnu/usr.bin/cvs/cvs/lock.c b/gnu/usr.bin/cvs/cvs/lock.c index 940e963..3e15fbb 100644 --- a/gnu/usr.bin/cvs/cvs/lock.c +++ b/gnu/usr.bin/cvs/cvs/lock.c @@ -13,31 +13,30 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)lock.c 1.50 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)lock.c 1.50 94/09/30 $"; +USE(rcsid); #endif -extern char *ctime (); - static int readers_exist PROTO((char *repository)); -static int set_lock PROTO((char *lockdir, int will_wait, char *repository)); +static int set_lock PROTO((char *repository, int will_wait)); +static void clear_lock PROTO((void)); static void set_lockers_name PROTO((struct stat *statp)); static int set_writelock_proc PROTO((Node * p, void *closure)); static int unlock_proc PROTO((Node * p, void *closure)); static int write_lock PROTO((char *repository)); static void unlock PROTO((char *repository)); static void lock_wait PROTO((char *repository)); +static int Check_Owner PROTO((char *lockdir)); static char lockers_name[20]; static char *repository; -static char readlock[PATH_MAX], writelock[PATH_MAX]; +static char readlock[PATH_MAX], writelock[PATH_MAX], masterlock[PATH_MAX]; static int cleanup_lckdir; static List *locklist; #define L_OK 0 /* success */ #define L_ERROR 1 /* error condition */ -#define L_LOCK_OWNED 2 /* lock already owned by us */ -#define L_LOCKED 3 /* lock owned by someone else */ +#define L_LOCKED 2 /* lock owned by someone else */ /* * Clean up all outstanding locks @@ -80,19 +79,18 @@ unlock (repository) char *repository; { char tmp[PATH_MAX]; - struct stat sb; if (readlock[0] != '\0') { (void) sprintf (tmp, "%s/%s", repository, readlock); - if (unlink (tmp) < 0 && errno != ENOENT) + if (unlink (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); } if (writelock[0] != '\0') { (void) sprintf (tmp, "%s/%s", repository, writelock); - if (unlink (tmp) < 0 && errno != ENOENT) + if (unlink (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); } @@ -101,21 +99,53 @@ unlock (repository) * lead to the limitation that one user ID should not be committing * files into the same Repository directory at the same time. Oh well. */ - (void) sprintf (tmp, "%s/%s", repository, CVSLCK); - if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid () && - (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir))) + if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)) { - (void) rmdir (tmp); + (void) sprintf (tmp, "%s/%s", repository, CVSLCK); + if (Check_Owner(tmp)) + { +#ifdef AFSCVS + char rmuidlock[PATH_MAX]; + sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() ); + system(rmuidlock); +#endif + (void) rmdir (tmp); + } } cleanup_lckdir = 0; } /* - * Since some systems don't define this... + * Check the owner of a lock. Returns 1 if we own it, 0 otherwise. */ -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 256 +static int +Check_Owner(lockdir) + char *lockdir; +{ + struct stat sb; + +#ifdef AFSCVS + /* In the Andrew File System (AFS), user ids from stat don't match + those from geteuid(). The AFSCVS code can deal with either AFS or + non-AFS repositories; the non-AFSCVS code is faster. */ + char uidlock[PATH_MAX]; + + /* Check if the uidlock is in the lock directory */ + sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() ); + if( stat(uidlock, &sb) != -1) + return 1; /* The file exists, therefore we own the lock */ + else + return 0; /* The file didn't exist or some other error. + * Assume that we don't own it. + */ +#else + if (stat (lockdir, &sb) != -1 && sb.st_uid == geteuid ()) + return 1; + else + return 0; #endif +} /* end Check_Owner() */ + /* * Create a lock file for readers @@ -127,18 +157,10 @@ Reader_Lock (xrepository) int err = 0; FILE *fp; char tmp[PATH_MAX]; -#ifdef HAVE_LONG_FILE_NAMES - char hostname[MAXHOSTNAMELEN]; -#endif if (noexec) return (0); -#ifdef HAVE_LONG_FILE_NAMES - memset(hostname, 0, sizeof(hostname)); - gethostname(hostname, sizeof(hostname) - 1); -#endif - /* we only do one directory at a time for read locks! */ if (repository != NULL) { @@ -158,13 +180,7 @@ Reader_Lock (xrepository) /* remember what we're locking (for lock_cleanup) */ repository = xrepository; - /* make sure we clean up on error */ - (void) SIG_register (SIGHUP, Lock_Cleanup); - (void) SIG_register (SIGINT, Lock_Cleanup); - (void) SIG_register (SIGQUIT, Lock_Cleanup); - (void) SIG_register (SIGPIPE, Lock_Cleanup); - (void) SIG_register (SIGTERM, Lock_Cleanup); - +#ifdef BOGUS_UNLESS_PROVEN_OTHERWISE /* make sure we can write the repository */ (void) sprintf (tmp, #ifdef HAVE_LONG_FILE_NAMES @@ -178,16 +194,16 @@ Reader_Lock (xrepository) error (0, errno, "cannot create read lock in repository `%s'", xrepository); readlock[0] = '\0'; - if (unlink (tmp) < 0 && errno != ENOENT) + if (unlink (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); return (1); } if (unlink (tmp) < 0) error (0, errno, "failed to remove lock %s", tmp); +#endif /* get the lock dir for our own */ - (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK); - if (set_lock (tmp, 1, xrepository) != L_OK) + if (set_lock (xrepository, 1) != L_OK) { error (0, 0, "failed to obtain dir lock in repository `%s'", xrepository); @@ -206,9 +222,7 @@ Reader_Lock (xrepository) } /* free the lock dir */ - (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK); - if (rmdir (tmp) < 0) - error (0, errno, "failed to remove lock dir `%s'", tmp); + clear_lock(); return (err); } @@ -294,14 +308,6 @@ write_lock (repository) int status; FILE *fp; char tmp[PATH_MAX]; -#ifdef HAVE_LONG_FILE_NAMES - char hostname[MAXHOSTNAMELEN]; -#endif - -#ifdef HAVE_LONG_FILE_NAMES - memset(hostname, 0, sizeof(hostname)); - gethostname(hostname, sizeof(hostname) - 1); -#endif if (writelock[0] == '\0') (void) sprintf (writelock, @@ -312,13 +318,7 @@ write_lock (repository) #endif getpid()); - /* make sure we clean up on error */ - (void) SIG_register (SIGHUP, Lock_Cleanup); - (void) SIG_register (SIGINT, Lock_Cleanup); - (void) SIG_register (SIGQUIT, Lock_Cleanup); - (void) SIG_register (SIGPIPE, Lock_Cleanup); - (void) SIG_register (SIGTERM, Lock_Cleanup); - +#ifdef BOGUS_UNLESS_PROVEN_OTHERWISE /* make sure we can write the repository */ (void) sprintf (tmp, #ifdef HAVE_LONG_FILE_NAMES @@ -331,17 +331,17 @@ write_lock (repository) { error (0, errno, "cannot create write lock in repository `%s'", repository); - if (unlink (tmp) < 0 && errno != ENOENT) + if (unlink (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); return (L_ERROR); } if (unlink (tmp) < 0) error (0, errno, "failed to remove lock %s", tmp); +#endif /* make sure the lock dir is ours (not necessarily unique to us!) */ - (void) sprintf (tmp, "%s/%s", repository, CVSLCK); - status = set_lock (tmp, 0, repository); - if (status == L_OK || status == L_LOCK_OWNED) + status = set_lock (repository, 0); + if (status == L_OK) { /* we now own a writer - make sure there are no readers */ if (readers_exist (repository)) @@ -349,8 +349,7 @@ write_lock (repository) /* clean up the lock dir if we created it */ if (status == L_OK) { - if (rmdir (tmp) < 0) - error (0, errno, "failed to remove lock dir `%s'", tmp); + clear_lock(); } /* indicate we failed due to read locks instead of error */ @@ -363,15 +362,13 @@ write_lock (repository) { int xerrno = errno; - if (unlink (tmp) < 0 && errno != ENOENT) + if (unlink (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); /* free the lock dir if we created it */ if (status == L_OK) { - (void) sprintf (tmp, "%s/%s", repository, CVSLCK); - if (rmdir (tmp) < 0) - error (0, errno, "failed to remove lock dir `%s'", tmp); + clear_lock(); } /* return the error */ @@ -428,9 +425,7 @@ again: */ if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1) { - if (closedir (dirp) < 0) - error (0, errno, - "error closing directory %s", repository); + (void) closedir (dirp); goto again; } #endif @@ -445,8 +440,7 @@ again: if (errno != 0) error (0, errno, "error reading directory %s", repository); - if (closedir (dirp) < 0) - error (0, errno, "error closing directory %s", repository); + closedir (dirp); return (ret); } @@ -466,7 +460,7 @@ set_lockers_name (statp) (void) strcpy (lockers_name, pw->pw_name); } else - (void) sprintf (lockers_name, "uid%d", statp->st_uid); + (void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid); } /* @@ -475,16 +469,18 @@ set_lockers_name (statp) * seconds old, just try to remove the directory. */ static int -set_lock (lockdir, will_wait, repository) - char *lockdir; - int will_wait; +set_lock (repository, will_wait) char *repository; + int will_wait; { struct stat sb; + mode_t omask; #ifdef CVS_FUDGELOCKS time_t now; #endif + (void) sprintf (masterlock, "%s/%s", repository, CVSLCK); + /* * Note that it is up to the callers of set_lock() to arrange for signal * handlers that do the appropriate things, like remove the lock @@ -493,14 +489,39 @@ set_lock (lockdir, will_wait, repository) cleanup_lckdir = 0; for (;;) { + int status = -1; + omask = umask (cvsumask); SIG_beginCrSect (); - if (mkdir (lockdir, 0777) == 0) + if (CVS_MKDIR (masterlock, 0777) == 0) { +#ifdef AFSCVS + char uidlock[PATH_MAX]; + FILE *fp; + + sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() ); + if ((fp = fopen(uidlock, "w+")) == NULL) + { + /* We failed to create the uidlock, + so rm masterlock and leave */ + rmdir(masterlock); + SIG_endCrSect (); + status = L_ERROR; + goto out; + } + + /* We successfully created the uid lock, so close the file */ + fclose(fp); +#endif cleanup_lckdir = 1; SIG_endCrSect (); - return (L_OK); + status = L_OK; + goto out; } SIG_endCrSect (); + out: + (void) umask (omask); + if (status != -1) + return status; if (errno != EEXIST) { @@ -514,24 +535,16 @@ set_lock (lockdir, will_wait, repository) * stat the dir - if it is non-existent, re-try the loop since * someone probably just removed it (thus releasing the lock) */ - if (stat (lockdir, &sb) < 0) + if (stat (masterlock, &sb) < 0) { - if (errno == ENOENT) + if (existence_error (errno)) continue; - error (0, errno, "couldn't stat lock directory `%s'", lockdir); + error (0, errno, "couldn't stat lock directory `%s'", masterlock); return (L_ERROR); } - /* - * if we already own the lock, go ahead and return 1 which means it - * existed but we owned it - */ - if (sb.st_uid == geteuid () && !will_wait) - return (L_LOCK_OWNED); - #ifdef CVS_FUDGELOCKS - /* * If the create time of the directory is more than CVSLCKAGE seconds * ago, try to clean-up the lock directory, and if successful, just @@ -540,7 +553,13 @@ set_lock (lockdir, will_wait, repository) (void) time (&now); if (now >= (sb.st_ctime + CVSLCKAGE)) { - if (rmdir (lockdir) >= 0) +#ifdef AFSCVS + /* Remove the uidlock first */ + char rmuidlock[PATH_MAX]; + sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); + system(rmuidlock); +#endif + if (rmdir (masterlock) >= 0) continue; } #endif @@ -556,6 +575,24 @@ set_lock (lockdir, will_wait, repository) } /* + * Clear master lock. We don't have to recompute the lock name since + * clear_lock is never called except after a successful set_lock(). + */ +static void +clear_lock() +{ +#ifdef AFSCVS + /* Remove the uidlock first */ + char rmuidlock[PATH_MAX]; + sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); + system(rmuidlock); +#endif + if (rmdir (masterlock) < 0) + error (0, errno, "failed to remove lock dir `%s'", masterlock); + cleanup_lckdir = 0; +} + +/* * Print out a message that the lock is still held, then sleep a while. */ static void diff --git a/gnu/usr.bin/cvs/cvs/log.c b/gnu/usr.bin/cvs/cvs/log.c index 4c3718e..88c9adc 100644 --- a/gnu/usr.bin/cvs/cvs/log.c +++ b/gnu/usr.bin/cvs/cvs/log.c @@ -7,6 +7,9 @@ * * Print Log Information * + * This line exists solely to test some pcl-cvs/ChangeLog stuff. You + * can delete it, if indeed it's still here when you read it. -Karl + * * Prints the RCS "log" (rlog) information for the specified files. With no * argument, prints the log information for all the files in the directory * (recursive by default). @@ -15,30 +18,30 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)log.c 1.44 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)log.c 1.44 94/09/30 $"; +USE(rcsid); #endif static Dtype log_dirproc PROTO((char *dir, char *repository, char *update_dir)); static int log_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); -static char options[PATH_MAX]; - -static char *log_usage[] = +static const char *const log_usage[] = { "Usage: %s %s [-l] [rlog-options] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", NULL }; +static int ac; +static char **av; + int cvslog (argc, argv) int argc; - char *argv[]; + char **argv; { int i; - int numopt = 1; int err = 0; int local = 0; @@ -48,34 +51,49 @@ cvslog (argc, argv) /* * All 'log' command options except -l are passed directly on to 'rlog' */ - options[0] = '\0'; /* Assume none */ - for (i = 1; i < argc; i++) - { - if (argv[i][0] == '-' || argv[i][0] == '\0') - { - numopt++; - switch (argv[i][1]) - { - case 'l': - local = 1; - break; - default: - (void) strcat (options, " "); - (void) strcat (options, argv[i]); - break; - } - } + for (i = 1; i < argc && argv[i][0] == '-'; i++) + if (argv[i][1] == 'l') + local = 1; + + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (client_active) { + /* We're the local client. Fire up the remote server. */ + start_server (); + + ign_setup (); + + for (i = 1; i < argc && argv[i][0] == '-'; i++) + send_arg (argv[i]); + +#if 0 +/* FIXME: We shouldn't have to send current files to get log entries, but it + doesn't work yet and I haven't debugged it. So send the files -- + it's slower but it works. gnu@cygnus.com Apr94 */ + send_file_names (argc - i, argv + i); +#else + send_files (argc - i, argv + i, local, 0); +#endif + + if (fprintf (to_server, "log\n") < 0) + error (1, errno, "writing to server"); + err = get_responses_and_close (); + return err; } - argc -= numopt; - argv += numopt; - err = start_recursion (log_fileproc, (int (*) ()) NULL, log_dirproc, - (int (*) ()) NULL, argc, argv, local, + ac = argc; + av = argv; +#endif + + err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc, + (DIRLEAVEPROC) NULL, argc - i, argv + i, local, W_LOCAL | W_REPOS | W_ATTIC, 0, 1, (char *) NULL, 1, 0); return (err); } + /* * Do an rlog on a file */ @@ -117,8 +135,23 @@ log_fileproc (file, update_dir, repository, entries, srcfiles) return (1); } - run_setup ("%s%s %s", Rcsbin, RCS_RLOG, options); + run_setup ("%s%s", Rcsbin, RCS_RLOG); + { + int i; + for (i = 1; i < ac && av[i][0] == '-'; i++) + if (av[i][1] != 'l') + run_arg (av[i]); + } run_arg (rcsfile->path); + + if (*update_dir) + { + char *workfile = xmalloc (strlen (update_dir) + strlen (file) + 2); + sprintf (workfile, "%s/%s", update_dir, file); + run_arg (workfile); + free (workfile); + } + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY)) == -1) { error (1, errno, "fork failed for rlog on %s", file); diff --git a/gnu/usr.bin/cvs/cvs/logmsg.c b/gnu/usr.bin/cvs/cvs/logmsg.c index 8fd0b2d..5f2763f 100644 --- a/gnu/usr.bin/cvs/cvs/logmsg.c +++ b/gnu/usr.bin/cvs/cvs/logmsg.c @@ -7,18 +7,11 @@ */ #include "cvs.h" +#include "getline.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)logmsg.c 1.48 94/09/29 $"; -USE(rcsid) -#endif - -/* this is slightly dangerous, since it could conflict with other systems' - * own prototype. - */ -#if 0 -/* Which is why I'll nuke this */ -extern int gethostname PROTO((char *name, int len)); +static const char rcsid[] = "$CVSid: @(#)logmsg.c 1.48 94/09/29 $"; +USE(rcsid); #endif static int find_type PROTO((Node * p, void *closure)); @@ -33,7 +26,7 @@ static void setup_tmpfile PROTO((FILE * xfp, char *xprefix, List * changes)); static int editinfo_proc PROTO((char *repository, char *template)); static FILE *fp; -static char *strlist; +static char *str_list; static char *editinfo_editor; static Ctype type; @@ -137,7 +130,10 @@ do_editor (dir, messagep, repository, changes) List *changes; { static int reuse_log_message = 0; - char line[MAXLINELEN], fname[L_tmpnam+1]; + char *line; + int line_length; + size_t line_chars_allocated; + char fname[L_tmpnam+1]; struct stat pre_stbuf, post_stbuf; int retcode = 0; char *p; @@ -171,7 +167,7 @@ do_editor (dir, messagep, repository, changes) (void) fprintf (fp, "%sEnter Log. Lines beginning with `%s' are removed automatically\n%s\n", CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX); - if (dir != NULL) + if (dir != NULL && *dir) (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); if (changes != NULL) @@ -181,7 +177,8 @@ do_editor (dir, messagep, repository, changes) CVSEDITPREFIX); /* finish off the temp file */ - (void) fclose (fp); + if (fclose (fp) == EOF) + error (1, errno, "%s", fname); if (stat (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; @@ -214,25 +211,34 @@ do_editor (dir, messagep, repository, changes) *messagep = NULL; else { - *messagep = (char *) xmalloc (post_stbuf.st_size); + *messagep = (char *) xmalloc (post_stbuf.st_size + 1); *messagep[0] = '\0'; } -/* !!! XXX FIXME: fgets is broken. This should not have any line - length limits. */ + line = NULL; + line_chars_allocated = 0; if (*messagep) { p = *messagep; - while (fgets (line, sizeof (line), fp) != NULL) + while (1) { + line_length = getline (&line, &line_chars_allocated, fp); + if (line_length == -1) + { + if (ferror (fp)) + error (0, errno, "warning: cannot read %s", fname); + break; + } if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0) continue; (void) strcpy (p, line); - p += strlen (line); + p += line_length; } } - (void) fclose (fp); + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", fname); + if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL || strcmp (*messagep, "\n") == 0) @@ -243,9 +249,9 @@ do_editor (dir, messagep, repository, changes) (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); (void) printf ("Action: (continue) "); (void) fflush (stdout); - *line = '\0'; - (void) fgets (line, sizeof (line), stdin); - if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C') + line_length = getline (&line, &line_chars_allocated, stdin); + if (line_length <= 0 + || *line == '\n' || *line == 'c' || *line == 'C') break; if (*line == 'a' || *line == 'A') error (1, 0, "aborted by user"); @@ -259,7 +265,10 @@ do_editor (dir, messagep, repository, changes) (void) printf ("Unknown input\n"); } } - (void) unlink_file (fname); + if (line) + free (line); + if (unlink_file (fname) < 0) + error (0, errno, "warning: cannot remove temp file %s", fname); } /* @@ -275,7 +284,6 @@ rcsinfo_proc (repository, template) { static char *last_template; FILE *tfp; - char line[MAXLINELEN]; /* nothing to do if the last one included is the same as this one */ if (last_template && strcmp (last_template, template) == 0) @@ -286,14 +294,22 @@ rcsinfo_proc (repository, template) if ((tfp = fopen (template, "r")) != NULL) { - while (fgets (line, sizeof (line), tfp) != NULL) + char *line; + size_t line_chars_allocated; + + while (getline (&line, &line_chars_allocated, tfp) >= 0) (void) fputs (line, fp); - (void) fclose (tfp); + if (ferror (tfp)) + error (0, errno, "warning: cannot read %s", template); + if (fclose (tfp) < 0) + error (0, errno, "warning: cannot close %s", template); + if (line) + free (line); return (0); } else { - error (0, 0, "Couldn't open rcsinfo template file %s", template); + error (0, errno, "Couldn't open rcsinfo template file %s", template); return (1); } } @@ -334,9 +350,9 @@ Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges) srepos = Short_Repository (repository); /* allocate a chunk of memory to hold the title string */ - if (!strlist) - strlist = xmalloc (MAXLISTLEN); - strlist[0] = '\0'; + if (!str_list) + str_list = xmalloc (MAXLISTLEN); + str_list[0] = '\0'; type = T_TITLE; (void) walklist (changes, title_proc, NULL); @@ -346,12 +362,12 @@ Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges) (void) walklist (changes, title_proc, NULL); type = T_REMOVED; (void) walklist (changes, title_proc, NULL); - title = xmalloc (strlen (srepos) + strlen (strlist) + 1 + 2); /* for 's */ - (void) sprintf (title, "'%s%s'", srepos, strlist); + title = xmalloc (strlen (srepos) + strlen (str_list) + 1 + 2); /* for 's */ + (void) sprintf (title, "'%s%s'", srepos, str_list); /* to be nice, free up this chunk of memory */ - free (strlist); - strlist = (char *) NULL; + free (str_list); + str_list = (char *) NULL; /* call Parse_Info to do the actual logfile updates */ (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1); @@ -373,7 +389,7 @@ update_logfile_proc (repository, filter) } /* - * concatenate each name onto strlist + * concatenate each name onto str_list */ static int title_proc (p, closure) @@ -382,8 +398,8 @@ title_proc (p, closure) { if (p->data == (char *) type) { - (void) strcat (strlist, " "); - (void) strcat (strlist, p->key); + (void) strcat (str_list, " "); + (void) strcat (str_list, p->key); } return (0); } @@ -409,12 +425,13 @@ logfile_write (repository, filter, title, message, revision, logfp, changes) FILE *logfp; List *changes; { - char cwd[PATH_MAX], host[MAXHOSTNAMELEN]; + char cwd[PATH_MAX]; FILE *pipefp, *Popen (); char *prog = xmalloc (MAXPROGLEN); char *cp; int c; + /* XXX <woods@web.net> -- this is gross, ugly, and a hack! FIXME! */ /* * A maximum of 6 %s arguments are supported in the filter */ @@ -426,10 +443,8 @@ logfile_write (repository, filter, title, message, revision, logfp, changes) free (prog); return (1); } - if (gethostname (host, sizeof (host)) < 0) - (void) strcpy (host, "(unknown)"); (void) fprintf (pipefp, "Update of %s\n", repository); - (void) fprintf (pipefp, "In directory %s:%s\n\n", host, + (void) fprintf (pipefp, "In directory %s:%s\n\n", hostname, ((cp = getwd (cwd)) != NULL) ? cp : cwd); if (revision && *revision) (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision); @@ -438,7 +453,7 @@ logfile_write (repository, filter, title, message, revision, logfp, changes) if (logfp != (FILE *) 0) { (void) fprintf (pipefp, "Status:\n"); - (void) rewind (logfp); + rewind (logfp); while ((c = getc (logfp)) != EOF) (void) putc ((char) c, pipefp); } diff --git a/gnu/usr.bin/cvs/cvs/main.c b/gnu/usr.bin/cvs/cvs/main.c index 3751b3c..91e376d 100644 --- a/gnu/usr.bin/cvs/cvs/main.c +++ b/gnu/usr.bin/cvs/cvs/main.c @@ -21,6 +21,7 @@ * commit Checks files into the repository * diff Runs diffs between revisions * log Prints "rlog" information for files + * login Record user, host, repos, password * add Adds an entry to the repository * remove Removes an entry from the repository * status Status info on the revisions @@ -35,16 +36,40 @@ #include "cvs.h" #include "patchlevel.h" -#ifndef lint -char rcsid[] = "$CVSid: @(#)main.c 1.78 94/10/07 $\n"; -USE(rcsid) +#if HAVE_KERBEROS +#include <sys/socket.h> +#include <netinet/in.h> +#include <krb.h> +#ifndef HAVE_KRB_GET_ERR_TEXT +#define krb_get_err_text(status) krb_err_txt[status] +#endif #endif -extern char *getenv (); +#ifndef lint +static const char rcsid[] = "$CVSid: @(#)main.c 1.78 94/10/07 $\n"; +USE(rcsid); +#endif char *program_name; -char *command_name = ""; +char *program_path; +/* + * Initialize comamnd_name to "cvs" so that the first call to + * read_cvsrc tries to find global cvs options. + */ +char *command_name = "cvs"; +/* + * Since some systems don't define this... + */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +char hostname[MAXHOSTNAMELEN]; + +#ifdef AUTH_CLIENT_SUPPORT +int use_authenticating_server = FALSE; +#endif /* AUTH_CLIENT_SUPPORT */ int use_editor = TRUE; int use_cvsrc = TRUE; int cvswrite = !CVSREAD_DFLT; @@ -53,6 +78,7 @@ int quiet = FALSE; int trace = FALSE; int noexec = FALSE; int logoff = FALSE; +mode_t cvsumask = UMASK_DFLT; char *CurDir; @@ -77,6 +103,9 @@ int diff PROTO((int argc, char **argv)); int history PROTO((int argc, char **argv)); int import PROTO((int argc, char **argv)); int cvslog PROTO((int argc, char **argv)); +#ifdef AUTH_CLIENT_SUPPORT +int login PROTO((int argc, char **argv)); +#endif /* AUTH_CLIENT_SUPPORT */ int patch PROTO((int argc, char **argv)); int release PROTO((int argc, char **argv)); int cvsremove PROTO((int argc, char **argv)); @@ -85,41 +114,69 @@ int status PROTO((int argc, char **argv)); int tag PROTO((int argc, char **argv)); int update PROTO((int argc, char **argv)); -struct cmd +const struct cmd { char *fullname; /* Full name of the function (e.g. "commit") */ char *nick1; /* alternate name (e.g. "ci") */ char *nick2; /* another alternate names (e.g. "ci") */ int (*func) (); /* Function takes (argc, argv) arguments. */ +#ifdef CLIENT_SUPPORT + int (*client_func) (); /* Function to do it via the protocol. */ +#endif } cmds[] = { - { "add", "ad", "new", add }, - { "admin", "adm", "rcs", admin }, - { "checkout", "co", "get", checkout }, - { "commit", "ci", "com", commit }, - { "diff", "di", "dif", diff }, - { "export", "exp", "ex", checkout }, - { "history", "hi", "his", history }, - { "import", "im", "imp", import }, - { "log", "lo", "rlog", cvslog }, - { "rdiff", "patch", "pa", patch }, - { "release", "re", "rel", release }, - { "remove", "rm", "delete", cvsremove }, - { "status", "st", "stat", status }, - { "rtag", "rt", "rfreeze", rtag }, - { "tag", "ta", "freeze", tag }, - { "update", "up", "upd", update }, - { NULL, NULL, NULL, NULL }, +#ifdef CLIENT_SUPPORT +#define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1, f2 } +#else +#define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1 } +#endif + + CMD_ENTRY("add", "ad", "new", add, client_add), +#ifndef CVS_NOADMIN + CMD_ENTRY("admin", "adm", "rcs", admin, client_admin), +#endif + CMD_ENTRY("checkout", "co", "get", checkout, client_checkout), + CMD_ENTRY("commit", "ci", "com", commit, client_commit), + CMD_ENTRY("diff", "di", "dif", diff, client_diff), + CMD_ENTRY("export", "exp", "ex", checkout, client_export), + CMD_ENTRY("history", "hi", "his", history, client_history), + CMD_ENTRY("import", "im", "imp", import, client_import), + CMD_ENTRY("log", "lo", "rlog", cvslog, client_log), +#ifdef AUTH_CLIENT_SUPPORT + CMD_ENTRY("login", "logon", "lgn", login, login), +#endif /* AUTH_CLIENT_SUPPORT */ + CMD_ENTRY("rdiff", "patch", "pa", patch, client_rdiff), + CMD_ENTRY("release", "re", "rel", release, client_release), + CMD_ENTRY("remove", "rm", "delete", cvsremove, client_remove), + CMD_ENTRY("status", "st", "stat", status, client_status), + CMD_ENTRY("rtag", "rt", "rfreeze", rtag, client_rtag), + CMD_ENTRY("tag", "ta", "freeze", tag, client_tag), + CMD_ENTRY("update", "up", "upd", update, client_update), + +#ifdef SERVER_SUPPORT + /* + * The client_func is also server because we might have picked up a + * CVSROOT environment variable containing a colon. The client will send + * the real root later. + */ + CMD_ENTRY("server", "server", "server", server, server), +#endif + CMD_ENTRY(NULL, NULL, NULL, NULL, NULL), + +#undef CMD_ENTRY }; -static char *usg[] = +static const char *const usg[] = { "Usage: %s [cvs-options] command [command-options] [files...]\n", " Where 'cvs-options' are:\n", " -H Displays Usage information for command\n", " -Q Cause CVS to be really quiet.\n", " -q Cause CVS to be somewhat quiet.\n", +#ifdef AUTH_CLIENT_SUPPORT + " -a Use the authenticating server, not rsh.\n", +#endif /* AUTH_CLIENT_SUPPORT */ " -r Make checked-out files read-only\n", " -w Make checked-out files read-write (default)\n", " -l Turn History logging off\n", @@ -130,6 +187,9 @@ static char *usg[] = " -e editor Use 'editor' for editing log information\n", " -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n", " -f Do not use the ~/.cvsrc file\n", +#ifdef CLIENT_SUPPORT + " -z # Use 'gzip -#' for net traffic if possible.\n", +#endif "\n", " and where 'command' is:\n", " add Adds a new file/directory to the repository\n", @@ -141,6 +201,9 @@ static char *usg[] = " import Import sources into CVS, using vendor branches\n", " export Export sources from CVS, similar to checkout\n", " log Prints out 'rlog' information for files\n", +#ifdef AUTH_CLIENT_SUPPORT + " login Prompt for password for authenticating server.\n", +#endif /* AUTH_CLIENT_SUPPORT */ " rdiff 'patch' format diffs between releases\n", " release Indicate that a Module is no longer in use\n", " remove Removes an entry from the repository\n", @@ -157,29 +220,61 @@ main_cleanup () exit (1); } +static void +error_cleanup () +{ + Lock_Cleanup(); +#ifdef SERVER_SUPPORT + if (server_active) + server_cleanup (0); +#endif +} + int main (argc, argv) int argc; - char *argv[]; + char **argv; { extern char *version_string; - char *cp; - struct cmd *cm; - int c, help = FALSE, err = 0; + extern char *config_string; + char *cp, *end; + const struct cmd *cm; + int c, err = 0; + static int help = FALSE, version_flag = FALSE; int rcsbin_update_env, cvs_update_env = 0; char tmp[PATH_MAX]; + static struct option long_options[] = + { + {"help", 0, &help, TRUE}, + {"version", 0, &version_flag, TRUE}, + {0, 0, 0, 0} + }; + /* `getopt_long' stores the option index here, but right now we + don't use it. */ + int option_index = 0; + + error_set_cleanup (error_cleanup); + +/* The IBM TCP/IP library under OS/2 needs to be initialized: */ +#ifdef NEED_CALL_SOCKINIT + if (SockInit () != TRUE) + { + fprintf (stderr, "SockInit() failed!\n"); + exit (1); + } +#endif /* NEED_CALL_SOCKINIT */ /* * Just save the last component of the path for error messages */ - if ((program_name = strrchr (argv[0], '/')) == NULL) - program_name = argv[0]; - else - program_name++; + program_path = xstrdup (argv[0]); + program_name = last_component (argv[0]); CurDir = xmalloc (PATH_MAX); +#ifndef SERVER_SUPPORT if (!getwd (CurDir)) error (1, 0, "cannot get working directory: %s", CurDir); +#endif /* * Query the environment variables up-front, so that @@ -196,6 +291,8 @@ main (argc, argv) Editor = cp; else if ((cp = getenv (EDITOR2_ENV)) != NULL) Editor = cp; + else if ((cp = getenv (EDITOR3_ENV)) != NULL) + Editor = cp; if ((cp = getenv (CVSROOT_ENV)) != NULL) { CVSroot = cp; @@ -203,12 +300,36 @@ main (argc, argv) } if (getenv (CVSREAD_ENV) != NULL) cvswrite = FALSE; + if ((cp = getenv (CVSUMASK_ENV)) != NULL) + { + /* FIXME: Should be accepting symbolic as well as numeric mask. */ + cvsumask = strtol (cp, &end, 8) & 0777; + if (*end != '\0') + error (1, errno, "invalid umask value in %s (%s)", + CVSUMASK_ENV, cp); + } + + /* + * Scan cvsrc file for global options. + */ + read_cvsrc(&argc, &argv); + /* This has the effect of setting getopt's ordering to REQUIRE_ORDER, + which is what we need to distinguish between global options and + command options. FIXME: It would appear to be possible to do this + much less kludgily by passing "+" as the first character to the + option string we pass to getopt_long. */ optind = 1; - while ((c = getopt (argc, argv, "Qqrwtnlvb:e:d:Hf")) != -1) - { + + while ((c = getopt_long + (argc, argv, "Qqrawtnlvb:e:d:Hfz:", long_options, &option_index)) + != EOF) + { switch (c) - { + { + case 0: + /* getopt_long took care of setting the flag. */ + break; case 'Q': really_quiet = TRUE; /* FALL THROUGH */ @@ -218,6 +339,11 @@ main (argc, argv) case 'r': cvswrite = FALSE; break; +#ifdef AUTH_CLIENT_SUPPORT + case 'a': + use_authenticating_server = TRUE; + break; +#endif /* AUTH_CLIENT_SUPPORT */ case 'w': cvswrite = TRUE; break; @@ -230,11 +356,7 @@ main (argc, argv) logoff = TRUE; break; case 'v': - (void) fputs (version_string, stdout); - (void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL); - (void) fputs (tmp, stdout); - (void) fputs ("\nCopyright (c) 1993-1994 Brian Berliner\nCopyright (c) 1993-1994 david d `zoo' zuhn\nCopyright (c) 1992, Brian Berliner and Jeff Polk\nCopyright (c) 1989-1992, Brian Berliner\n\nCVS may be copied only under the terms of the GNU General Public License,\na copy of which can be found with the CVS distribution kit.\n", stdout); - exit (0); + version_flag = TRUE; break; case 'b': Rcsbin = optarg; @@ -254,55 +376,181 @@ main (argc, argv) case 'f': use_cvsrc = FALSE; break; +#ifdef CLIENT_SUPPORT + case 'z': + gzip_level = atoi (optarg); + if (gzip_level <= 0 || gzip_level > 9) + error (1, 0, + "gzip compression level must be between 1 and 9"); + break; +#endif case '?': default: - usage (usg); + usage (usg); } } + + if (version_flag == TRUE) + { + (void) fputs (version_string, stdout); + (void) fputs (config_string, stdout); + (void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL); + (void) fputs (tmp, stdout); + (void) fputs ("\n", stdout); + (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout); + (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout); + (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout); + (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout); + (void) fputs ("\n", stdout); + (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); + (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout); + exit (0); + } + argc -= optind; argv += optind; if (argc < 1) usage (usg); +#ifdef HAVE_KERBEROS + /* If we are invoked with a single argument "kserver", then we are + running as Kerberos server as root. Do the authentication as + the very first thing, to minimize the amount of time we are + running as root. */ + if (strcmp (argv[0], "kserver") == 0) + { + int status; + char instance[INST_SZ]; + struct sockaddr_in peer; + struct sockaddr_in laddr; + int len; + KTEXT_ST ticket; + AUTH_DAT auth; + char version[KRB_SENDAUTH_VLEN]; + Key_schedule sched; + char user[ANAME_SZ]; + struct passwd *pw; + + strcpy (instance, "*"); + len = sizeof peer; + if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 + || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, + &len) < 0) + { + printf ("E Fatal error, aborting.\n\ +error %s getpeername or getsockname failed\n", strerror (errno)); + exit (1); + } + + status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd", + instance, &peer, &laddr, &auth, "", sched, + version); + if (status != KSUCCESS) + { + printf ("E Fatal error, aborting.\n\ +error 0 kerberos: %s\n", krb_get_err_text(status)); + exit (1); + } + + /* Get the local name. */ + status = krb_kntoln (&auth, user); + if (status != KSUCCESS) + { + printf ("E Fatal error, aborting.\n\ +error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); + exit (1); + } + + pw = getpwnam (user); + if (pw == NULL) + { + printf ("E Fatal error, aborting.\n\ +error 0 %s: no such user\n", user); + exit (1); + } + + initgroups (pw->pw_name, pw->pw_gid); + setgid (pw->pw_gid); + setuid (pw->pw_uid); + /* Inhibit access by randoms. Don't want people randomly + changing our temporary tree before we check things in. */ + umask (077); + +#if HAVE_PUTENV + /* Set LOGNAME and USER in the environment, in case they are + already set to something else. */ + { + char *env; + + env = xmalloc (sizeof "LOGNAME=" + strlen (user)); + (void) sprintf (env, "LOGNAME=%s", user); + (void) putenv (env); + + env = xmalloc (sizeof "USER=" + strlen (user)); + (void) sprintf (env, "USER=%s", user); + (void) putenv (env); + } +#endif + + /* Pretend we were invoked as a plain server. */ + argv[0] = "server"; + } +#endif /* HAVE_KERBEROS */ + + +#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT) + if (strcmp (argv[0], "pserver") == 0) + { + /* Gets username and password from client, authenticates, then + switches to run as that user and sends an ACK back to the + client. */ + authenticate_connection (); + + /* Pretend we were invoked as a plain server. */ + argv[0] = "server"; + } +#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */ + + #ifdef CVSADM_ROOT /* * See if we are able to find a 'better' value for CVSroot in the * CVSADM_ROOT directory. */ +#ifdef SERVER_SUPPORT + if (strcmp (argv[0], "server") == 0 && CVSroot == NULL) + CVSADM_Root = NULL; + else + CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); +#else /* No SERVER_SUPPORT */ CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); +#endif /* No SERVER_SUPPORT */ if (CVSADM_Root != NULL) { - if (CVSroot == NULL) + if (CVSroot == NULL || !cvs_update_env) { CVSroot = CVSADM_Root; cvs_update_env = 1; /* need to update environment */ } +#ifdef CLIENT_SUPPORT + else if (!getenv ("CVS_IGNORE_REMOTE_ROOT")) +#else else +#endif { /* * Now for the hard part, compare the two directories. If they * are not identical, then abort this command. */ - if ((strcmp (CVSroot, CVSADM_Root) != 0) && + if ((fncmp (CVSroot, CVSADM_Root) != 0) && !same_directories(CVSroot, CVSADM_Root)) { - error (0, 0, "%s value for CVS Root found in %s", - CVSADM_Root, CVSADM_ROOT); - if (cvs_update_env) - { - error (0, 0, "does not match command line -d %s setting", - CVSroot); - error (1, 0, - "you may wish to try the cvs command again without the -d option "); - } - else - { - error (0, 0, - "does not match CVSROOT environment value of %s", - CVSroot); - error (1, 0, - "you may wish to unsetenv CVSROOT and try again"); - } + error (0, 0, "%s value for CVS Root found in %s", + CVSADM_Root, CVSADM_ROOT); + error (0, 0, "does not match command line -d %s setting", + CVSroot); + error (1, 0, + "you may wish to try the cvs command again without the -d option "); } } } @@ -322,6 +570,9 @@ main (argc, argv) * we assume that we can't work in the repository. * BUT, only if the history file exists. */ +#ifdef SERVER_SUPPORT + if (strcmp (command_name, "server") != 0 || CVSroot != NULL) +#endif { char path[PATH_MAX]; int save_errno; @@ -330,16 +581,23 @@ main (argc, argv) error (1, 0, "You don't have a %s environment variable", CVSROOT_ENV); (void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM); - if (access (path, R_OK | X_OK)) + if (!isaccessible (path, R_OK | X_OK)) { save_errno = errno; +#ifdef CLIENT_SUPPORT + if (strchr (CVSroot, ':') == NULL) + { +#endif error (0, 0, "Sorry, you don't have sufficient access to %s", CVSroot); error (1, save_errno, "%s", path); +#ifdef CLIENT_SUPPORT + } +#endif } (void) strcat (path, "/"); (void) strcat (path, CVSROOTADM_HISTORY); - if (isfile (path) && access (path, R_OK | W_OK)) + if (isfile (path) && !isaccessible (path, R_OK | W_OK)) { save_errno = errno; error (0, 0, @@ -349,6 +607,16 @@ main (argc, argv) } } +#ifdef SERVER_SUPPORT + if (strcmp (command_name, "server") == 0) + /* This is only used for writing into the history file. Might + be nice to have hostname and/or remote path, on the other hand + I'm not sure whether it is worth the trouble. */ + strcpy (CurDir, "<remote>"); + else if (!getwd (CurDir)) + error (1, 0, "cannot get working directory: %s", CurDir); +#endif + #ifdef HAVE_PUTENV /* Now, see if we should update the environment with the Rcsbin value */ if (cvs_update_env) @@ -404,11 +672,30 @@ main (argc, argv) else { command_name = cm->fullname; /* Global pointer for later use */ + + /* make sure we clean up on error */ +#ifdef SIGHUP (void) SIG_register (SIGHUP, main_cleanup); + (void) SIG_register (SIGHUP, Lock_Cleanup); +#endif +#ifdef SIGINT (void) SIG_register (SIGINT, main_cleanup); + (void) SIG_register (SIGINT, Lock_Cleanup); +#endif +#ifdef SIGQUIT (void) SIG_register (SIGQUIT, main_cleanup); + (void) SIG_register (SIGQUIT, Lock_Cleanup); +#endif +#ifdef SIGPIPE (void) SIG_register (SIGPIPE, main_cleanup); + (void) SIG_register (SIGPIPE, Lock_Cleanup); +#endif +#ifdef SIGTERM (void) SIG_register (SIGTERM, main_cleanup); + (void) SIG_register (SIGTERM, Lock_Cleanup); +#endif + + gethostname(hostname, sizeof (hostname)); #ifdef HAVE_SETVBUF /* @@ -422,8 +709,19 @@ main (argc, argv) if (use_cvsrc) read_cvsrc(&argc, &argv); +#ifdef CLIENT_SUPPORT + /* If cvsroot contains a colon, try to do it via the protocol. */ + { + char *p = CVSroot == NULL ? NULL : strchr (CVSroot, ':'); + if (p) + err = (*(cm->client_func)) (argc, argv); + else + err = (*(cm->func)) (argc, argv); + } +#else /* No CLIENT_SUPPORT */ err = (*(cm->func)) (argc, argv); +#endif /* No CLIENT_SUPPORT */ } /* * If the command's error count is modulo 256, we need to change it @@ -462,7 +760,7 @@ Make_Date (rawdate) void usage (cpp) - register char **cpp; + register const char *const *cpp; { (void) fprintf (stderr, *cpp++, program_name, command_name); for (; *cpp; cpp++) diff --git a/gnu/usr.bin/cvs/cvs/modules.c b/gnu/usr.bin/cvs/cvs/modules.c index 384fbfd..528287a 100644 --- a/gnu/usr.bin/cvs/cvs/modules.c +++ b/gnu/usr.bin/cvs/cvs/modules.c @@ -21,10 +21,11 @@ */ #include "cvs.h" +#include "save-cwd.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)modules.c 1.62 94/09/29 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)modules.c 1.62 94/09/29 $"; +USE(rcsid); #endif struct sortrec @@ -35,7 +36,7 @@ struct sortrec char *comment; }; -static int sort_order PROTO((CONST PTR l, CONST PTR r)); +static int sort_order PROTO((const PTR l, const PTR r)); static void save_d PROTO((char *k, int ks, char *d, int ds)); @@ -83,7 +84,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, char *mname; enum mtype m_type; char *msg; - int (*callback_proc) (); + CALLBACKPROC callback_proc; char *where; int shorten; int local_specified; @@ -92,26 +93,39 @@ do_module (db, mname, m_type, msg, callback_proc, where, { char *checkin_prog = NULL; char *checkout_prog = NULL; + char *export_prog = NULL; char *tag_prog = NULL; char *update_prog = NULL; - char cwd[PATH_MAX]; + struct saved_cwd cwd; char line[MAXLINELEN]; - char *xmodargv[MAXFILEPERDIR]; + int modargc; + int xmodargc; char **modargv; + char *xmodargv[MAXFILEPERDIR]; char *value; char *zvalue; char *mwhere = NULL; char *mfile = NULL; char *spec_opt = NULL; char xvalue[PATH_MAX]; - int modargc, alias = 0; + int alias = 0; datum key, val; char *cp; int c, err = 0; +#ifdef SERVER_SUPPORT + if (trace) + { + fprintf (stderr, "%c-> do_module (%s, %s, %s, %s)\n", + (server_active) ? 'S' : ' ', + mname, msg, where ? where : "", + extra_arg ? extra_arg : ""); + } +#endif + /* remember where we start */ - if (getwd (cwd) == NULL) - error (1, 0, "cannot get current working directory: %s", cwd); + if (save_cwd (&cwd)) + exit (1); /* if this is a directory to ignore, add it to that list */ if (mname[0] == '!' && mname[1] != '\0') @@ -329,18 +343,24 @@ do_module (db, mname, m_type, msg, callback_proc, where, err++; goto out; } - if (!isfile (CVSADM) && !isfile (OCVSADM)) + if (!isfile (CVSADM)) { char nullrepos[PATH_MAX]; (void) sprintf (nullrepos, "%s/%s/%s", CVSroot, CVSROOTADM, CVSNULLREPOS); if (!isfile (nullrepos)) - (void) mkdir (nullrepos, 0777); + { + mode_t omask; + omask = umask (cvsumask); + (void) CVS_MKDIR (nullrepos, 0777); + (void) umask (omask); + } if (!isdir (nullrepos)) error (1, 0, "there is no repository %s", nullrepos); - Create_Admin (".", nullrepos, (char *) NULL, (char *) NULL); + Create_Admin (".", dir, + nullrepos, (char *) NULL, (char *) NULL); if (!noexec) { FILE *fp; @@ -348,6 +368,10 @@ do_module (db, mname, m_type, msg, callback_proc, where, fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose (fp) == EOF) error (1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (dir, nullrepos); +#endif } } out: @@ -372,7 +396,8 @@ do_module (db, mname, m_type, msg, callback_proc, where, (void) sprintf (line, "%s %s", "XXX", value); /* turn the line into an argv[] array */ - line2argv (&modargc, xmodargv, line); + line2argv (&xmodargc, xmodargv, line); + modargc = xmodargc; modargv = xmodargv; /* parse the args */ @@ -397,6 +422,9 @@ do_module (db, mname, m_type, msg, callback_proc, where, case 'o': checkout_prog = optarg; break; + case 'e': + export_prog = optarg; + break; case 't': tag_prog = optarg; break; @@ -451,8 +479,12 @@ do_module (db, mname, m_type, msg, callback_proc, where, err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten, local_specified, mname, msg); - /* clean up */ - free_names (&modargc, modargv); +#if 0 + /* FIXME: I've fixed this so that the correct arguments are called, + but now this fails because there is code below this point that + uses optarg values extracted from the arg vector. */ + free_names (&xmodargc, xmodargv); +#endif /* if there were special include args, process them now */ @@ -495,6 +527,16 @@ do_module (db, mname, m_type, msg, callback_proc, where, } /* write out the checkin/update prog files if necessary */ +#ifdef SERVER_SUPPORT + if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding) + { + if (checkin_prog != NULL) + server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN); + if (update_prog != NULL) + server_prog (where ? where : mname, update_prog, PROG_UPDATE); + } + else +#endif if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog) { FILE *fp; @@ -516,23 +558,26 @@ do_module (db, mname, m_type, msg, callback_proc, where, } /* cd back to where we started */ - if (chdir (cwd) < 0) - error (1, errno, "failed chdir to %s!", cwd); + if (restore_cwd (&cwd, NULL)) + exit (1); + free_cwd (&cwd); /* run checkout or tag prog if appropriate */ if (err == 0 && run_module_prog) { if ((m_type == TAG && tag_prog != NULL) || - (m_type == CHECKOUT && checkout_prog != NULL)) + (m_type == CHECKOUT && checkout_prog != NULL) || + (m_type == EXPORT && export_prog != NULL)) { /* - * If a relative pathname is specified as the checkout or - * tag proc, try to tack on the current "where" value. + * If a relative pathname is specified as the checkout, tag + * or export proc, try to tack on the current "where" value. * if we can't find a matching program, just punt and use * whatever is specified in the modules file. */ char real_prog[PATH_MAX]; - char *prog = (m_type == TAG ? tag_prog : checkout_prog); + char *prog = (m_type == TAG ? tag_prog : + (m_type == CHECKOUT ? checkout_prog : export_prog)); char *real_where = (where != NULL ? where : mwhere); if ((*prog != '/') && (*prog != '.')) @@ -594,7 +639,10 @@ static struct sortrec *s_head; static int s_max = 0; /* Number of elements allocated */ static int s_count = 0; /* Number of elements used */ -static int Status; +static int Status; /* Nonzero if the user is + interested in status + information as well as + module name */ static char def_status[] = "NONE"; /* Sort routine for qsort: @@ -604,12 +652,12 @@ static char def_status[] = "NONE"; */ static int sort_order (l, r) - CONST PTR l; - CONST PTR r; + const PTR l; + const PTR r; { int i; - CONST struct sortrec *left = (CONST struct sortrec *) l; - CONST struct sortrec *right = (CONST struct sortrec *) r; + const struct sortrec *left = (const struct sortrec *) l; + const struct sortrec *right = (const struct sortrec *) r; if (Status) { @@ -700,6 +748,9 @@ save_d (k, ks, d, ds) s_count++; } +/* Print out the module database as we know it. If STATUS is + non-zero, print out status information for each module. */ + void cat_module (status) int status; @@ -827,5 +878,7 @@ cat_module (status) *cp++ = '\0'; (void) printf ("%s\n", cp2); } + + free_names(&moduleargc, moduleargv); } } diff --git a/gnu/usr.bin/cvs/cvs/no_diff.c b/gnu/usr.bin/cvs/cvs/no_diff.c index ec3e641..281d348 100644 --- a/gnu/usr.bin/cvs/cvs/no_diff.c +++ b/gnu/usr.bin/cvs/cvs/no_diff.c @@ -17,8 +17,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)no_diff.c 1.39 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)no_diff.c 1.39 94/10/07 $"; +USE(rcsid); #endif int @@ -34,6 +34,7 @@ No_Difference (file, vers, entries, repository, update_dir) int ret; char *ts, *options; int retcode = 0; + char *tocvsPath; if (!vers->srcfile || !vers->srcfile->path) return (-1); /* different since we couldn't tell */ @@ -48,20 +49,39 @@ No_Difference (file, vers, entries, repository, update_dir) run_arg (vers->srcfile->path); if ((retcode = run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY)) == 0) { +#if 0 + /* Why would we want to munge the modes? And only if the timestamps + are different? And even for commands like "cvs status"???? */ if (!iswritable (file)) /* fix the modes as a side effect */ xchmod (file, 1); +#endif + + tocvsPath = wrap_tocvs_process_file (file); /* do the byte by byte compare */ - if (xcmp (file, tmp) == 0) + if (xcmp (tocvsPath == NULL ? file : tocvsPath, tmp) == 0) { +#if 0 + /* Why would we want to munge the modes? And only if the + timestamps are different? And even for commands like + "cvs status"???? */ if (cvswrite == FALSE) /* fix the modes as a side effect */ xchmod (file, 0); +#endif /* no difference was found, so fix the entries file */ ts = time_stamp (file); Register (entries, file, vers->vn_user ? vers->vn_user : vers->vn_rcs, ts, options, vers->tag, vers->date, (char *) 0); +#ifdef SERVER_SUPPORT + if (server_active) + { + /* We need to update the entries line on the client side. */ + server_update_entries + (file, update_dir, repository, SERVER_UPDATED); + } +#endif free (ts); /* update the entdata pointer in the vers_ts structure */ @@ -72,10 +92,25 @@ No_Difference (file, vers, entries, repository, update_dir) } else ret = 1; /* files were really different */ + if (tocvsPath) + { + /* Need to call unlink myself because the noexec variable + * has been set to 1. */ + if (trace) + (void) fprintf (stderr, "%c-> unlink (%s)\n", +#ifdef SERVER_SUPPORT + (server_active) ? 'S' : ' ', +#else + ' ', +#endif + tocvsPath); + if (unlink (tocvsPath) < 0) + error (0, errno, "could not remove %s", tocvsPath); + } } else { - if (update_dir[0] == '\0') + if (update_dir[0] == '\0') error (0, retcode == -1 ? errno : 0, "could not check out revision %s of %s", vers->vn_user, file); @@ -87,7 +122,12 @@ No_Difference (file, vers, entries, repository, update_dir) } if (trace) - (void) fprintf (stderr, "-> unlink(%s)\n", tmp); +#ifdef SERVER_SUPPORT + (void) fprintf (stderr, "%c-> unlink2 (%s)\n", + (server_active) ? 'S' : ' ', tmp); +#else + (void) fprintf (stderr, "-> unlink (%s)\n", tmp); +#endif if (unlink (tmp) < 0) error (0, errno, "could not remove %s", tmp); free (options); diff --git a/gnu/usr.bin/cvs/cvs/options.h b/gnu/usr.bin/cvs/cvs/options.h index ad07c14..8af48de 100644 --- a/gnu/usr.bin/cvs/cvs/options.h +++ b/gnu/usr.bin/cvs/cvs/options.h @@ -53,26 +53,30 @@ /* * The "diff" program to execute when creating patch output. This "diff" - * must support the "-c" option for context diffing. Specify a full pathname - * if your site wants to use a particular diff. If you are using the GNU - * version of diff (version 1.15 or later), this should be "diff -a". + * must support the "-c" option for context diffing. Specify a full + * pathname if your site wants to use a particular diff. If you are + * using the GNU version of diff (version 1.15 or later), this should + * be "diff -a". * - * NOTE: this program is only used for the ``patch'' sub-command. The other - * commands use rcsdiff which will use whatever version of diff was specified + * NOTE: this program is only used for the ``patch'' sub-command (and + * for ``update'' if you are using the server). The other commands + * use rcsdiff which will use whatever version of diff was specified * when rcsdiff was built on your system. */ + #ifndef DIFF -#define DIFF "diff" +#define DIFF "diff -a" #endif /* * The "grep" program to execute when checking to see if a merged file had - * any conflicts. This "grep" must support the "-s" option and a standard + * any conflicts. This "grep" must support a standard basic * regular expression as an argument. Specify a full pathname if your site * wants to use a particular grep. */ + #ifndef GREP -#define GREP "grep" +#define GREP "grep" #endif /* @@ -93,6 +97,15 @@ #endif /* + * The "patch" program to run when using the CVS server and accepting + * patches across the network. Specify a full pathname if your site + * wants to use a particular patch. + */ +#ifndef PATCH_PROGRAM +#define PATCH_PROGRAM "patch" +#endif + +/* * By default, RCS programs are executed with the shell or through execlp(), * so the user's PATH environment variable is searched. If you'd like to * bind all RCS programs to a certain directory (perhaps one not in most @@ -120,6 +133,30 @@ #endif /* + * The default umask to use when creating or otherwise setting file or + * directory permissions in the repository. Must be a value in the + * range of 0 through 0777. For example, a value of 002 allows group + * rwx access and world rx access; a value of 007 allows group rwx + * access but no world access. This value is overridden by the value + * of the CVSUMASK environment variable, which is interpreted as an + * octal number. + */ +#ifndef UMASK_DFLT +#define UMASK_DFLT 002 +#endif + +/* + * The cvs admin command is restricted to the members of the group + * CVS_ADMIN_GROUP. If this group does not exist, all users are + * allowed to run cvs admin. To disable the cvs admin for all users, + * create an empty group CVS_ADMIN_GROUP. To disable access control for + * cvs admin, comment out the define below. + */ +#ifndef CVS_ADMIN_GROUP +#define CVS_ADMIN_GROUP "cvsadmin" +#endif + +/* * The Repository file holds the path to the directory within the source * repository that contains the RCS ,v files for each CVS working directory. * This path is either a full-path or a path relative to CVSROOT. @@ -175,6 +212,19 @@ #endif /* + * The "cvs admin" command allows people to get around most of the logging + * and info procedures within CVS. For exmaple, "cvs tag tagname filename" + * will perform some validity checks on the tag, while "cvs admin -Ntagname" + * will not perform those checks. For this reason, some sites may wish to + * disable the admin function completely. + * + * To disable the admin function, uncomment the lines below. + */ +#ifndef CVS_NOADMIN +/* #define CVS_NOADMIN */ +#endif + +/* * The "cvs diff" command accepts all the single-character options that GNU * diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put * cpp-style #define's around the output differences. CVS, by default, uses @@ -186,6 +236,40 @@ #define CVS_DIFFDATE #endif +/* + * define this to enable the SETXID support (see FAQ 4D.13) + */ +#ifndef SETXID_SUPPORT +/* #define SETXID_SUPPORT */ +#endif + +/* + * The authenticated client/server is under construction. Don't + * define either of these unless you're testing them, in which case + * you're me and you already know that. + */ +#undef AUTH_CLIENT_SUPPORT +#undef AUTH_SERVER_SUPPORT + +/* + * If you are working with a large remote repository and a 'cvs checkout' is + * swamping your network and memory, define these to enable flow control. + * You will end up with even less guarantees of a consistant checkout, + * but that may be better than no checkout at all. The master server process + * will monitor how far it is getting behind, if it reaches the high water + * mark, it will signal the child process to stop generating data when + * convenient (ie: no locks are held, currently at the beginning of a + * new directory). Once the buffer has drained sufficiently to reach the + * low water mark, it will be signalled to start again. + * -- EXPERIMENTAL! -- A better solution may be in the works. + * You may override the default hi/low watermarks here too. + */ +#ifndef SERVER_FLOWCONTROL +/* #define SERVER_FLOWCONTROL */ +/* #define SERVER_HI_WATER (2 * 1024 * 1024) */ +/* #define SERVER_LO_WATER (1 * 1024 * 1024) */ +#endif + /* End of CVS configuration section */ /* diff --git a/gnu/usr.bin/cvs/cvs/parseinfo.c b/gnu/usr.bin/cvs/cvs/parseinfo.c index 17cf1da..6d59884 100644 --- a/gnu/usr.bin/cvs/cvs/parseinfo.c +++ b/gnu/usr.bin/cvs/cvs/parseinfo.c @@ -9,20 +9,22 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)parseinfo.c 1.18 94/09/23 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)parseinfo.c 1.18 94/09/23 $"; +USE(rcsid); #endif /* * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for - * each line in the file that matches the REPOSITORY. + * the first line in the file that matches the REPOSITORY, or if ALL != 0, any lines + * matching "ALL", or if no lines match, the last line matching "DEFAULT". + * * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure. */ int Parse_Info (infofile, repository, callproc, all) char *infofile; char *repository; - int (*callproc) (); + CALLPROC callproc; int all; { int err = 0; @@ -30,9 +32,10 @@ Parse_Info (infofile, repository, callproc, all) char infopath[PATH_MAX]; char line[MAXLINELEN]; char *default_value = NULL; + char *expanded_value= NULL; int callback_done, line_number; char *cp, *exp, *value, *srepos; - CONST char *regex_err; + const char *regex_err; if (CVSroot == NULL) { @@ -50,6 +53,10 @@ Parse_Info (infofile, repository, callproc, all) /* strip off the CVSROOT if repository was absolute */ srepos = Short_Repository (repository); + if (trace) + (void) fprintf (stderr, "-> ParseInfo(%s, %s, %s)\n", + infopath, srepos, all ? "ALL" : "not ALL"); + /* search the info file for lines that match */ callback_done = line_number = 0; while (fgets (line, sizeof (line), fp_info) != NULL) @@ -91,6 +98,16 @@ Parse_Info (infofile, repository, callproc, all) if ((cp = strrchr (value, '\n')) != NULL) *cp = '\0'; + expanded_value = expand_path (value); + if (!expanded_value) + { + error (0, 0, + "Invalid environmental variable at line %d in file %s", + line_number, infofile); + continue; + + } + /* * At this point, exp points to the regular expression, and value * points to the value to call the callback routine with. Evaluate @@ -101,25 +118,29 @@ Parse_Info (infofile, repository, callproc, all) /* save the default value so we have it later if we need it */ if (strcmp (exp, "DEFAULT") == 0) { - default_value = xstrdup (value); + default_value = xstrdup (expanded_value); continue; } /* * For a regular expression of "ALL", do the callback always We may - * execute lots of ALL callbacks in addition to one regular matching + * execute lots of ALL callbacks in addition to *one* regular matching * callback or default */ if (strcmp (exp, "ALL") == 0) { if (all) - err += callproc (repository, value); + err += callproc (repository, expanded_value); else error(0, 0, "Keyword `ALL' is ignored at line %d in %s file", line_number, infofile); continue; } + if (callback_done) + /* only first matching, plus "ALL"'s */ + continue; + /* see if the repository matched this regular expression */ if ((regex_err = re_comp (exp)) != NULL) { @@ -128,10 +149,10 @@ Parse_Info (infofile, repository, callproc, all) continue; } if (re_exec (srepos) == 0) - continue; /* no match */ + continue; /* no match */ /* it did, so do the callback and note that we did one */ - err += callproc (repository, value); + err += callproc (repository, expanded_value); callback_done = 1; } (void) fclose (fp_info); @@ -143,6 +164,8 @@ Parse_Info (infofile, repository, callproc, all) /* free up space if necessary */ if (default_value != NULL) free (default_value); + if (expanded_value != NULL) + free (expanded_value); return (err); } diff --git a/gnu/usr.bin/cvs/cvs/patch.c b/gnu/usr.bin/cvs/cvs/patch.c index 69e5b09..560f4b4 100644 --- a/gnu/usr.bin/cvs/cvs/patch.c +++ b/gnu/usr.bin/cvs/cvs/patch.c @@ -13,17 +13,18 @@ */ #include "cvs.h" +#include "getline.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)patch.c 1.57 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)patch.c 1.57 94/09/30 $"; +USE(rcsid); #endif static RETSIGTYPE patch_cleanup PROTO((void)); static Dtype patch_dirproc PROTO((char *dir, char *repos, char *update_dir)); static int patch_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); -static int patch_proc PROTO((int *pargc, char *argv[], char *xwhere, +static int patch_proc PROTO((int *pargc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local_specified, char *mname, char *msg)); @@ -39,11 +40,10 @@ static char *date2 = NULL; static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1]; static int unidiff = 0; -static char *patch_usage[] = +static const char *const patch_usage[] = { - "Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n", + "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n", " -r rev|-D date [-r rev2 | -D date2] modules...\n", - "\t-Q\tReally quiet.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", "\t-c\tContext diffs (default)\n", @@ -59,7 +59,7 @@ static char *patch_usage[] = int patch (argc, argv) int argc; - char *argv[]; + char **argv; { register int i; int c; @@ -75,10 +75,15 @@ patch (argc, argv) switch (c) { case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'f': force_tag_match = 0; @@ -162,12 +167,64 @@ patch (argc, argv) if (options == NULL) options = xstrdup (""); +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (force_tag_match) + send_arg("-f"); + if (toptwo_diffs) + send_arg("-t"); + if (patch_short) + send_arg("-s"); + if (unidiff) + send_arg("-u"); + + if (rev1) + option_with_arg ("-r", rev1); + if (date1) + client_senddate (date1); + if (rev2) + option_with_arg ("-r", rev2); + if (date2) + client_senddate (date2); + if (options[0] != '\0') + send_arg (options); + + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + } + + if (fprintf (to_server, "rdiff\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + /* clean up if we get a signal */ +#ifdef SIGHUP (void) SIG_register (SIGHUP, patch_cleanup); +#endif +#ifdef SIGINT (void) SIG_register (SIGINT, patch_cleanup); +#endif +#ifdef SIGQUIT (void) SIG_register (SIGQUIT, patch_cleanup); +#endif +#ifdef SIGPIPE (void) SIG_register (SIGPIPE, patch_cleanup); +#endif +#ifdef SIGTERM (void) SIG_register (SIGTERM, patch_cleanup); +#endif db = open_module (); for (i = 0; i < argc; i++) @@ -188,7 +245,7 @@ static int patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, mname, msg) int *pargc; - char *argv[]; + char **argv; char *xwhere; char *mwhere; char *mfile; @@ -255,8 +312,8 @@ patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, which = W_REPOS; /* start the recursion processor */ - err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc, - (int (*) ()) NULL, *pargc - 1, argv + 1, local, + err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc, + (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local, which, 0, 1, where, 1, 1); return (err); @@ -286,7 +343,9 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) int isattic = 0; int retcode = 0; char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX]; - char line1[MAXLINELEN], line2[MAXLINELEN]; + char *line1, *line2; + size_t line1_chars_allocated; + size_t line2_chars_allocated; char *cp1, *cp2, *commap; FILE *fp; @@ -304,7 +363,7 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) if (isattic && rev2 == NULL && date2 == NULL) vers_head = NULL; else - vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match); + vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 0); if (toptwo_diffs) { @@ -322,7 +381,7 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) return (1); } } - vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match); + vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, 0); if (vers_tag == NULL && (vers_head == NULL || isattic)) return (0); /* nothing known about specified revs */ @@ -336,8 +395,21 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) if (vers_tag == NULL) (void) printf ("%s is new; current revision %s\n", rcs, vers_head); else if (vers_head == NULL) +#ifdef DEATH_SUPPORT + { + (void) printf ("%s is removed; not included in ", rcs); + if (rev2 != NULL) + (void) printf ("release tag %s", rev2); + else if (date2 != NULL) + (void) printf ("release date %s", date2); + else + (void) printf ("current release"); + (void) printf ("\n"); + } +#else (void) printf ("%s is removed; not included in release %s\n", rcs, rev2 ? rev2 : date2); +#endif else (void) printf ("%s changed from revision %s to %s\n", rcs, vers_tag, vers_head); @@ -396,6 +468,12 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c'); run_arg (tmpfile1); run_arg (tmpfile2); + + line1 = NULL; + line1_chars_allocated = 0; + line2 = NULL; + line2_chars_allocated = 0; + switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL)) { case -1: /* fork/wait failure */ @@ -419,8 +497,8 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) (void) fflush (stdout); fp = open_file (tmpfile3, "r"); - if (fgets (line1, sizeof (line1), fp) == NULL || - fgets (line2, sizeof (line2), fp) == NULL) + if (getline (&line1, &line1_chars_allocated, fp) < 0 || + getline (&line2, &line2_chars_allocated, fp) < 0) { error (0, errno, "failed to read diff file header %s for %s", tmpfile3, rcs); @@ -488,14 +566,19 @@ patch_fileproc (file, update_dir, repository, entries, srcfiles) if (update_dir[0] != '\0') (void) printf ("%s/", update_dir); (void) printf ("%s%s", rcs, cp2); - while (fgets (line1, sizeof (line1), fp) != NULL) - (void) printf ("%s", line1); + /* spew the rest of the diff out */ + while (getline (&line1, &line1_chars_allocated, fp) >= 0) + (void) fputs (line1, stdout); (void) fclose (fp); break; default: error (0, 0, "diff failed for %s", rcs); } out: + if (line1) + free (line1); + if (line2) + free (line2); (void) unlink_file (tmpfile1); (void) unlink_file (tmpfile2); (void) unlink_file (tmpfile3); diff --git a/gnu/usr.bin/cvs/cvs/patchlevel.h b/gnu/usr.bin/cvs/cvs/patchlevel.h index dc2214d..50d3863 100644 --- a/gnu/usr.bin/cvs/cvs/patchlevel.h +++ b/gnu/usr.bin/cvs/cvs/patchlevel.h @@ -1 +1 @@ -#define PATCHLEVEL 0 +#define PATCHLEVEL 2 diff --git a/gnu/usr.bin/cvs/cvs/rcs.c b/gnu/usr.bin/cvs/cvs/rcs.c index 2c424d9..43282a6 100644 --- a/gnu/usr.bin/cvs/cvs/rcs.c +++ b/gnu/usr.bin/cvs/cvs/rcs.c @@ -8,14 +8,15 @@ * manipulation */ +#include <assert.h> #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)rcs.c 1.40 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)rcs.c 1.40 94/10/07 $"; +USE(rcsid); #endif -static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, char *rcsfile)); +static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile)); static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch)); static int getrcskey PROTO((FILE * fp, char **keyp, char **valp)); static int parse_rcs_proc PROTO((Node * file, void *closure)); @@ -56,7 +57,7 @@ static const char spacetab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */ }; -#define whitespace(c) (spacetab[c] != 0) +#define whitespace(c) (spacetab[(unsigned char)c] != 0) /* * Parse all the rcs files specified and return a list @@ -108,7 +109,7 @@ parse_rcs_proc (file, closure) void RCS_addnode (file, rcs, list) - char *file; + const char *file; RCSNode *rcs; List *list; { @@ -128,13 +129,19 @@ RCS_addnode (file, rcs, list) */ RCSNode * RCS_parse (file, repos) - char *file; - char *repos; + const char *file; + const char *repos; { RCSNode *rcs; FILE *fp; char rcsfile[PATH_MAX]; +#ifdef LINES_CRLF_TERMINATED + /* Some ports of RCS to Windows NT write RCS files with newline- + delimited lines. We would need to pass fopen a "binary" flag. */ + abort (); +#endif + (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT); if ((fp = fopen (rcsfile, "r")) != NULL) { @@ -145,6 +152,17 @@ RCS_parse (file, repos) fclose (fp); return (rcs); } + else if (! existence_error (errno)) + { + error (0, errno, "cannot open %s", rcsfile); + return NULL; + } + +#ifdef LINES_CRLF_TERMINATED + /* Some ports of RCS to Windows NT write RCS files with newline- + delimited lines. We would need to pass fopen a "binary" flag. */ + abort (); +#endif (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT); if ((fp = fopen (rcsfile, "r")) != NULL) @@ -159,6 +177,11 @@ RCS_parse (file, repos) fclose (fp); return (rcs); } + else if (! existence_error (errno)) + { + error (0, errno, "cannot open %s", rcsfile); + return NULL; + } return (NULL); } @@ -173,6 +196,12 @@ RCS_parsercsfile (rcsfile) FILE *fp; RCSNode *rcs; +#ifdef LINES_CRLF_TERMINATED + /* Some ports of RCS to Windows NT write RCS files with newline- + delimited lines. We would need to pass fopen a "binary" flag. */ + abort (); +#endif + /* open the rcsfile */ if ((fp = fopen (rcsfile, "r")) == NULL) { @@ -186,19 +215,15 @@ RCS_parsercsfile (rcsfile) return (rcs); } + /* - * Do the real work of parsing an RCS file - */ + */ static RCSNode * RCS_parsercsfile_i (fp, rcsfile) FILE *fp; - char *rcsfile; + const char *rcsfile; { - Node *q, *r; RCSNode *rdata; - RCSVers *vnode; - int n; - char *cp; char *key, *value; /* make a node */ @@ -206,6 +231,87 @@ RCS_parsercsfile_i (fp, rcsfile) memset ((char *) rdata, 0, sizeof (RCSNode)); rdata->refcount = 1; rdata->path = xstrdup (rcsfile); + + /* Process HEAD and BRANCH keywords from the RCS header. + * + * Most cvs operatations on the main branch don't need any more + * information. Those that do call XXX to completely parse the + * RCS file. */ + + if (getrcskey (fp, &key, &value) == -1 || key == NULL) + goto l_error; + + if (strcmp (RCSHEAD, key) == 0 && value != NULL) + rdata->head = xstrdup (value); + + if (getrcskey (fp, &key, &value) == -1 || key == NULL) + goto l_error; + + if (strcmp (RCSBRANCH, key) == 0 && value != NULL) + { + char *cp; + + rdata->branch = xstrdup (value); + if ((numdots (rdata->branch) & 1) != 0) + { + /* turn it into a branch if it's a revision */ + cp = strrchr (rdata->branch, '.'); + *cp = '\0'; + } + } + + rdata->flags |= PARTIAL; + return rdata; + +l_error: + if (!really_quiet) + { + if (ferror(fp)) + { + error (1, 0, "error reading `%s'", rcsfile); + } + else + { + error (0, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); + } + } + freercsnode (&rdata); + return (NULL); +} + + +/* + * Do the real work of parsing an RCS file + * + * There are no allowances for error here. + */ +void +RCS_reparsercsfile (rdata) + RCSNode *rdata; +{ + FILE *fp; + char *rcsfile; + + Node *q, *r; + RCSVers *vnode; + int n; + char *cp; + char *key, *value; + + rcsfile = rdata->path; + +#ifdef LINES_CRLF_TERMINATED + /* Some ports of RCS to Windows NT write RCS files with newline- + delimited lines. We would need to pass fopen a "binary" flag. */ + abort (); +#endif + + fp = fopen(rcsfile, "r"); + if (fp == NULL) + error (1, 0, "unable to reopen `%s'", rcsfile); + + /* make a node */ rdata->versions = getlist (); rdata->dates = getlist (); @@ -221,40 +327,17 @@ RCS_parsercsfile_i (fp, rcsfile) or we had trouble reading the file. */ if (getrcskey (fp, &key, &value) == -1 || key == NULL) { - - if (!really_quiet) + if (ferror(fp)) { - if (ferror(fp)) - { - error (1, 0, "error reading `%s'", rcsfile); - } - else - { - error (0, 0, "`%s' does not appear to be a valid rcs file", - rcsfile); - } + error (1, 0, "error reading `%s'", rcsfile); } - freercsnode (&rdata); - return (NULL); - } - - /* process it */ - if (strcmp (RCSHEAD, key) == 0 && value != NULL) - { - rdata->head = xstrdup (value); - continue; - } - if (strcmp (RCSBRANCH, key) == 0 && value != NULL) - { - rdata->branch = xstrdup (value); - if ((numdots (rdata->branch) & 1) != 0) + else { - /* turn it into a branch if it's a revision */ - cp = strrchr (rdata->branch, '.'); - *cp = '\0'; + error (1, 0, "`%s' does not appear to be a valid rcs file", + rcsfile); } - continue; } + if (strcmp (RCSSYMBOLS, key) == 0) { if (value != NULL) @@ -264,6 +347,12 @@ RCS_parsercsfile_i (fp, rcsfile) } } + if (strcmp (RCSEXPAND, key) == 0) + { + rdata->expand = xstrdup (value); + continue; + } + /* * check key for '.''s and digits (probably a rev) if it is a * revision, we are done with the headers and are down to the @@ -312,6 +401,17 @@ RCS_parsercsfile_i (fp, rcsfile) /* throw away the state field */ (void) getrcskey (fp, &key, &value); +#ifdef DEATH_SUPPORT + /* Accept this regardless of DEATH_STATE, so that we can read + repositories created with different versions of CVS. */ + if (strcmp (key, "state") != 0) + error (1, 0, "\ +unable to parse rcs file; `state' not in the expected place"); + if (strcmp (value, "dead") == 0) + { + vnode->dead = 1; + } +#endif /* fill in the date field */ r->key = vnode->date = xstrdup (date); @@ -335,6 +435,15 @@ RCS_parsercsfile_i (fp, rcsfile) */ while ((n = getrcskey (fp, &key, &value)) >= 0) { +#ifdef DEATH_SUPPORT + /* Enable use of repositories created with a CVS which defines + DEATH_SUPPORT and not DEATH_STATE. */ + if (strcmp(key, RCSDEAD) == 0) + { + vnode->dead = 1; + continue; + } +#endif /* if we have a revision, break and do it */ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++) /* do nothing */ ; @@ -354,7 +463,8 @@ RCS_parsercsfile_i (fp, rcsfile) break; } - return (rdata); + fclose (fp); + rdata->flags &= ~PARTIAL; } /* @@ -390,6 +500,8 @@ freercsnode (rnodep) dellist (&(*rnodep)->symbols); if ((*rnodep)->symbols_data != (char *) NULL) free ((*rnodep)->symbols_data); + if ((*rnodep)->expand != NULL) + free ((*rnodep)->expand); if ((*rnodep)->head != (char *) NULL) free ((*rnodep)->head); if ((*rnodep)->branch != (char *) NULL) @@ -435,17 +547,17 @@ null_delproc (p) * space or semicolon * o if key == "desc" then key and data are NULL and return -1 * o if key wasn't terminated by a semicolon, skip white space and fill - * in value with everything up to a semicolon o compress all whitespace - * down to a single space + * in value with everything up to a semicolon + * o compress all whitespace down to a single space * o if a word starts with @, do funky rcs processing * o strip whitespace off end of value or set value to NULL if it empty * o return 0 since we found something besides "desc" */ static char *key = NULL; -static int keysize = 0; static char *value = NULL; -static int valsize = 0; +static size_t keysize = 0; +static size_t valsize = 0; #define ALLOCINCR 1024 @@ -457,11 +569,9 @@ getrcskey (fp, keyp, valp) { char *cur, *max; int c; - int funky = 0; - int white = 1; /* skip leading whitespace */ - while (1) + do { c = getc (fp); if (c == EOF) @@ -470,25 +580,22 @@ getrcskey (fp, keyp, valp) *valp = (char *) NULL; return (-1); } - if (!whitespace (c)) - break; - } + } while (whitespace (c)); /* fill in key */ cur = key; max = key + keysize; while (!whitespace (c) && c != ';') { - if (cur < max) - *cur++ = c; - else + if (cur >= max) { key = xrealloc (key, keysize + ALLOCINCR); cur = key + keysize; keysize += ALLOCINCR; max = key + keysize; - *cur++ = c; } + *cur++ = c; + c = getc (fp); if (c == EOF) { @@ -504,7 +611,6 @@ getrcskey (fp, keyp, valp) keysize += ALLOCINCR; max = key + keysize; } - *cur = '\0'; /* if we got "desc", we are done with the file */ @@ -515,6 +621,18 @@ getrcskey (fp, keyp, valp) return (-1); } + /* skip whitespace between key and val */ + while (whitespace (c)) + { + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + } + /* if we ended key with a semicolon, there is no value */ if (c == ';') { @@ -524,33 +642,25 @@ getrcskey (fp, keyp, valp) } /* otherwise, there might be a value, so fill it in */ - (void) ungetc (c, fp); cur = value; max = value + valsize; /* process the value */ for (;;) { - /* get a character */ - c = getc (fp); - if (c == EOF) - { - *keyp = (char *) NULL; - *valp = (char *) NULL; - return (-1); - } - - /* if we are in funky mode, do the rest of this string */ - if (funky) + /* handle RCS "strings" */ + if (c == '@') { - - /* - * funky mode processing does the following: o @@ means one @ o - * all other characters are literal up to a single @ (including - * ';') - */ for (;;) { + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + if (c == '@') { c = getc (fp); @@ -560,16 +670,11 @@ getrcskey (fp, keyp, valp) *valp = (char *) NULL; return (-1); } + if (c != '@') - { - /* @ followed by non @ turns off funky mode */ - funky = 0; break; - } - /* otherwise, we already ate one @ so copy the other one */ } - /* put the character on the value (maybe allocating space) */ if (cur >= max) { value = xrealloc (value, valsize + ALLOCINCR); @@ -578,6 +683,13 @@ getrcskey (fp, keyp, valp) max = value + valsize; } *cur++ = c; + } + } + + /* compress whitespace down to a single space */ + if (whitespace (c)) + { + do { c = getc (fp); if (c == EOF) { @@ -585,45 +697,8 @@ getrcskey (fp, keyp, valp) *valp = (char *) NULL; return (-1); } - } - } + } while (whitespace (c)); - /* if we got the semi-colon we are done with the entire value */ - if (c == ';') - break; - - /* process the character we got */ - if (white && c == '@') - { - - /* - * if we are starting a word with an '@', enable funky processing - */ - white = 0; /* you can't be funky and white :-) */ - funky = 1; - } - else - { - - /* - * we put the character on the list, compressing all whitespace - * to a single space - */ - - /* whitespace with white set means compress it out */ - if (white && whitespace (c)) - continue; - - if (whitespace (c)) - { - /* make c a space and set white */ - white = 1; - c = ' '; - } - else - white = 0; - - /* put the char on the end of value (maybe allocating space) */ if (cur >= max) { value = xrealloc (value, valsize + ALLOCINCR); @@ -631,17 +706,13 @@ getrcskey (fp, keyp, valp) valsize += ALLOCINCR; max = value + valsize; } - *cur++ = c; + *cur++ = ' '; } - } - /* if the last char was white space, take it off */ - if (white && cur != value) - cur--; + /* if we got a semi-colon we are done with the entire value */ + if (c == ';') + break; - /* terminate the string */ - if (cur) - { if (cur >= max) { value = xrealloc (value, valsize + ALLOCINCR); @@ -649,8 +720,26 @@ getrcskey (fp, keyp, valp) valsize += ALLOCINCR; max = value + valsize; } - *cur = '\0'; + *cur++ = c; + + c = getc (fp); + if (c == EOF) + { + *keyp = (char *) NULL; + *valp = (char *) NULL; + return (-1); + } + } + + /* terminate the string */ + if (cur >= max) + { + value = xrealloc (value, valsize + ALLOCINCR); + cur = value + valsize; + valsize += ALLOCINCR; + max = value + valsize; } + *cur = '\0'; /* if the string is empty, make it null */ if (value && *value != '\0') @@ -746,15 +835,15 @@ do_branches (list, val) * The result is returned; null-string if error. */ char * -RCS_getversion (rcs, tag, date, force_tag_match) +RCS_getversion (rcs, tag, date, force_tag_match, return_both) RCSNode *rcs; char *tag; char *date; int force_tag_match; + int return_both; { /* make sure we have something to look at... */ - if (rcs == NULL) - return ((char *) NULL); + assert (rcs != NULL); if (tag && date) { @@ -764,7 +853,7 @@ RCS_getversion (rcs, tag, date, force_tag_match) * first lookup the tag; if that works, turn the revision into * a branch and lookup the date. */ - tagrev = RCS_gettag (rcs, tag, force_tag_match); + tagrev = RCS_gettag (rcs, tag, force_tag_match, 0); if (tagrev == NULL) return ((char *) NULL); @@ -775,7 +864,7 @@ RCS_getversion (rcs, tag, date, force_tag_match) return (rev); } else if (tag) - return (RCS_gettag (rcs, tag, force_tag_match)); + return (RCS_gettag (rcs, tag, force_tag_match, return_both)); else if (date) return (RCS_getdate (rcs, date, force_tag_match)); else @@ -792,22 +881,29 @@ RCS_getversion (rcs, tag, date, force_tag_match) * If the matched tag is a branch tag, find the head of the branch. */ char * -RCS_gettag (rcs, tag, force_tag_match) +RCS_gettag (rcs, symtag, force_tag_match, return_both) RCSNode *rcs; - char *tag; + char *symtag; int force_tag_match; + int return_both; { Node *p; + char *tag = symtag; /* make sure we have something to look at... */ - if (rcs == NULL) - return ((char *) NULL); + assert (rcs != NULL); + + /* XXX this is probably not necessary, --jtc */ + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); /* If tag is "HEAD", special case to get head RCS revision */ if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0')) +#if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */ if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC)) return ((char *) NULL); /* head request for removed file */ else +#endif return (RCS_head (rcs)); if (!isdigit (tag[0])) @@ -895,7 +991,25 @@ RCS_gettag (rcs, tag, force_tag_match) else p = findnode (rcs->versions, tag); if (p != NULL) - return (xstrdup (tag)); + { + /* + * we have found a numeric revision for the revision tag. + * To support expanding the RCS keyword Name, return both + * the numeric tag and the supplied tag (which might be + * symbolic). They are separated with a ':' which is not + * a valid tag char. The variable return_both is only set + * if this function is called through Version_TS -> + * RCS_getversion. + */ + if (return_both) + { + char *both = xmalloc(strlen(tag) + 2 + strlen(symtag)); + sprintf(both, "%s:%s", tag, symtag); + return both; + } + else + return (xstrdup (tag)); + } else { /* The revision wasn't there, so return the head or NULL */ @@ -1124,8 +1238,10 @@ RCS_getbranch (rcs, tag, force_tag_match) char *cp; /* make sure we have something to look at... */ - if (rcs == NULL) - return ((char *) NULL); + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); /* find out if the tag contains a dot, or is on the trunk */ cp = strrchr (tag, '.'); @@ -1233,16 +1349,14 @@ RCS_head (rcs) RCSNode *rcs; { /* make sure we have something to look at... */ - if (rcs == NULL) - return ((char *) NULL); - - if (rcs->branch) - return (RCS_getbranch (rcs, rcs->branch, 1)); + assert (rcs != NULL); /* * NOTE: we call getbranch with force_tag_match set to avoid any * possibility of recursion */ + if (rcs->branch) + return (RCS_getbranch (rcs, rcs->branch, 1)); else return (xstrdup (rcs->head)); } @@ -1263,8 +1377,10 @@ RCS_getdate (rcs, date, force_tag_match) RCSVers *vers = NULL; /* make sure we have something to look at... */ - if (rcs == NULL) - return ((char *) NULL); + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); /* if the head is on a branch, try the branch first */ if (rcs->branch != NULL) @@ -1347,6 +1463,12 @@ RCS_getdatebranch (rcs, date, branch) return (NULL); } *cp = '\0'; /* turn it into a revision */ + + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); + p = findnode (rcs->versions, xrev); free (xrev); if (p == NULL) @@ -1427,8 +1549,10 @@ RCS_getrevtime (rcs, rev, date, fudge) RCSVers *vers; /* make sure we have something to look at... */ - if (rcs == NULL) - return (revdate); + assert (rcs != NULL); + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); /* look up the revision */ p = findnode (rcs->versions, rev); @@ -1481,14 +1605,19 @@ List * RCS_symbols(rcs) RCSNode *rcs; { - if (rcs->symbols_data) { - rcs->symbols = getlist (); - do_symbols (rcs->symbols, rcs->symbols_data); - free(rcs->symbols_data); - rcs->symbols_data = NULL; - } + assert(rcs != NULL); - return rcs->symbols; + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); + + if (rcs->symbols_data) { + rcs->symbols = getlist (); + do_symbols (rcs->symbols, rcs->symbols_data); + free(rcs->symbols_data); + rcs->symbols_data = NULL; + } + + return rcs->symbols; } /* @@ -1498,12 +1627,24 @@ RCS_symbols(rcs) */ char * RCS_check_kflag (arg) - char *arg; + const char *arg; { - static char *kflags[] = - {"kv", "kvl", "k", "v", "o", (char *) NULL}; + static const char *const kflags[] = + {"kv", "kvl", "k", "v", "o", "b", (char *) NULL}; + static const char *const keyword_usage[] = + { + "%s %s: invalid RCS keyword expansion mode\n", + "Valid expansion modes include:\n", + " -kkv\tGenerate keywords using the default form.\n", + " -kkvl\tLike -kkv, except locker's name inserted.\n", + " -kk\tGenerate only keyword names in keyword strings.\n", + " -kv\tGenerate only keyword values in keyword strings.\n", + " -ko\tGenerate the old keyword string (no changes from checked in file).\n", + " -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n", + NULL, + }; char karg[10]; - char **cpp = NULL; + char const *const *cpp = NULL; #ifndef HAVE_RCS5 error (1, 0, "%s %s: your version of RCS does not support the -k option", @@ -1521,12 +1662,7 @@ RCS_check_kflag (arg) if (arg == NULL || *cpp == NULL) { - (void) fprintf (stderr, "%s %s: invalid -k option\n", - program_name, command_name); - (void) fprintf (stderr, "\tvalid options are:\n"); - for (cpp = kflags; *cpp != NULL; cpp++) - (void) fprintf (stderr, "\t\t-k%s\n", *cpp); - error (1, 0, "Please retry with a valid -k option"); + usage (keyword_usage); } (void) sprintf (karg, "-k%s", *cpp); @@ -1539,10 +1675,10 @@ RCS_check_kflag (arg) */ void RCS_check_tag (tag) - char *tag; + const char *tag; { char *invalid = "$,.:;@"; /* invalid RCS tag characters */ - char *cp; + const char *cp; /* * The first character must be an alphabetic letter. The remaining @@ -1565,3 +1701,26 @@ RCS_check_tag (tag) error (1, 0, "tag `%s' must start with a letter", tag); } +#ifdef DEATH_SUPPORT +/* + * Return true if RCS revision with TAG is a dead revision. + */ +int +RCS_isdead (rcs, tag) + RCSNode *rcs; + const char *tag; +{ + Node *p; + RCSVers *version; + + if (rcs->flags & PARTIAL) + RCS_reparsercsfile (rcs); + + p = findnode (rcs->versions, tag); + if (p == NULL) + return (0); + + version = (RCSVers *) p->data; + return (version->dead); +} +#endif /* DEATH_SUPPORT */ diff --git a/gnu/usr.bin/cvs/cvs/rcs.h b/gnu/usr.bin/cvs/cvs/rcs.h index 6d6ecd4..f64501d 100644 --- a/gnu/usr.bin/cvs/cvs/rcs.h +++ b/gnu/usr.bin/cvs/cvs/rcs.h @@ -25,6 +25,13 @@ #define RCSSYMBOLS "symbols" #define RCSDATE "date" #define RCSDESC "desc" +#define RCSEXPAND "expand" + +/* Used by the version of death support which results if you define + DEATH_SUPPORT and not DEATH_STATE. Requires a hacked up RCS. Considered + obsolete. */ +#define RCSDEAD "dead" + #define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d" #define SDATEFORM "%d.%d.%d.%d.%d.%d" @@ -33,6 +40,8 @@ */ #define VALID 0x1 /* flags field contains valid data */ #define INATTIC 0x2 /* RCS file is located in the Attic */ +#define PARTIAL 0x4 /* RCS file not completly parsed */ + struct rcsnode { int refcount; @@ -41,10 +50,12 @@ struct rcsnode char *head; char *branch; char *symbols_data; + char *expand; List *symbols; List *versions; List *dates; }; + typedef struct rcsnode RCSNode; struct rcsversnode @@ -52,6 +63,7 @@ struct rcsversnode char *version; char *date; char *next; + int dead; List *branches; }; typedef struct rcsversnode RCSVers; @@ -69,13 +81,14 @@ typedef struct rcsversnode RCSVers; * exported interfaces */ List *RCS_parsefiles PROTO((List * files, char *xrepos)); -RCSNode *RCS_parse PROTO((char *file, char *repos)); +RCSNode *RCS_parse PROTO((const char *file, const char *repos)); RCSNode *RCS_parsercsfile PROTO((char *rcsfile)); -char *RCS_check_kflag PROTO((char *arg)); +char *RCS_check_kflag PROTO((const char *arg)); char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match)); -char *RCS_gettag PROTO((RCSNode * rcs, char *tag, int force_tag_match)); +char *RCS_gettag PROTO((RCSNode * rcs, char *symtag, int force_tag_match, + int return_both)); char *RCS_getversion PROTO((RCSNode * rcs, char *tag, char *date, - int force_tag_match)); + int force_tag_match, int return_both)); char *RCS_magicrev PROTO((RCSNode *rcs, char *rev)); int RCS_isbranch PROTO((char *file, char *rev, List *srcfiles)); int RCS_nodeisbranch PROTO((char *rev, RCSNode *rcs)); @@ -84,7 +97,11 @@ char *RCS_head PROTO((RCSNode * rcs)); int RCS_datecmp PROTO((char *date1, char *date2)); time_t RCS_getrevtime PROTO((RCSNode * rcs, char *rev, char *date, int fudge)); List *RCS_symbols PROTO((RCSNode *rcs)); -void RCS_check_tag PROTO((char *tag)); +void RCS_check_tag PROTO((const char *tag)); void freercsnode PROTO((RCSNode ** rnodep)); -void RCS_addnode PROTO((char *file, RCSNode *rcs, List *list)); +void RCS_addnode PROTO((const char *file, RCSNode *rcs, List *list)); char *RCS_getbranch PROTO((RCSNode * rcs, char *tag, int force_tag_match)); + +#ifdef DEATH_SUPPORT +int RCS_isdead PROTO((RCSNode *, const char *)); +#endif diff --git a/gnu/usr.bin/cvs/cvs/recurse.c b/gnu/usr.bin/cvs/cvs/recurse.c index 5ea344c..f5d9433 100644 --- a/gnu/usr.bin/cvs/cvs/recurse.c +++ b/gnu/usr.bin/cvs/cvs/recurse.c @@ -9,10 +9,11 @@ */ #include "cvs.h" +#include "save-cwd.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)recurse.c 1.31 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)recurse.c 1.31 94/09/30 $"; +USE(rcsid); #endif static int do_dir_proc PROTO((Node * p, void *closure)); @@ -25,10 +26,10 @@ static void addfile PROTO((List **listp, char *dir, char *file)); /* * Local static versions eliminates the need for globals */ -static int (*fileproc) (); -static int (*filesdoneproc) (); -static Dtype (*direntproc) (); -static int (*dirleaveproc) (); +static FILEPROC fileproc; +static FILESDONEPROC filesdoneproc; +static DIRENTPROC direntproc; +static DIRLEAVEPROC dirleaveproc; static int which; static Dtype flags; static int aflag; @@ -43,10 +44,10 @@ static List *filelist = NULL; /* holds list of files on which to operate */ static List *dirlist = NULL; /* holds list of directories on which to operate */ struct recursion_frame { - int (*fileproc)(); - int (*filesdoneproc) (); - Dtype (*direntproc) (); - int (*dirleaveproc) (); + FILEPROC fileproc; + FILESDONEPROC filesdoneproc; + DIRENTPROC direntproc; + DIRLEAVEPROC dirleaveproc; Dtype flags; int which; int aflag; @@ -61,18 +62,18 @@ struct recursion_frame { * we operate. In the special case of no arguments, we default to * ".". * - * The general algorythm is as follows. + * The general algorithm is as follows. */ int start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, argc, argv, local, which, aflag, readlock, update_preload, dosrcs, wd_is_repos) - int (*fileproc) (); - int (*filesdoneproc) (); - Dtype (*direntproc) (); - int (*dirleaveproc) (); + FILEPROC fileproc; + FILESDONEPROC filesdoneproc; + DIRENTPROC direntproc; + DIRLEAVEPROC dirleaveproc; int argc; - char *argv[]; + char **argv; int local; int which; int aflag; @@ -103,7 +104,10 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, repository = (char *) NULL; } if (entries) - dellist (&entries); + { + Entries_Close (entries); + entries = NULL; + } if (srcfiles) dellist (&srcfiles); if (filelist) @@ -122,7 +126,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, * process each of the sub-directories, so we pretend like we were * called with the list of sub-dirs of the current dir as args */ - if ((which & W_LOCAL) && !isdir (CVSADM) && !isdir (OCVSADM)) + if ((which & W_LOCAL) && !isdir (CVSADM)) dirlist = Find_Dirs ((char *) NULL, W_LOCAL); else addlist (&dirlist, "."); @@ -151,7 +155,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, /* if this argument is a directory, then add it to the list of directories. */ - if (isdir(argv[i])) + if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) addlist (&dirlist, argv[i]); else { @@ -192,7 +196,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, addfile (&files_by_dir, dir, comp); else if (isdir (dir)) { - if (isdir (CVSADM) || isdir (OCVSADM)) + if (isdir (CVSADM)) { /* otherwise, look for it in the repository. */ char *save_update_dir; @@ -209,8 +213,9 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, /* look for it in the repository. */ repos = Name_Repository (dir, update_dir); (void) sprintf (tmp, "%s/%s", repos, comp); - - if (isdir(tmp)) + free (repos); + + if (!wrap_name_has (comp, WRAP_TOCVS) && isdir(tmp)) addlist (&dirlist, argv[i]); else addfile (&files_by_dir, dir, comp); @@ -262,10 +267,10 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, int do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, xflags, xwhich, xaflag, xreadlock, xdosrcs) - int (*xfileproc) (); - int (*xfilesdoneproc) (); - Dtype (*xdirentproc) (); - int (*xdirleaveproc) (); + FILEPROC xfileproc; + FILESDONEPROC xfilesdoneproc; + DIRENTPROC xdirentproc; + DIRLEAVEPROC xdirleaveproc; Dtype xflags; int xwhich; int xaflag; @@ -291,12 +296,24 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, readlock = noexec ? 0 : xreadlock; dosrcs = xdosrcs; +#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) + /* + * Now would be a good time to check to see if we need to stop + * generating data, to give the buffers a chance to drain to the + * remote client. We should not have locks active at this point. + */ + if (server_active + /* If there are writelocks around, we cannot pause here. */ + && (readlock || noexec)) + server_pause_check(); +#endif + /* * Fill in repository with the current repository */ if (which & W_LOCAL) { - if (isdir (CVSADM) || isdir (OCVSADM)) + if (isdir (CVSADM)) repository = Name_Repository ((char *) NULL, update_dir); else repository = NULL; @@ -350,7 +367,7 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, { /* we will process files, so pre-parse entries */ if (which & W_LOCAL) - entries = ParseEntries (aflag); + entries = Entries_Open (aflag); } } @@ -377,7 +394,8 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, /* clean up */ dellist (&filelist); dellist (&srcfiles); - dellist (&entries); + Entries_Close (entries); + entries = NULL; } /* call-back files done proc (if any) */ @@ -396,7 +414,7 @@ do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc, /* free the saved copy of the pointer if necessary */ if (srepository) { - (void) free (srepository); + free (srepository); repository = (char *) NULL; } @@ -426,7 +444,6 @@ do_dir_proc (p, closure) void *closure; { char *dir = p->key; - char savewd[PATH_MAX]; char newrepos[PATH_MAX]; List *sdirlist; char *srepository; @@ -434,6 +451,7 @@ do_dir_proc (p, closure) Dtype dir_return = R_PROCESS; int stripped_dot = 0; int err = 0; + struct saved_cwd cwd; /* set up update_dir - skip dots if not at start */ if (strcmp (dir, ".") != 0) @@ -477,8 +495,8 @@ do_dir_proc (p, closure) if (dir_return != R_SKIP_ALL) { /* save our current directory and static vars */ - if (getwd (savewd) == NULL) - error (1, 0, "could not get working directory: %s", savewd); + if (save_cwd (&cwd)) + exit (1); sdirlist = dirlist; srepository = repository; dirlist = NULL; @@ -511,8 +529,9 @@ do_dir_proc (p, closure) err = dirleaveproc (dir, err, update_dir); /* get back to where we started and restore state vars */ - if (chdir (savewd) < 0) - error (1, errno, "could not chdir to %s", savewd); + if (restore_cwd (&cwd, NULL)) + exit (1); + free_cwd (&cwd); dirlist = sdirlist; repository = srepository; } @@ -554,7 +573,7 @@ addfile (listp, dir, file) Node *n; /* add this dir. */ - (void) addlist (listp, dir); + addlist (listp, dir); n = findnode (*listp, dir); if (n == NULL) @@ -577,8 +596,8 @@ unroll_files_proc (p, closure) struct recursion_frame *frame = (struct recursion_frame *) closure; int err = 0; List *save_dirlist; - char savewd[PATH_MAX]; char *save_update_dir = NULL; + struct saved_cwd cwd; /* if this dir was also an explicitly named argument, then skip it. We'll catch it later when we do dirs. */ @@ -593,9 +612,8 @@ unroll_files_proc (p, closure) if (strcmp(p->key, ".") != 0) { - if (getwd (savewd) == NULL) - error (1, 0, "could not get working directory: %s", savewd); - + if (save_cwd (&cwd)) + exit (1); if (chdir (p->key) < 0) error (1, errno, "could not chdir to %s", p->key); @@ -617,8 +635,9 @@ unroll_files_proc (p, closure) (void) strcpy (update_dir, save_update_dir); free (save_update_dir); - if (chdir (savewd) < 0) - error (1, errno, "could not chdir to %s", savewd); + if (restore_cwd (&cwd, NULL)) + exit (1); + free_cwd (&cwd); } dirlist = save_dirlist; diff --git a/gnu/usr.bin/cvs/cvs/release.c b/gnu/usr.bin/cvs/cvs/release.c index a6200d9..c768bd3 100644 --- a/gnu/usr.bin/cvs/cvs/release.c +++ b/gnu/usr.bin/cvs/cvs/release.c @@ -12,18 +12,16 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)release.c 1.23 94/09/21 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)release.c 1.23 94/09/21 $"; +USE(rcsid); #endif static void release_delete PROTO((char *dir)); -static char *release_usage[] = +static const char *const release_usage[] = { "Usage: %s %s [-d] modules...\n", - "\t-Q\tReally quiet.\n", "\t-d\tDelete the given directory.\n", - "\t-q\tSomewhat quiet.\n", NULL }; @@ -36,160 +34,201 @@ release (argc, argv) { FILE *fp; register int i, c; - register char *cp; - int margc; - DBM *db; - datum key, val; char *repository, *srepos; - char **margv, *modargv[MAXFILEPERDIR], line[PATH_MAX]; + char line[PATH_MAX], update_cmd[PATH_MAX]; + char *thisarg; + int arg_start_idx; - if (argc == -1) - usage (release_usage); - optind = 1; - while ((c = getopt (argc, argv, "Qdq")) != -1) - { - switch (c) - { - case 'Q': - really_quiet = 1; - /* FALL THROUGH */ - case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + if (!server_active) + { +#endif /* SERVER_SUPPORT */ + if (argc == -1) + usage (release_usage); + optind = 1; + while ((c = getopt (argc, argv, "Qdq")) != -1) + { + switch (c) + { + case 'Q': + case 'q': +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; - case 'd': + case 'd': delete++; break; - case '?': - default: + case '?': + default: usage (release_usage); break; - } - } - argc -= optind; - argv += optind; + } + } + argc -= optind; + argv += optind; +#ifdef SERVER_SUPPORT + } +#endif /* SERVER_SUPPORT */ - if (!(db = open_module ())) - return (1); - for (i = 0; i < argc; i++) - { + /* We're going to run "cvs -n -q update" and check its output; if + * the output is sufficiently unalarming, then we release with no + * questions asked. Else we prompt, then maybe release. + */ + /* Construct the update command. */ + sprintf (update_cmd, "%s -n -q -d %s update", + program_path, CVSroot); - /* - * If we are in a repository, do it. Else if we are in the parent of - * a directory with the same name as the module, "cd" into it and - * look for a repository there. - */ - if (isdir (argv[i])) - { - if (chdir (argv[i]) < 0) - { - if (!really_quiet) - error (0, 0, "can't chdir to: %s", argv[i]); - continue; - } - if (!isdir (CVSADM) && !isdir (OCVSADM)) - { - if (!really_quiet) - error (0, 0, "no repository module: %s", argv[i]); - continue; - } +#ifdef CLIENT_SUPPORT + /* Start the server; we'll close it after looping. */ + if (client_active) + { + start_server (); + ign_setup (); + } +#endif /* CLIENT_SUPPORT */ + + /* If !server_active, we already skipped over argv[0] in the "argc + -= optind;" statement above. But if server_active, we need to + skip it now. */ +#ifdef SERVER_SUPPORT + if (server_active) + arg_start_idx = 1; + else + arg_start_idx = 0; +#endif /* SERVER_SUPPORT */ + + for (i = arg_start_idx; i < argc; i++) + { + thisarg = argv[i]; + +#ifdef SERVER_SUPPORT + if (server_active) + { + /* Just log the release -- all the interesting stuff happened + * on the client. + */ + history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ + } + else + { +#endif /* SERVER_SUPPORT */ + + /* + * If we are in a repository, do it. Else if we are in the parent of + * a directory with the same name as the module, "cd" into it and + * look for a repository there. + */ + if (isdir (thisarg)) + { + if (chdir (thisarg) < 0) + { + if (!really_quiet) + error (0, 0, "can't chdir to: %s", thisarg); + continue; + } + if (!isdir (CVSADM)) + { + if (!really_quiet) + error (0, 0, "no repository module: %s", thisarg); + continue; + } } else - { - if (!really_quiet) - error (0, 0, "no such directory/module: %s", argv[i]); - continue; + { + if (!really_quiet) + error (0, 0, "no such directory: %s", thisarg); + continue; } repository = Name_Repository ((char *) NULL, (char *) NULL); srepos = Short_Repository (repository); - - /* grab module entry from database and check against short repos */ - key.dptr = argv[i]; - key.dsize = strlen (key.dptr); - val = dbm_fetch (db, key); - if (!val.dptr) - { - error (0, 0, "no such module name: %s", argv[i]); - continue; - } - val.dptr[val.dsize] = '\0'; - if ((cp = strchr (val.dptr, '#')) != NULL) /* Strip out a comment */ + + if (!really_quiet) { - do - { - *cp-- = '\0'; - } while (isspace (*cp)); - } - (void) sprintf (line, "%s %s", key.dptr, val.dptr); - line2argv (&margc, modargv, line); - margv = modargv; + /* The "release" command piggybacks on "update", which + * does the real work of finding out if anything is not + * up-to-date with the repository. Then "release" prompts + * the user, telling her how many files have been + * modified, and asking if she still wants to do the + * release. + */ + fp = Popen (update_cmd, "r"); + c = 0; - optind = 1; - while (getopt (margc, margv, CVSMODULE_OPTS) != -1) - /* do nothing */ ; - margc -= optind; - margv += optind; + while (fgets (line, sizeof (line), fp)) + { + if (strchr ("MARCZ", *line)) + c++; + (void) printf (line); + } - if (margc < 1) - { - error (0, 0, "modules file missing directory for key %s value %s", - key.dptr, val.dptr); - continue; - } - if (strcmp (*margv, srepos)) - { - error (0, 0, "repository mismatch: module[%s], here[%s]", - *margv, srepos); - free (repository); - continue; - } + /* If the update exited with an error, then we just want to + * complain and go on to the next arg. Especially, we do + * not want to delete the local copy, since it's obviously + * not what the user thinks it is. + */ + if ((pclose (fp)) != 0) + { + error (0, 0, "unable to release `%s'", thisarg); + continue; + } - if (!really_quiet) - { - - /* - * Now see if there is any reason not to allow a "Release" This - * is "popen()" instead of "Popen()" since we don't want "-n" to - * stop it. - */ - fp = popen ("cvs -n -q update", "r"); - c = 0; - while (fgets (line, sizeof (line), fp)) - { - if (strchr ("MARCZ", *line)) - c++; - (void) printf (line); - } - (void) pclose (fp); - (void) printf ("You have [%d] altered files in this repository.\n", - c); - (void) printf ("Are you sure you want to release %smodule `%s': ", - delete ? "(and delete) " : "", argv[i]); - c = !yesno (); - if (c) /* "No" */ - { - (void) fprintf (stderr, "** `%s' aborted by user choice.\n", - command_name); - free (repository); - continue; - } + (void) printf ("You have [%d] altered files in this repository.\n", + c); + (void) printf ("Are you sure you want to release %smodule `%s': ", + delete ? "(and delete) " : "", thisarg); + c = !yesno (); + if (c) /* "No" */ + { + (void) fprintf (stderr, "** `%s' aborted by user choice.\n", + command_name); + free (repository); + continue; + } } - /* - * So, we've passed all the tests, go ahead and release it. First, - * log the release, then attempt to delete it. - */ - history_write ('F', argv[i], "", argv[i], ""); /* F == Free */ - free (repository); - - if (delete) - release_delete (argv[i]); - } - close_module (db); +#ifdef CLIENT_SUPPORT + if (client_active) + { + if (fprintf (to_server, "Argument %s\n", thisarg) < 0) + error (1, errno, "writing to server"); + if (fprintf (to_server, "release\n") < 0) + error (1, errno, "writing to server"); + } + else + { +#endif /* CLIENT_SUPPORT */ + history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ +#ifdef CLIENT_SUPPORT + } /* else client not active */ +#endif /* CLIENT_SUPPORT */ + + free (repository); + if (delete) release_delete (thisarg); + +#ifdef CLIENT_SUPPORT + if (client_active) + return get_responses_and_close (); + else +#endif /* CLIENT_SUPPORT */ + return (0); + +#ifdef SERVER_SUPPORT + } /* else server not active */ +#endif /* SERVER_SUPPORT */ + } /* `for' loop */ return (0); } -/* We want to "rm -r" the repository, but let us be a little paranoid. */ + +/* We want to "rm -r" the working directory, but let us be a little + paranoid. */ static void release_delete (dir) char *dir; diff --git a/gnu/usr.bin/cvs/cvs/remove.c b/gnu/usr.bin/cvs/cvs/remove.c index 0a893d1..a33c4f9 100644 --- a/gnu/usr.bin/cvs/cvs/remove.c +++ b/gnu/usr.bin/cvs/cvs/remove.c @@ -18,8 +18,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)remove.c 1.39 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)remove.c 1.39 94/10/07 $"; +USE(rcsid); #endif static int remove_fileproc PROTO((char *file, char *update_dir, @@ -32,7 +32,7 @@ static int local; static int removed_files; static int existing_files; -static char *remove_usage[] = +static const char *const remove_usage[] = { "Usage: %s %s [-flR] [files...]\n", "\t-f\tDelete the file before removing it.\n", @@ -44,7 +44,7 @@ static char *remove_usage[] = int cvsremove (argc, argv) int argc; - char *argv[]; + char **argv; { int c, err; @@ -74,6 +74,21 @@ cvsremove (argc, argv) argc -= optind; argv += optind; + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (client_active) { + start_server (); + ign_setup (); + if (local) + send_arg("-l"); + send_files (argc, argv, local, 0); + if (fprintf (to_server, "remove\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + /* start the recursion processor */ err = start_recursion (remove_fileproc, (int (*) ()) NULL, remove_dirproc, (int (*) ()) NULL, argc, argv, local, @@ -108,12 +123,22 @@ remove_fileproc (file, update_dir, repository, entries, srcfiles) char fname[PATH_MAX]; Vers_TS *vers; - /* - * If unlinking the file works, good. If not, the "unremoved" - * error will indicate problems. - */ if (force) - (void) unlink (file); + { + if (!noexec) + { + if (unlink (file) < 0 && ! existence_error (errno)) + { + if (update_dir[0] == '\0') + error (0, errno, "unable to remove %s", file); + else + error (0, errno, "unable to remove %s/%s", update_dir, + file); + } + } + /* else FIXME should probably act as if the file doesn't exist + in doing the following checks. */ + } vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL, file, 0, 0, entries, srcfiles); @@ -133,16 +158,18 @@ remove_fileproc (file, update_dir, repository, entries, srcfiles) { /* * It's a file that has been added, but not commited yet. So, - * remove the ,p and ,t file for it and scratch it from the - * entries file. - */ + * remove the ,t file for it and scratch it from the + * entries file. */ Scratch_Entry (entries, file); - (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT); - (void) unlink_file (fname); (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); (void) unlink_file (fname); if (!quiet) error (0, 0, "removed `%s'", file); + +#ifdef SERVER_SUPPORT + if (server_active) + server_checked_in (file, update_dir, repository); +#endif } else if (vers->vn_user[0] == '-') { @@ -159,6 +186,11 @@ remove_fileproc (file, update_dir, repository, entries, srcfiles) if (!quiet) error (0, 0, "scheduling `%s' for removal", file); removed_files++; + +#ifdef SERVER_SUPPORT + if (server_active) + server_checked_in (file, update_dir, repository); +#endif } freevers_ts (&vers); diff --git a/gnu/usr.bin/cvs/cvs/repos.c b/gnu/usr.bin/cvs/cvs/repos.c index e7794d4..8566433 100644 --- a/gnu/usr.bin/cvs/cvs/repos.c +++ b/gnu/usr.bin/cvs/cvs/repos.c @@ -13,8 +13,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)repos.c 1.32 94/09/23 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)repos.c 1.32 94/09/23 $"; +USE(rcsid); #endif char * @@ -28,9 +28,7 @@ Name_Repository (dir, update_dir) char path[PATH_MAX]; char tmp[PATH_MAX]; char cvsadm[PATH_MAX]; - char ocvsadm[PATH_MAX]; char *cp; - int has_cvsadm = 0, has_ocvsadm = 0; if (update_dir && *update_dir) xupdate_dir = update_dir; @@ -38,47 +36,18 @@ Name_Repository (dir, update_dir) xupdate_dir = "."; if (dir != NULL) - { (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); - (void) sprintf (ocvsadm, "%s/%s", dir, OCVSADM); - } else - { (void) strcpy (cvsadm, CVSADM); - (void) strcpy (ocvsadm, OCVSADM); - } /* sanity checks */ - if (!(has_cvsadm = isdir (cvsadm)) && !(has_ocvsadm = isdir (ocvsadm))) + if (!isdir (cvsadm)) { error (0, 0, "in directory %s:", xupdate_dir); error (1, 0, "there is no version here; do '%s checkout' first", program_name); } - if (has_ocvsadm) - { - if (has_cvsadm) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, 0, "error: both `%s' and `%s' exist; I give up", - CVSADM, OCVSADM); - } - if (rename (ocvsadm, cvsadm) < 0) - { - error (0, 0, "in directory %s:", xupdate_dir); - error (1, errno, "cannot rename `%s' to `%s'; I give up", - OCVSADM, CVSADM); - } - - /* - * We have converted the old CVS.adm directory to the new CVS - * directory. Now, convert the Entries file to the new format, if - * necessary. - */ - check_entries (dir); - } - if (dir != NULL) (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT); else @@ -127,7 +96,7 @@ Name_Repository (dir, update_dir) error (0, 0, "`..'-relative repositories are not supported."); error (1, 0, "illegal source repository"); } - if (repos[0] != '/') + if (! isabsolute(repos)) { if (CVSroot == NULL) { @@ -139,7 +108,11 @@ Name_Repository (dir, update_dir) (void) strcpy (path, repos); (void) sprintf (repos, "%s/%s", CVSroot, path); } +#ifdef CLIENT_SUPPORT + if (!client_active && !isdir (repos)) +#else if (!isdir (repos)) +#endif { error (0, 0, "in directory %s:", xupdate_dir); error (1, 0, "there is no repository %s", repos); @@ -162,9 +135,13 @@ Short_Repository (repository) if (repository == NULL) return (NULL); - /* if repository matches CVSroot at the beginning, strip off CVSroot */ + /* If repository matches CVSroot at the beginning, strip off CVSroot */ + /* And skip leading '/' in rep, in case CVSroot ended with '/'. */ if (strncmp (CVSroot, repository, strlen (CVSroot)) == 0) - return (repository + strlen (CVSroot) + 1); + { + char *rep = repository + strlen (CVSroot); + return (*rep == '/') ? rep+1 : rep; + } else return (repository); } diff --git a/gnu/usr.bin/cvs/cvs/root.c b/gnu/usr.bin/cvs/cvs/root.c index 9ff9ffa..e3cb979 100644 --- a/gnu/usr.bin/cvs/cvs/root.c +++ b/gnu/usr.bin/cvs/cvs/root.c @@ -14,8 +14,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "@(#)root.c,v 1.2 1994/09/15 05:32:17 zoo Exp"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)root.c,v 1.2 1994/09/15 05:32:17 zoo Exp"; +USE(rcsid); #endif char * @@ -38,22 +38,19 @@ Name_Root(dir, update_dir) if (dir != NULL) { (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); + (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); } else { (void) strcpy (cvsadm, CVSADM); - } - - if (dir != NULL) - (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); - else (void) strcpy (tmp, CVSADM_ROOT); + } /* * Do not bother looking for a readable file if there is no cvsadm * directory present. * - * It is possiible that not all repositories will have a CVS/Root + * It is possible that not all repositories will have a CVS/Root * file. This is ok, but the user will need to specify -d * /path/name or have the environment variable CVSROOT set in * order to continue. @@ -90,7 +87,14 @@ Name_Root(dir, update_dir) * root now contains a candidate for CVSroot. It must be an * absolute pathname */ + +#ifdef CLIENT_SUPPORT + /* It must specify a server via remote CVS or be an absolute pathname. */ + if ((strchr (root, ':') == NULL) + && ! isabsolute (root)) +#else if (root[0] != '/') +#endif { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, @@ -99,7 +103,11 @@ Name_Root(dir, update_dir) return (NULL); } +#ifdef CLIENT_SUPPORT + if ((strchr (root, ':') == NULL) && !isdir (root)) +#else if (!isdir (root)) +#endif { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, @@ -154,6 +162,9 @@ Create_Root (dir, rootdir) FILE *fout; char tmp[PATH_MAX]; + if (noexec) + return; + /* record the current cvs root */ if (rootdir != NULL) @@ -163,7 +174,7 @@ Create_Root (dir, rootdir) else (void) strcpy (tmp, CVSADM_ROOT); fout = open_file (tmp, "w+"); - if (fprintf (fout, "%s\n", rootdir) == EOF) + if (fprintf (fout, "%s\n", rootdir) < 0) error (1, errno, "write to %s failed", tmp); if (fclose (fout) == EOF) error (1, errno, "cannot close %s", tmp); diff --git a/gnu/usr.bin/cvs/cvs/rtag.c b/gnu/usr.bin/cvs/cvs/rtag.c index c10f68b..76e1776 100644 --- a/gnu/usr.bin/cvs/cvs/rtag.c +++ b/gnu/usr.bin/cvs/cvs/rtag.c @@ -14,19 +14,45 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)rtag.c 1.61 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)rtag.c 1.61 94/09/30 $"; +USE(rcsid); #endif +static int check_fileproc PROTO((char *file, char *update_dir, + char *repository, List * entries, + List * srcfiles)); +static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int pretag_proc PROTO((char *repository, char *filter)); +static void masterlist_delproc PROTO((Node *p)); +static void tag_delproc PROTO((Node *p)); +static int pretag_list_proc PROTO((Node *p, void *closure)); + static Dtype rtag_dirproc PROTO((char *dir, char *repos, char *update_dir)); static int rtag_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); -static int rtag_proc PROTO((int *pargc, char *argv[], char *xwhere, +static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere, char *mwhere, char *mfile, int shorten, int local_specified, char *mname, char *msg)); static int rtag_delete PROTO((RCSNode *rcsfile)); + +struct tag_info +{ + Ctype status; + char *rev; + char *tag; + char *options; +}; + +struct master_lists +{ + List *tlist; +}; + +static List *mtlist; +static List *tlist; + static char *symtag; static char *numtag; static int delete; /* adding a tag by default */ @@ -37,16 +63,14 @@ static int local; /* recursive by default */ static int force_tag_match = 1; /* force by default */ static int force_tag_move; /* don't move existing tags by default */ -static char *rtag_usage[] = +static const char *const rtag_usage[] = { - "Usage: %s %s [-QaflRnqF] [-b] [-d] [-r tag|-D date] tag modules...\n", - "\t-Q\tReally quiet.\n", + "Usage: %s %s [-aflRnF] [-b] [-d] [-r tag|-D date] tag modules...\n", "\t-a\tClear tag from removed files that would not otherwise be tagged.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", "\t-R\tProcess directories recursively.\n", "\t-n\tNo execution of 'tag program'\n", - "\t-q\tSomewhat quiet.\n", "\t-d\tDelete the given Tag.\n", "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", "\t-[rD]\tExisting tag or Date.\n", @@ -57,7 +81,7 @@ static char *rtag_usage[] = int rtag (argc, argv) int argc; - char *argv[]; + char **argv; { register int i; int c; @@ -80,10 +104,15 @@ rtag (argc, argv) run_module_prog = 0; break; case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'l': local = 1; @@ -131,6 +160,46 @@ rtag (argc, argv) error (0, 0, "warning: -b ignored with -d options"); RCS_check_tag (symtag); +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (delete) + send_arg("-d"); + if (branch_mode) + send_arg("-b"); + if (force_tag_move) + send_arg("-F"); + if (run_module_prog) + send_arg("-n"); + if (attic_too) + send_arg("-a"); + + if (numtag) + option_with_arg ("-r", numtag); + if (date) + client_senddate (date); + + send_arg (symtag); + + { + int i; + for (i = 0; i < argc; ++i) + send_arg (argv[i]); + } + + if (fprintf (to_server, "rtag\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + db = open_module (); for (i = 0; i < argc; i++) { @@ -153,7 +222,7 @@ static int rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, mname, msg) int *pargc; - char *argv[]; + char **argv; char *xwhere; char *mwhere; char *mfile; @@ -220,14 +289,214 @@ rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified, else which = W_REPOS; + /* check to make sure they are authorized to tag all the + specified files in the repository */ + + mtlist = getlist(); + err = start_recursion (check_fileproc, check_filesdoneproc, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + *pargc - 1, argv + 1, local, which, 0, 1, + where, 1, 1); + + if (err) + { + error (1, 0, "correct the above errors first!"); + } + /* start the recursion processor */ - err = start_recursion (rtag_fileproc, (int (*) ()) NULL, rtag_dirproc, - (int (*) ()) NULL, *pargc - 1, argv + 1, local, + err = start_recursion (rtag_fileproc, (FILESDONEPROC) NULL, rtag_dirproc, + (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local, which, 0, 1, where, 1, 1); + dellist(&mtlist); + return (err); } +/* check file that is to be tagged */ +/* All we do here is add it to our list */ + +static int +check_fileproc(file, update_dir, repository, entries, srcfiles) + char *file; + char *update_dir; + char *repository; + List * entries; + List * srcfiles; +{ + char *xdir; + Node *p; + Vers_TS *vers; + + if (update_dir[0] == '\0') + xdir = "."; + else + xdir = update_dir; + if ((p = findnode (mtlist, xdir)) != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + struct master_lists *ml; + + tlist = getlist (); + p = getnode (); + p->key = xstrdup (xdir); + p->type = UPDATE; + ml = (struct master_lists *) + xmalloc (sizeof (struct master_lists)); + ml->tlist = tlist; + p->data = (char *) ml; + p->delproc = masterlist_delproc; + (void) addnode (mtlist, p); + } + /* do tlist */ + p = getnode (); + p->key = xstrdup (file); + p->type = UPDATE; + p->delproc = tag_delproc; + vers = Version_TS (repository, (char *) NULL, (char *) NULL, + (char *) NULL, file, 0, 0, entries, srcfiles); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + if (p->data != NULL) + { + int addit = 1; + char *oversion; + + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + if (oversion == NULL) + { + if (delete) + { + addit = 0; + } + } + else if (strcmp(oversion, p->data) == 0) + { + addit = 0; + } + else if (!force_tag_move) + { + addit = 0; + } + if (oversion != NULL) + { + free(oversion); + } + if (!addit) + { + free(p->data); + p->data = NULL; + } + } + freevers_ts (&vers); + (void) addnode (tlist, p); + return (0); +} + +static int +check_filesdoneproc(err, repos, update_dir) + int err; + char *repos; + char *update_dir; +{ + int n; + Node *p; + + p = findnode(mtlist, update_dir); + if (p != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + tlist = (List *) NULL; + } + if ((tlist == NULL) || (tlist->list->next == tlist->list)) + { + return (err); + } + if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) + { + error (0, 0, "Pre-tag check failed"); + err += n; + } + return (err); +} + +static int +pretag_proc(repository, filter) + char *repository; + char *filter; +{ + if (filter[0] == '/') + { + char *s, *cp; + + s = xstrdup(filter); + for (cp=s; *cp; cp++) + { + if (isspace(*cp)) + { + *cp = '\0'; + break; + } + } + if (!isfile(s)) + { + error (0, errno, "cannot find pre-tag filter '%s'", s); + free(s); + return (1); + } + free(s); + } + run_setup("%s %s %s %s", + filter, + symtag, + delete ? "del" : force_tag_move ? "mov" : "add", + repository); + walklist(tlist, pretag_list_proc, NULL); + return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); +} + +static void +masterlist_delproc(p) + Node *p; +{ + struct master_lists *ml; + + ml = (struct master_lists *)p->data; + dellist(&ml->tlist); + free(ml); + return; +} + +static void +tag_delproc(p) + Node *p; +{ + if (p->data != NULL) + { + free(p->data); + p->data = NULL; + } + return; +} + +static int +pretag_list_proc(p, closure) + Node *p; + void *closure; +{ + if (p->data != NULL) + { + run_arg(p->key); + run_arg(p->data); + } + return (0); +} + /* * Called to tag a particular file, as appropriate with the options that were * set above. @@ -272,7 +541,7 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles) return (rtag_delete (rcsfile)); } - version = RCS_getversion (rcsfile, numtag, date, force_tag_match); + version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 0); if (version == NULL) { /* If -a specified, clean up any old tags */ @@ -299,7 +568,7 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles) * the branch. Use a symbolic tag for that. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, numtag); + retcode = RCS_settag(rcsfile->path, symtag, numtag); } else { @@ -315,7 +584,7 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles) * typical tagging operation. */ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; - oversion = RCS_getversion (rcsfile, symtag, (char *) 0, 1); + oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); if (oversion != NULL) { int isbranch = RCS_isbranch (file, symtag, srcfiles); @@ -347,10 +616,10 @@ rtag_fileproc (file, update_dir, repository, entries, srcfiles) } free (oversion); } - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev); + retcode = RCS_settag(rcsfile->path, symtag, rev); } - run_arg (rcsfile->path); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + + if (retcode != 0) { error (1, retcode == -1 ? errno : 0, "failed to set tag `%s' to revision `%s' in `%s'", @@ -382,20 +651,18 @@ rtag_delete (rcsfile) if (numtag) { - version = RCS_getversion (rcsfile, numtag, (char *) 0, 1); + version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0); if (version == NULL) return (0); free (version); } - version = RCS_getversion (rcsfile, symtag, (char *) 0, 1); + version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0); if (version == NULL) return (0); free (version); - run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag); - run_arg (rcsfile->path); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0) + if ((retcode = RCS_deltag(rcsfile->path, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -420,3 +687,6 @@ rtag_dirproc (dir, repos, update_dir) error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir); return (R_PROCESS); } + + + diff --git a/gnu/usr.bin/cvs/cvs/status.c b/gnu/usr.bin/cvs/cvs/status.c index 2f14a0b..fe53bcb 100644 --- a/gnu/usr.bin/cvs/cvs/status.c +++ b/gnu/usr.bin/cvs/cvs/status.c @@ -11,8 +11,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)status.c 1.56 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)status.c 1.56 94/10/07 $"; +USE(rcsid); #endif static Dtype status_dirproc PROTO((char *dir, char *repos, char *update_dir)); @@ -26,7 +26,7 @@ static int long_format = 0; static char *xfile; static List *xsrcfiles; -static char *status_usage[] = +static const char *const status_usage[] = { "Usage: %s %s [-vlR] [files...]\n", "\t-v\tVerbose format; includes tag information for the file\n", @@ -38,7 +38,7 @@ static char *status_usage[] = int status (argc, argv) int argc; - char *argv[]; + char **argv; { int c; int err = 0; @@ -69,9 +69,34 @@ status (argc, argv) argc -= optind; argv += optind; + wrap_setup (); + +#ifdef CLIENT_SUPPORT + if (client_active) { + start_server (); + + ign_setup (); + + if (long_format) + send_arg("-v"); + if (local) + send_arg("-l"); + + /* XXX This should only need to send file info; the file + contents themselves will not be examined. */ + send_files (argc, argv, local, 0); + + if (fprintf (to_server, "status\n") < 0) + error (1, errno, "writing to server"); + err = get_responses_and_close (); + + return err; + } +#endif + /* start the recursion processor */ - err = start_recursion (status_fileproc, (int (*) ()) NULL, status_dirproc, - (int (*) ()) NULL, argc, argv, local, + err = start_recursion (status_fileproc, (FILESDONEPROC) NULL, status_dirproc, + (DIRLEAVEPROC) NULL, argc, argv, local, W_LOCAL, 0, 1, (char *) NULL, 1, 0); return (err); @@ -104,6 +129,11 @@ status_fileproc (file, update_dir, repository, entries, srcfiles) case T_CHECKOUT: sstat = "Needs Checkout"; break; +#ifdef SERVER_SUPPORT + case T_PATCH: + sstat = "Needs Patch"; + break; +#endif case T_CONFLICT: sstat = "Unresolved Conflict"; break; @@ -143,6 +173,10 @@ status_fileproc (file, update_dir, repository, entries, srcfiles) (void) printf (" Working revision:\tNo entry for %s\n", file); else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') (void) printf (" Working revision:\tNew file!\n"); +#ifdef SERVER_SUPPORT + else if (server_active) + (void) printf (" Working revision:\t%s\n", vers->vn_user); +#endif else (void) printf (" Working revision:\t%s\t%s\n", vers->vn_user, vers->ts_rcs); @@ -181,17 +215,17 @@ status_fileproc (file, update_dir, repository, entries, srcfiles) } } } - else + else if (!really_quiet) (void) printf (" Sticky Tag:\t\t(none)\n"); if (edata->date) (void) printf (" Sticky Date:\t\t%s\n", edata->date); - else + else if (!really_quiet) (void) printf (" Sticky Date:\t\t(none)\n"); if (edata->options && edata->options[0]) (void) printf (" Sticky Options:\t%s\n", edata->options); - else + else if (!really_quiet) (void) printf (" Sticky Options:\t(none)\n"); if (long_format && vers->srcfile) diff --git a/gnu/usr.bin/cvs/cvs/tag.c b/gnu/usr.bin/cvs/cvs/tag.c index fa7f162..55c8659 100644 --- a/gnu/usr.bin/cvs/cvs/tag.c +++ b/gnu/usr.bin/cvs/cvs/tag.c @@ -14,29 +14,57 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)tag.c 1.60 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)tag.c 1.60 94/09/30 $"; +USE(rcsid); #endif +static int check_fileproc PROTO((char *file, char *update_dir, + char *repository, List * entries, + List * srcfiles)); +static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); +static int pretag_proc PROTO((char *repository, char *filter)); +static void masterlist_delproc PROTO((Node *p)); +static void tag_delproc PROTO((Node *p)); +static int pretag_list_proc PROTO((Node *p, void *closure)); + static Dtype tag_dirproc PROTO((char *dir, char *repos, char *update_dir)); static int tag_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); +static char *numtag; +static char *date = NULL; static char *symtag; static int delete; /* adding a tag by default */ static int branch_mode; /* make an automagic "branch" tag */ static int local; /* recursive by default */ +static int force_tag_match = 1; /* force tag to match by default */ static int force_tag_move; /* don't force tag to move by default */ -static char *tag_usage[] = +struct tag_info +{ + Ctype status; + char *rev; + char *tag; + char *options; +}; + +struct master_lists { - "Usage: %s %s [-QlRqF] [-b] [-d] tag [files...]\n", - "\t-Q\tReally quiet.\n", + List *tlist; +}; + +static List *mtlist; +static List *tlist; + +static const char *const tag_usage[] = +{ + "Usage: %s %s [-lRF] [-b] [-d] tag [files...]\n", "\t-l\tLocal directory only, not recursive.\n", "\t-R\tProcess directories recursively.\n", - "\t-q\tSomewhat quiet.\n", "\t-d\tDelete the given Tag.\n", + "\t-[rD]\tExisting tag or date.\n", + "\t-f\tForce a head revision if tag etc not found.\n", "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", "\t-F\tMove tag if it already exists\n", NULL @@ -45,7 +73,7 @@ static char *tag_usage[] = int tag (argc, argv) int argc; - char *argv[]; + char **argv; { int c; int err = 0; @@ -54,15 +82,20 @@ tag (argc, argv) usage (tag_usage); optind = 1; - while ((c = getopt (argc, argv, "FQqlRdb")) != -1) + while ((c = getopt (argc, argv, "FQqlRdr:D:bf")) != -1) { switch (c) { case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'l': local = 1; @@ -73,6 +106,17 @@ tag (argc, argv) case 'd': delete = 1; break; + case 'r': + numtag = optarg; + break; + case 'D': + if (date) + free (date); + date = Make_Date (optarg); + break; + case 'f': + force_tag_match = 0; + break; case 'b': branch_mode = 1; break; @@ -98,13 +142,246 @@ tag (argc, argv) error (0, 0, "warning: -b ignored with -d options"); RCS_check_tag (symtag); +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* We're the client side. Fire up the remote server. */ + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (delete) + send_arg("-d"); + if (branch_mode) + send_arg("-b"); + if (force_tag_move) + send_arg("-F"); + + send_arg (symtag); + +#if 0 + /* FIXME: We shouldn't have to send current files, but I'm not sure + whether it works. So send the files -- + it's slower but it works. */ + send_file_names (argc, argv); +#else + send_files (argc, argv, local, 0); +#endif + if (fprintf (to_server, "tag\n") < 0) + error (1, errno, "writing to server"); + return get_responses_and_close (); + } +#endif + + /* check to make sure they are authorized to tag all the + specified files in the repository */ + + mtlist = getlist(); + err = start_recursion (check_fileproc, check_filesdoneproc, + (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, + argc, argv, local, W_LOCAL, 0, 1, + (char *) NULL, 1, 0); + + if (err) + { + error (1, 0, "correct the above errors first!"); + } + /* start the recursion processor */ - err = start_recursion (tag_fileproc, (int (*) ()) NULL, tag_dirproc, - (int (*) ()) NULL, argc, argv, local, + err = start_recursion (tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc, + (DIRLEAVEPROC) NULL, argc, argv, local, W_LOCAL, 0, 1, (char *) NULL, 1, 0); + dellist(&mtlist); + return (err); +} + +/* check file that is to be tagged */ +/* All we do here is add it to our list */ + +static int +check_fileproc(file, update_dir, repository, entries, srcfiles) + char *file; + char *update_dir; + char *repository; + List * entries; + List * srcfiles; +{ + char *xdir; + Node *p; + Vers_TS *vers; + + if (update_dir[0] == '\0') + xdir = "."; + else + xdir = update_dir; + if ((p = findnode (mtlist, xdir)) != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + struct master_lists *ml; + + tlist = getlist (); + p = getnode (); + p->key = xstrdup (xdir); + p->type = UPDATE; + ml = (struct master_lists *) + xmalloc (sizeof (struct master_lists)); + ml->tlist = tlist; + p->data = (char *) ml; + p->delproc = masterlist_delproc; + (void) addnode (mtlist, p); + } + /* do tlist */ + p = getnode (); + p->key = xstrdup (file); + p->type = UPDATE; + p->delproc = tag_delproc; + vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL, + file, 0, 0, entries, srcfiles); + p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); + if (p->data != NULL) + { + int addit = 1; + char *oversion; + + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); + if (oversion == NULL) + { + if (delete) + { + addit = 0; + } + } + else if (strcmp(oversion, p->data) == 0) + { + addit = 0; + } + else if (!force_tag_move) + { + addit = 0; + } + if (oversion != NULL) + { + free(oversion); + } + if (!addit) + { + free(p->data); + p->data = NULL; + } + } + freevers_ts(&vers); + (void) addnode (tlist, p); + return (0); +} + +static int +check_filesdoneproc(err, repos, update_dir) + int err; + char *repos; + char *update_dir; +{ + int n; + Node *p; + + p = findnode(mtlist, update_dir); + if (p != NULL) + { + tlist = ((struct master_lists *) p->data)->tlist; + } + else + { + tlist = (List *) NULL; + } + if ((tlist == NULL) || (tlist->list->next == tlist->list)) + { + return (err); + } + if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) + { + error (0, 0, "Pre-tag check failed"); + err += n; + } return (err); } +static int +pretag_proc(repository, filter) + char *repository; + char *filter; +{ + if (filter[0] == '/') + { + char *s, *cp; + + s = xstrdup(filter); + for (cp=s; *cp; cp++) + { + if (isspace(*cp)) + { + *cp = '\0'; + break; + } + } + if (!isfile(s)) + { + error (0, errno, "cannot find pre-tag filter '%s'", s); + free(s); + return (1); + } + free(s); + } + run_setup("%s %s %s %s", + filter, + symtag, + delete ? "del" : force_tag_move ? "mov" : "add", + repository); + walklist(tlist, pretag_list_proc, NULL); + return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); +} + +static void +masterlist_delproc(p) + Node *p; +{ + struct master_lists *ml; + + ml = (struct master_lists *)p->data; + dellist(&ml->tlist); + free(ml); + return; +} + +static void +tag_delproc(p) + Node *p; +{ + if (p->data != NULL) + { + free(p->data); + p->data = NULL; + } + return; +} + +static int +pretag_list_proc(p, closure) + Node *p; + void *closure; +{ + if (p->data != NULL) + { + run_arg(p->key); + run_arg(p->data); + } + return (0); +} + + /* * Called to tag a particular file (the currently checked out version is * tagged with the specified tag - or the specified tag is deleted). @@ -119,6 +396,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) List *srcfiles; { char *version, *oversion; + char *nversion = NULL; char *rev; Vers_TS *vers; int retcode = 0; @@ -126,6 +404,18 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL, file, 0, 0, entries, srcfiles); + if ((numtag != NULL) || (date != NULL)) + { + nversion = RCS_getversion(vers->srcfile, + numtag, + date, + force_tag_match, 0); + if (nversion == NULL) + { + freevers_ts (&vers); + return (0); + } + } if (delete) { @@ -138,7 +428,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) * "rcs" to remove the tag... trust me. */ - version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1); + version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); if (version == NULL || vers->srcfile == NULL) { freevers_ts (&vers); @@ -146,9 +436,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) } free (version); - run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag); - run_arg (vers->srcfile->path); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0) + if ((retcode = RCS_deltag(vers->srcfile->path, symtag, 1)) != 0) { if (!quiet) error (0, retcode == -1 ? errno : 0, @@ -175,7 +463,14 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) * If we are adding a tag, we need to know which version we have checked * out and we'll tag that version. */ - version = vers->vn_user; + if (nversion == NULL) + { + version = vers->vn_user; + } + else + { + version = nversion; + } if (version == NULL) { freevers_ts (&vers); @@ -212,7 +507,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) * module -- which I have found to be a typical tagging operation. */ rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; - oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1); + oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); if (oversion != NULL) { int isbranch = RCS_isbranch (file, symtag, srcfiles); @@ -245,9 +540,7 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) free (oversion); } - run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev); - run_arg (vers->srcfile->path); - if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0) { error (1, retcode == -1 ? errno : 0, "failed to set tag %s to revision %s in %s", @@ -266,6 +559,10 @@ tag_fileproc (file, update_dir, repository, entries, srcfiles) } freevers_ts (&vers); + if (nversion != NULL) + { + free(nversion); + } return (0); } diff --git a/gnu/usr.bin/cvs/cvs/update.c b/gnu/usr.bin/cvs/cvs/update.c index 9fdc0b4..0dd0e1b 100644 --- a/gnu/usr.bin/cvs/cvs/update.c +++ b/gnu/usr.bin/cvs/cvs/update.c @@ -34,14 +34,26 @@ */ #include "cvs.h" +#ifdef CLIENT_SUPPORT +#include "update.h" +#endif +#ifdef SERVER_SUPPORT +#include "md5.h" +#endif #ifndef lint -static char rcsid[] = "$CVSid: @(#)update.c 1.95 94/10/22 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)update.c 1.95 94/10/22 $"; +USE(rcsid); #endif static int checkout_file PROTO((char *file, char *repository, List *entries, List *srcfiles, Vers_TS *vers_ts, char *update_dir)); +#ifdef SERVER_SUPPORT +static int patch_file PROTO((char *file, char *repository, List *entries, + List *srcfiles, Vers_TS *vers_ts, char *update_dir, + int *docheckout, struct stat *file_info, + unsigned char *checksum)); +#endif static int isemptydir PROTO((char *dir)); static int merge_file PROTO((char *file, char *repository, List *entries, Vers_TS *vers, char *update_dir)); @@ -51,11 +63,18 @@ static Dtype update_dirent_proc PROTO((char *dir, char *repository, char *update static int update_dirleave_proc PROTO((char *dir, int err, char *update_dir)); static int update_file_proc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); +#ifndef CLIENT_SUPPORT static int update_filesdone_proc PROTO((int err, char *repository, char *update_dir)); +#endif static int write_letter PROTO((char *file, int letter, char *update_dir)); static void ignore_files PROTO((List * ilist, char *update_dir)); +#ifdef SERVER_SUPPORT +static void join_file PROTO((char *file, List *srcfiles, Vers_TS *vers_ts, + char *update_dir, List *entries, char *repository)); +#else static void join_file PROTO((char *file, List *srcfiles, Vers_TS *vers_ts, char *update_dir, List *entries)); +#endif static char *options = NULL; static char *tag = NULL; @@ -67,25 +86,32 @@ static int force_tag_match = 1; static int update_build_dirs = 0; static int update_prune_dirs = 0; static int pipeout = 0; +#ifdef SERVER_SUPPORT +static int patches = 0; +#endif +#ifdef CLIENT_SUPPORT +List *ignlist = (List *) NULL; +#else static List *ignlist = (List *) NULL; +#endif static time_t last_register_time; -static char *update_usage[] = +static const char *const update_usage[] = { - "Usage:\n %s %s [-APQdflRpq] [-k kopt] [-r rev|-D date] [-j rev] [-I ign] [files...]\n", + "Usage: %s %s [-APdflRp] [-k kopt] [-r rev|-D date] [-j rev]\n", + " [-I ign] [-W spec] [files...]\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-P\tPrune empty directories.\n", - "\t-Q\tReally quiet.\n", "\t-d\tBuild directories, like checkout does.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, no recursion.\n", "\t-R\tProcess directories recursively.\n", "\t-p\tSend updates to standard output.\n", - "\t-q\tSomewhat quiet.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", "\t-r rev\tUpdate using specified revision/tag.\n", "\t-D date\tSet date to update from.\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", "\t-I ign\tMore files to ignore (! to reset).\n", + "\t-W spec\tWrappers specification line.\n", NULL }; @@ -95,7 +121,7 @@ static char *update_usage[] = int update (argc, argv) int argc; - char *argv[]; + char **argv; { int c, err; int local = 0; /* recursive by default */ @@ -105,10 +131,11 @@ update (argc, argv) usage (update_usage); ign_setup (); + wrap_setup (); /* parse the args */ optind = 1; - while ((c = getopt (argc, argv, "ApPflRQqdk:r:D:j:I:")) != -1) + while ((c = getopt (argc, argv, "ApPflRQqduk:r:D:j:I:W:")) != -1) { switch (c) { @@ -118,6 +145,9 @@ update (argc, argv) case 'I': ign_add (optarg, 0); break; + case 'W': + wrap_add (optarg, 0); + break; case 'k': if (options) free (options); @@ -130,10 +160,15 @@ update (argc, argv) local = 0; break; case 'Q': - really_quiet = 1; - /* FALL THROUGH */ case 'q': - quiet = 1; +#ifdef SERVER_SUPPORT + /* The CVS 1.5 client sends these options (in addition to + Global_option requests), so we must ignore them. */ + if (!server_active) +#endif + error (1, 0, + "-q or -Q must be specified before \"%s\"", + command_name); break; case 'd': update_build_dirs = 1; @@ -162,6 +197,14 @@ update (argc, argv) else join_rev1 = optarg; break; + case 'u': +#ifdef SERVER_SUPPORT + if (server_active) + patches = 1; + else +#endif + usage (update_usage); + break; case '?': default: usage (update_usage); @@ -171,6 +214,98 @@ update (argc, argv) argc -= optind; argv += optind; +#ifdef CLIENT_SUPPORT + if (client_active) + { + /* The first pass does the regular update. If we receive at least + one patch which failed, we do a second pass and just fetch + those files whose patches failed. */ + do + { + int status; + + start_server (); + + ign_setup (); + + if (local) + send_arg("-l"); + if (update_build_dirs) + send_arg("-d"); + if (pipeout) + send_arg("-p"); + if (!force_tag_match) + send_arg("-f"); + if (aflag) + send_arg("-A"); + if (update_prune_dirs) + send_arg("-P"); + client_prune_dirs = update_prune_dirs; + option_with_arg ("-r", tag); + if (date) + client_senddate (date); + if (join_rev1) + option_with_arg ("-j", join_rev1); + if (join_rev2) + option_with_arg ("-j", join_rev2); + + /* If the server supports the command "update-patches", that means + that it knows how to handle the -u argument to update, which + means to send patches instead of complete files. */ + if (failed_patches == NULL) + { + struct request *rq; + + for (rq = requests; rq->name != NULL; rq++) + { + if (strcmp (rq->name, "update-patches") == 0) + { + if (rq->status == rq_supported) + { + send_arg("-u"); + } + break; + } + } + } + + if (failed_patches == NULL) + send_files (argc, argv, local, aflag); + else + { + int i; + + (void) printf ("%s client: refetching unpatchable files\n", + program_name); + + if (toplevel_wd[0] != '\0' + && chdir (toplevel_wd) < 0) + { + error (1, errno, "could not chdir to %s", toplevel_wd); + } + + for (i = 0; i < failed_patches_count; i++) + (void) unlink_file (failed_patches[i]); + send_files (failed_patches_count, failed_patches, local, + aflag); + } + + failed_patches = NULL; + failed_patches_count = 0; + + if (fprintf (to_server, "update\n") < 0) + error (1, errno, "writing to server"); + + status = get_responses_and_close (); + if (status != 0) + return status; + + } while (failed_patches != NULL); + + return 0; + } +#endif + /* * If we are updating the entire directory (for real) and building dirs * as we go, we make sure there is no static entries file and write the @@ -180,20 +315,30 @@ update (argc, argv) { if (update_build_dirs) { - if (unlink_file (CVSADM_ENTSTAT) < 0 && errno != ENOENT) + if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_clear_entstat (".", Name_Repository (NULL, NULL)); +#endif } /* keep the CVS/Tag file current with the specified arguments */ if (aflag || tag || date) + { WriteTag ((char *) NULL, tag, date); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_sticky (".", Name_Repository (NULL, NULL), tag, date); +#endif + } } /* look for files/dirs locally and in the repository */ which = W_LOCAL | W_REPOS; /* look in the attic too if a tag or date is specified */ - if (tag != NULL || date != NULL) + if (tag != NULL || date != NULL || joining()) which |= W_ATTIC; /* call the command line interface */ @@ -215,7 +360,7 @@ int do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir) int argc; - char *argv[]; + char **argv; char *xoptions; char *xtag; char *xdate; @@ -331,6 +476,9 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) case T_MODIFIED: /* locally modified */ case T_REMOVED: /* removed but not committed */ case T_CHECKOUT: /* needs checkout */ +#ifdef SERVER_SUPPORT + case T_PATCH: /* needs patch */ +#endif retval = checkout_file (file, repository, entries, srcfiles, vers, update_dir); break; @@ -356,8 +504,22 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) (void) write_letter (file, 'C', update_dir); break; case T_NEEDS_MERGE: /* needs merging */ - retval = merge_file (file, repository, entries, - vers, update_dir); + if (noexec) + { + retval = 1; + (void) write_letter (file, 'C', update_dir); + } + else + { + if (wrap_merge_is_copy (file)) + /* Should we be warning the user that we are + * overwriting the user's copy of the file? */ + retval = checkout_file (file, repository, entries, + srcfiles, vers, update_dir); + else + retval = merge_file (file, repository, entries, + vers, update_dir); + } break; case T_MODIFIED: /* locally modified */ retval = 0; @@ -370,9 +532,19 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) * If the timestamp has changed and no conflict indicators * are found, it isn't a 'C' any more. */ +#ifdef SERVER_SUPPORT + if (server_active) + retcode = vers->ts_conflict[0] != '='; + else { + filestamp = time_stamp (file); + retcode = strcmp (vers->ts_conflict, filestamp); + free (filestamp); + } +#else filestamp = time_stamp (file); retcode = strcmp (vers->ts_conflict, filestamp); free (filestamp); +#endif if (retcode) { @@ -380,10 +552,10 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) * If the timestamps differ, look for Conflict * indicators to see if 'C' anyway. */ - run_setup ("%s -s", GREP); + run_setup ("%s", GREP); run_arg (RCS_MERGE_PAT); run_arg (file); - retcode = run_exec (RUN_TTY, RUN_TTY, + retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY,RUN_NORMAL); if (retcode == -1) { @@ -413,9 +585,41 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) if (!retval) retval = write_letter (file, 'M', update_dir); break; +#ifdef SERVER_SUPPORT + case T_PATCH: /* needs patch */ + if (patches) + { + int docheckout; + struct stat file_info; + unsigned char checksum[16]; + + retval = patch_file (file, repository, entries, srcfiles, + vers, update_dir, &docheckout, + &file_info, checksum); + if (! docheckout) + { + if (server_active && retval == 0) + server_updated (file, update_dir, repository, + SERVER_PATCHED, &file_info, + checksum); + break; + } + } + /* Fall through. */ + /* If we're not running as a server, just check the + file out. It's simpler and faster than starting up + two new processes (diff and patch). */ + /* Fall through. */ +#endif case T_CHECKOUT: /* needs checkout */ retval = checkout_file (file, repository, entries, srcfiles, vers, update_dir); +#ifdef SERVER_SUPPORT + if (server_active && retval == 0) + server_updated (file, update_dir, repository, + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); +#endif break; case T_ADDED: /* added but not committed */ retval = write_letter (file, 'A', update_dir); @@ -425,6 +629,12 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) break; case T_REMOVE_ENTRY: /* needs to be un-registered */ retval = scratch_file (file, repository, entries, update_dir); +#ifdef SERVER_SUPPORT + if (server_active && retval == 0) + server_updated (file, update_dir, repository, + SERVER_UPDATED, (struct stat *) NULL, + (unsigned char *) NULL); +#endif break; default: /* can't ever happen :-) */ error (0, 0, @@ -436,7 +646,11 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) /* only try to join if things have gone well thus far */ if (retval == 0 && join_rev1) +#ifdef SERVER_SUPPORT + join_file (file, srcfiles, vers, update_dir, entries, repository); +#else join_file (file, srcfiles, vers, update_dir, entries); +#endif /* if this directory has an ignore list, add this file to it */ if (ignlist) @@ -458,7 +672,12 @@ update_file_proc (file, update_dir, repository, entries, srcfiles) * update_filesdone_proc () is used */ /* ARGSUSED */ +#ifdef CLIENT_SUPPORT +/* Also used by client.c */ +int +#else static int +#endif update_filesdone_proc (err, repository, update_dir) int err; char *repository; @@ -472,17 +691,35 @@ update_filesdone_proc (err, repository, update_dir) } /* Clean up CVS admin dirs if we are export */ +#ifdef CLIENT_SUPPORT + /* In the client, we need to clean these up after we create them. Doing + it here might would clean up the user's previous contents even on + SIGINT which probably is bad. */ + if (!client_active && strcmp (command_name, "export") == 0) +#else if (strcmp (command_name, "export") == 0) +#endif { run_setup ("%s -fr", RM); run_arg (CVSADM); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); } #ifdef CVSADM_ROOT - else +#ifdef SERVER_SUPPORT + else if (!server_active && !pipeout) +#else + else if (!pipeout) +#endif /* SERVER_SUPPORT */ { /* If there is no CVS/Root file, add one */ +#ifdef CLIENT_SUPPORT + if (!isfile (CVSADM_ROOT) + /* but only if we want it */ + && ! (getenv ("CVS_IGNORE_REMOTE_ROOT") && strchr (CVSroot, ':')) + ) +#else /* No CLIENT_SUPPORT */ if (!isfile (CVSADM_ROOT)) +#endif /* No CLIENT_SUPPORT */ Create_Root( (char *) NULL, CVSroot ); } #endif /* CVSADM_ROOT */ @@ -528,7 +765,7 @@ update_dirent_proc (dir, repository, update_dir) { /* otherwise, create the dir and appropriate adm files */ make_directory (dir); - Create_Admin (dir, repository, tag, date); + Create_Admin (dir, update_dir, repository, tag, date); } } @@ -543,13 +780,23 @@ update_dirent_proc (dir, repository, update_dir) char tmp[PATH_MAX]; (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT); - if (unlink_file (tmp) < 0 && errno != ENOENT) + if (unlink_file (tmp) < 0 && ! existence_error (errno)) error (1, errno, "cannot remove file %s", tmp); +#ifdef SERVER_SUPPORT + if (server_active) + server_clear_entstat (update_dir, repository); +#endif } /* keep the CVS/Tag file current with the specified arguments */ if (aflag || tag || date) + { WriteTag (dir, tag, date); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_sticky (update_dir, repository, tag, date); +#endif + } /* initialize the ignore list for this directory */ ignlist = getlist (); @@ -599,43 +846,7 @@ update_dirleave_proc (dir, err, update_dir) free (repository); } - /* Clean up CVS admin dirs if we are export */ - if (strcmp (command_name, "export") == 0) - { - run_setup ("%s -fr", RM); - run_arg (CVSADM); - (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); - } -#ifdef CVSADM_ROOT - else - { - /* If there is no CVS/Root file, add one */ - if (!isreadable (CVSADM_ROOT)) - { - if (isfile (CVSADM_ROOT)) - { - error (0, 0, "bad permissions %s/%s deleteing it", update_dir, - CVSADM_ROOT); - if (unlink_file (CVSADM_ROOT) == -1) - { - error (0, errno, "delete failed for %s/%s", - update_dir, CVSADM_ROOT); - } - } - Create_Root( (char *) NULL, CVSroot ); - } - else - { - char *root = Name_Root( (char *) NULL, update_dir); - - if (root == NULL) - Create_Root( (char *) NULL, CVSroot ); - else - free (root); /* all is well, release the storage */ - } - } -#endif /* CVSADM_ROOT */ - + /* FIXME: chdir ("..") loses with symlinks. */ /* Prune empty dirs on the way out - if necessary */ (void) chdir (".."); if (update_prune_dirs && isemptydir (dir)) @@ -667,8 +878,7 @@ isemptydir (dir) while ((dp = readdir (dirp)) != NULL) { if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 && - strcmp (dp->d_name, CVSADM) != 0 && - strcmp (dp->d_name, OCVSADM) != 0) + strcmp (dp->d_name, CVSADM) != 0) { (void) closedir (dirp); return (0); @@ -709,6 +919,9 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) char backup[PATH_MAX]; int set_time, retval = 0; int retcode = 0; +#ifdef DEATH_SUPPORT + int file_is_dead; +#endif /* don't screw with backup files if we're going to stdout */ if (!pipeout) @@ -720,7 +933,13 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) (void) unlink_file (backup); } - run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs, +#ifdef DEATH_SUPPORT + file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs); + + if (!file_is_dead) { +#endif + + run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_tag, vers_ts->options); /* @@ -749,15 +968,49 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) if (!pipeout) run_arg (file); +#ifdef DEATH_SUPPORT + } + if (file_is_dead || (retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, +#else if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, +#endif (pipeout ? (RUN_NORMAL|RUN_REALLY) : RUN_NORMAL))) == 0) { if (!pipeout) { Vers_TS *xvers_ts; +#ifdef DEATH_SUPPORT + int resurrecting; + + resurrecting = 0; + if (file_is_dead && joining()) + { + /* when joining, we need to get dead files checked + out. Try harder. */ + run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs, + vers_ts->options); + + run_arg ("-f"); + run_arg (vers_ts->srcfile->path); + run_arg (file); + if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) + { + error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, + "could not check out %s", file); + (void) unlink_file (backup); + return (retcode); + } + file_is_dead = 0; + resurrecting = 1; + } + + if (cvswrite == TRUE && !file_is_dead) + xchmod (file, 1); +#else /* No DEATH_SUPPORT */ if (cvswrite == TRUE) xchmod (file, 1); +#endif /* No DEATH_SUPPORT */ /* set the time from the RCS file iff it was unknown before */ if (vers_ts->vn_user == NULL || @@ -768,16 +1021,64 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) else set_time = 0; + wrap_fromcvs_process_file (file); + xvers_ts = Version_TS (repository, options, tag, date, file, force_tag_match, set_time, entries, srcfiles); if (strcmp (xvers_ts->options, "-V4") == 0) xvers_ts->options[0] = '\0'; + /* If no keyword expansion was specified on command line, + use whatever was in the file. This is how we tell the client + whether a file is binary. */ + if (xvers_ts->options[0] == '\0') + { + if (vers_ts->srcfile->expand != NULL) + { + free (xvers_ts->options); + xvers_ts->options = + xmalloc (strlen (vers_ts->srcfile->expand) + 3); + strcpy (xvers_ts->options, "-k"); + strcat (xvers_ts->options, vers_ts->srcfile->expand); + } + } (void) time (&last_register_time); +#ifdef DEATH_SUPPORT + if (file_is_dead) + { + if (xvers_ts->vn_user != NULL) + { + if (update_dir[0] == '\0') + error (0, 0, + "warning: %s is not (any longer) pertinent", + file); + else + error (0, 0, + "warning: %s/%s is not (any longer) pertinent", + update_dir, file); + } + Scratch_Entry (entries, file); + if (unlink_file (file) < 0 && ! existence_error (errno)) + { + if (update_dir[0] == '\0') + error (0, errno, "cannot remove %s", file); + else + error (0, errno, "cannot remove %s/%s", update_dir, + file); + } + } + else + Register (entries, file, + resurrecting ? "0" : xvers_ts->vn_rcs, + xvers_ts->ts_user, xvers_ts->options, + xvers_ts->tag, xvers_ts->date, + (char *)0); /* Clear conflict flag on fresh checkout */ +#else /* No DEATH_SUPPORT */ Register (entries, file, xvers_ts->vn_rcs, xvers_ts->ts_user, xvers_ts->options, xvers_ts->tag, xvers_ts->date, (char *)0); /* Clear conflict flag on fresh checkout */ +#endif /* No DEATH_SUPPORT */ /* fix up the vers structure, in case it is used by join */ if (join_rev1) @@ -797,7 +1098,11 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) freevers_ts (&xvers_ts); +#ifdef DEATH_SUPPORT + if (!really_quiet && !file_is_dead) +#else if (!really_quiet) +#endif { if (update_dir[0]) (void) printf ("U %s/%s\n", update_dir, file); @@ -825,6 +1130,226 @@ checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir) return (retval); } +#ifdef SERVER_SUPPORT +/* Patch a file. Runs rcsdiff. This is only done when running as the + * server. The hope is that the diff will be smaller than the file + * itself. + */ +static int +patch_file (file, repository, entries, srcfiles, vers_ts, update_dir, + docheckout, file_info, checksum) + char *file; + char *repository; + List *entries; + List *srcfiles; + Vers_TS *vers_ts; + char *update_dir; + int *docheckout; + struct stat *file_info; + unsigned char *checksum; +{ + char backup[PATH_MAX]; + char file1[PATH_MAX]; + char file2[PATH_MAX]; + int retval = 0; + int retcode = 0; + int fail; + FILE *e; + + *docheckout = 0; + + if (pipeout || joining ()) + { + *docheckout = 1; + return 0; + } + + (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file); + if (isfile (file)) + rename_file (file, backup); + else + (void) unlink_file (backup); + + (void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, file); + (void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, file); + + fail = 0; + + /* We need to check out both revisions first, to see if either one + has a trailing newline. Because of this, we don't use rcsdiff, + but just use diff. */ + run_setup ("%s%s -q -p -r%s %s %s", Rcsbin, RCS_CO, vers_ts->vn_user, + vers_ts->options, vers_ts->srcfile->path); + if (run_exec (RUN_TTY, file1, RUN_TTY, RUN_NORMAL) != 0) + fail = 1; + else + { + e = fopen (file1, "r"); + if (e == NULL) + fail = 1; + else + { + if (fseek (e, (long) -1, SEEK_END) == 0 + && getc (e) != '\n') + { + fail = 1; + } + fclose (e); + } + } + + if (! fail) + { + /* Check it out into file, and then move to file2, so that we + can get the right modes into *FILE_INFO. We can't check it + out directly into file2 because co doesn't understand how + to do that. */ + run_setup ("%s%s -q -r%s %s %s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs, + vers_ts->options, vers_ts->srcfile->path, file); + if (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0) + fail = 1; + else + { + if (!isreadable (file)) + { + /* File is dead. */ + fail = 1; + } + else + { + rename_file (file, file2); + if (cvswrite == TRUE) + xchmod (file2, 1); + e = fopen (file2, "r"); + if (e == NULL) + fail = 1; + else + { + struct MD5Context context; + int nl; + unsigned char buf[8192]; + unsigned len; + + nl = 0; + + /* Compute the MD5 checksum and make sure there is + a trailing newline. */ + MD5Init (&context); + while ((len = fread (buf, 1, sizeof buf, e)) != 0) + { + nl = buf[len - 1] == '\n'; + MD5Update (&context, buf, len); + } + MD5Final (checksum, &context); + + if (ferror (e) || ! nl) + { + fail = 1; + } + + fclose (e); + } + } + } + } + + if (! fail) + { + /* FIXME: This whole thing with diff/patch is rather more + convoluted than necessary (lots of forks and execs, need to + worry about versions of diff and patch, etc.). Also, we + send context lines which aren't needed (in the rare case in + which the diff doesn't apply, the checksum would catches it). + Solution perhaps is to librarify the RCS routines which apply + deltas or something equivalent. */ + /* This is -c, not -u, because we have no way of knowing which + DIFF is in use. */ + run_setup ("%s -c %s %s", DIFF, file1, file2); + + /* A retcode of 0 means no differences. 1 means some differences. */ + if ((retcode = run_exec (RUN_TTY, file, RUN_TTY, RUN_NORMAL)) != 0 + && retcode != 1) + { + fail = 1; + } + else + { +#define BINARY "Binary" + char buf[sizeof BINARY]; + unsigned int c; + + /* Check the diff output to make sure patch will be handle it. */ + e = fopen (file, "r"); + if (e == NULL) + error (1, errno, "could not open diff output file %s", file); + c = fread (buf, 1, sizeof BINARY - 1, e); + buf[c] = '\0'; + if (strcmp (buf, BINARY) == 0) + { + /* These are binary files. We could use diff -a, but + patch can't handle that. */ + fail = 1; + } + fclose (e); + } + } + + if (! fail) + { + Vers_TS *xvers_ts; + + /* This stuff is just copied blindly from checkout_file. I + don't really know what it does. */ + xvers_ts = Version_TS (repository, options, tag, date, file, + force_tag_match, 0, entries, srcfiles); + if (strcmp (xvers_ts->options, "-V4") == 0) + xvers_ts->options[0] = '\0'; + + Register (entries, file, xvers_ts->vn_rcs, + xvers_ts->ts_user, xvers_ts->options, + xvers_ts->tag, xvers_ts->date, NULL); + + if (stat (file2, file_info) < 0) + error (1, errno, "could not stat %s", file2); + + /* If this is really Update and not Checkout, recode history */ + if (strcmp (command_name, "update") == 0) + history_write ('P', update_dir, xvers_ts->vn_rcs, file, + repository); + + freevers_ts (&xvers_ts); + + if (!really_quiet) + { + if (update_dir[0]) + (void) printf ("P %s/%s\n", update_dir, file); + else + (void) printf ("P %s\n", file); + } + } + else + { + int old_errno = errno; /* save errno value over the rename */ + + if (isfile (backup)) + rename_file (backup, file); + + if (retcode != 0 && retcode != 1) + error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0, + "could not diff %s", file); + + *docheckout = 1; + retval = retcode; + } + + (void) unlink_file (backup); + (void) unlink_file (file1); + (void) unlink_file (file2); + + return (retval); +} +#endif + /* * Several of the types we process only print a bit of information consisting * of a single letter and the name. @@ -878,16 +1403,9 @@ merge_file (file, repository, entries, vers, update_dir) copy_file (file, backup); xchmod (file, 1); - /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */ - run_setup ("%s%s %s -r%s -r%s", Rcsbin, RCS_RCSMERGE, vers->options, - vers->vn_user, vers->vn_rcs); - run_arg (vers->srcfile->path); - status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); - if (status != 0 -#ifdef HAVE_RCS5 - && status != 1 -#endif - ) + status = RCS_merge(vers->srcfile->path, + vers->options, vers->vn_user, vers->vn_rcs); + if (status != 0 && status != 1) { error (0, status == -1 ? errno : 0, "could not merge revision %s of %s", vers->vn_user, user); @@ -919,7 +1437,19 @@ merge_file (file, repository, entries, vers, update_dir) vers->vn_user = xstrdup (vers->vn_rcs); } - if (!xcmp (backup, file)) +#ifdef SERVER_SUPPORT + /* Send the new contents of the file before the message. If we + wanted to be totally correct, we would have the client write + the message only after the file has safely been written. */ + if (server_active) + { + server_copy_file (file, update_dir, repository, backup); + server_updated (file, update_dir, repository, SERVER_MERGED, + (struct stat *) NULL, (unsigned char *) NULL); + } +#endif + + if (!noexec && !xcmp (backup, file)) { printf ("%s already contains the differences between %s and %s\n", user, vers->vn_user, vers->vn_rcs); @@ -927,12 +1457,7 @@ merge_file (file, repository, entries, vers, update_dir) return (0); } - /* possibly run GREP to see if there appear to be conflicts in the file */ - run_setup ("%s -s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (file); - if (status == 1 || - (retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) == 0) + if (status == 1) { if (!noexec) error (0, 0, "conflicts found in %s", user); @@ -961,7 +1486,12 @@ merge_file (file, repository, entries, vers, update_dir) * (-j option) */ static void +#ifdef SERVER_SUPPORT +join_file (file, srcfiles, vers, update_dir, entries, repository) + char *repository; +#else join_file (file, srcfiles, vers, update_dir, entries) +#endif char *file; List *srcfiles; Vers_TS *vers; @@ -985,6 +1515,14 @@ join_file (file, srcfiles, vers, update_dir, entries) jdate1 = date_rev1; jdate2 = date_rev2; + if (wrap_merge_is_copy (file)) + { + /* FIXME: Should be including update_dir in message. */ + error (0, 0, + "Cannot merge %s because it is a merge-by-copy file.", file); + return; + } + /* determine if we need to do anything at all */ if (vers->srcfile == NULL || vers->srcfile->path == NULL) @@ -1003,9 +1541,38 @@ join_file (file, srcfiles, vers, update_dir, entries) jdate1 = NULL; } + /* The file in the working directory doesn't exist in CVS/Entries. + FIXME: Shouldn't this case result in additional processing (if + the file was added going from rev1 to rev2, then do the equivalent + of a "cvs add")? (yes; easier said than done.. :-) */ + if (vers->vn_user == NULL) + { + /* No merge possible YET. */ + if (jdate2 != NULL) + error (0, 0, + "file %s is present in revision %s as of %s", + file, jrev2, jdate2); + else + error (0, 0, + "file %s is present in revision %s", + file, jrev2); + return; + } + + /* Fix for bug CVS/193: + * Used to dump core if the file had been removed on the current branch. + */ + if (strcmp(vers->vn_user, "0") == 0) + { + error(0, 0, + "file %s has been deleted", + file); + return; + } + /* convert the second rev spec, walking branches and dates. */ - rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1); + rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, 0); if (rev2 == NULL) { if (!quiet) @@ -1018,13 +1585,14 @@ join_file (file, srcfiles, vers, update_dir, entries) error (0, 0, "cannot find revision %s in file %s", jrev2, file); - return; } + return; } - + /* skip joining identical revs */ - if (strcmp (rev2, vers->vn_user) == 0) /* no merge necessary */ + if (strcmp (rev2, vers->vn_user) == 0) { + /* No merge necessary. */ free (rev2); return; } @@ -1036,6 +1604,9 @@ join_file (file, srcfiles, vers, update_dir, entries) greatest common ancestor of both the join rev, and the checked out rev. */ + /* FIXME: What is this check for '!' about? If it is legal to + have '!' in the first character of vn_user, it isn't + documented at struct vers_ts in cvs.h. */ tst = vers->vn_user; if (*tst == '!') { @@ -1053,7 +1624,7 @@ join_file (file, srcfiles, vers, update_dir, entries) abort(); } - tst = RCS_gettag (vers->srcfile, rev2, 1); + tst = RCS_gettag (vers->srcfile, rev2, 1, 0); if (tst == NULL) { /* this should not be possible. */ @@ -1076,10 +1647,10 @@ join_file (file, srcfiles, vers, update_dir, entries) /* otherwise, convert the first rev spec, walking branches and dates. */ - rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1); - if (rev1 == NULL - && !quiet) + rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, 0); + if (rev1 == NULL) { + if (!quiet) { if (jdate1 != NULL) error (0, 0, "cannot find revision %s as of %s in file %s", @@ -1088,7 +1659,8 @@ join_file (file, srcfiles, vers, update_dir, entries) error (0, 0, "cannot find revision %s in file %s", jrev1, file); - return; + } + return; } } @@ -1099,7 +1671,7 @@ join_file (file, srcfiles, vers, update_dir, entries) /* special handling when two revisions are specified */ if (join_rev1 && join_rev2) { - rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1); + rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1, 0); if (rev == NULL) { if (!quiet && date_rev2 == NULL) @@ -1108,7 +1680,7 @@ join_file (file, srcfiles, vers, update_dir, entries) return; } - baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1); + baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0); if (baserev == NULL) { if (!quiet && date_rev1 == NULL) @@ -1136,7 +1708,7 @@ join_file (file, srcfiles, vers, update_dir, entries) } else { - rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1); + rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1, 0); if (rev == NULL) return; if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */ @@ -1171,6 +1743,20 @@ join_file (file, srcfiles, vers, update_dir, entries) #endif /* OK, so we have two revisions; continue on */ + +#ifdef SERVER_SUPPORT + if (server_active && !isreadable (file)) + { + int retcode; + /* The file is up to date. Need to check out the current contents. */ + run_setup ("%s%s -q -r%s", Rcsbin, RCS_CO, vers->vn_user); + run_arg (vers->srcfile->path); + retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + if (retcode != 0) + error (1, retcode == -1 ? errno : 0, + "failed to check out %s file", file); + } +#endif /* * The users currently modified file is moved to a backup file name @@ -1197,16 +1783,8 @@ join_file (file, srcfiles, vers, update_dir, entries) #endif #endif - /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */ - run_setup ("%s%s %s -r%s -r%s", Rcsbin, RCS_RCSMERGE, options, - rev1, rev2); - run_arg (vers->srcfile->path); - status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); - if (status != 0 -#ifdef HAVE_RCS5 - && status != 1 -#endif - ) + status = RCS_merge (vers->srcfile->path, options, rev1, rev2); + if (status != 0 && status != 1) { error (0, status == -1 ? errno : 0, "could not merge revision %s of %s", rev2, user); @@ -1217,8 +1795,17 @@ join_file (file, srcfiles, vers, update_dir, entries) free (rev1); free (rev2); -#ifdef HAVE_RCS5 +#ifdef SERVER_SUPPORT + /* + * If we're in server mode, then we need to re-register the file + * even if there were no conflicts (status == 0). + * This tells server_updated() to send the modified file back to + * the client. + */ + if (status == 1 || (status == 0 && server_active)) +#else if (status == 1) +#endif { char *cp = 0; @@ -1229,9 +1816,15 @@ join_file (file, srcfiles, vers, update_dir, entries) if (cp) free(cp); } -#endif - return; +#ifdef SERVER_SUPPORT + if (server_active) + { + server_copy_file (file, update_dir, repository, backup); + server_updated (file, update_dir, repository, SERVER_MERGED, + (struct stat *) NULL, (unsigned char *) NULL); + } +#endif } /* @@ -1260,6 +1853,8 @@ ignore_files (ilist, update_dir) return; ign_add_file (CVSDOTIGNORE, 1); + wrap_add_file (CVSDOTWRAPPER, 1); + while ((dp = readdir (dirp)) != NULL) { file = dp->d_name; diff --git a/gnu/usr.bin/cvs/cvs/vers_ts.c b/gnu/usr.bin/cvs/cvs/vers_ts.c index 0bc1242..ebb7ca8 100644 --- a/gnu/usr.bin/cvs/cvs/vers_ts.c +++ b/gnu/usr.bin/cvs/cvs/vers_ts.c @@ -9,11 +9,13 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)vers_ts.c 1.45 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)vers_ts.c 1.45 94/10/07 $"; +USE(rcsid); #endif -#define ctime(X) do not use ctime, please +#ifdef SERVER_SUPPORT +static void time_stamp_server PROTO((char *, Vers_TS *)); +#endif /* * Fill in and return a Vers_TS structure "user" is the name of the local @@ -136,16 +138,39 @@ Version_TS (repository, options, tag, date, user, force_tag_match, /* squirrel away the rcsdata pointer for others */ vers_ts->srcfile = rcsdata; +#ifndef DEATH_SUPPORT + /* (is this indeed death support? I haven't looked carefully). */ /* get RCS version number into vn_rcs (if appropriate) */ if (((vers_ts->tag || vers_ts->date) && force_tag_match) || ((rcsdata->flags & VALID) && (rcsdata->flags & INATTIC) == 0)) { +#endif if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0) + { vers_ts->vn_rcs = xstrdup (vers_ts->vn_user); + vers_ts->vn_tag = xstrdup (vers_ts->vn_user); + } else + { vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag, - vers_ts->date, force_tag_match); + vers_ts->date, force_tag_match, 1); + if (vers_ts->vn_rcs == NULL) + vers_ts->vn_tag = NULL; + else + { + char *colon = strchr (vers_ts->vn_rcs, ':'); + if (colon) + { + vers_ts->vn_tag = xstrdup (colon+1); + *colon = '\0'; + } + else + vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs); + } + } +#ifndef DEATH_SUPPORT } +#endif /* * If the source control file exists and has the requested revision, @@ -167,12 +192,81 @@ Version_TS (repository, options, tag, date, user, force_tag_match, /* get user file time-stamp in ts_user */ if (entries != (List *) NULL) { - vers_ts->ts_user = time_stamp (user); +#ifdef SERVER_SUPPORT + if (server_active) + time_stamp_server (user, vers_ts); + else +#endif + vers_ts->ts_user = time_stamp (user); } return (vers_ts); } +#ifdef SERVER_SUPPORT + +/* Set VERS_TS->TS_USER to time stamp for FILE. */ + +/* Separate these out to keep the logic below clearer. */ +#define mark_lost(V) ((V)->ts_user = 0) +#define mark_unchanged(V) ((V)->ts_user = xstrdup ((V)->ts_rcs)) + +static void +time_stamp_server (file, vers_ts) + char *file; + Vers_TS *vers_ts; +{ + struct stat sb; + char *cp; + + if (stat (file, &sb) < 0) + { + if (! existence_error (errno)) + error (1, errno, "cannot stat temp file"); + if (use_unchanged) + { + /* Missing file means lost or unmodified; check entries + file to see which. + + XXX FIXME - If there's no entries file line, we + wouldn't be getting the file at all, so consider it + lost. I don't know that that's right, but it's not + clear to me that either choice is. Besides, would we + have an RCS string in that case anyways? */ + if (vers_ts->entdata == NULL) + mark_lost (vers_ts); + else if (vers_ts->entdata->timestamp + && vers_ts->entdata->timestamp[0] == '=') + mark_unchanged (vers_ts); + else + mark_lost (vers_ts); + } + else + { + /* Missing file in the temp directory means that the file + was not modified. */ + mark_unchanged (vers_ts); + } + } + else if (sb.st_mtime == 0) + { + if (use_unchanged) + /* We shouldn't reach this case any more! */ + abort (); + + /* Special code used by server.c to indicate the file was lost. */ + mark_lost (vers_ts); + } + else + { + vers_ts->ts_user = xmalloc (25); + cp = asctime (gmtime (&sb.st_mtime)); /* copy in the modify time */ + cp[24] = 0; + (void) strcpy (vers_ts->ts_user, cp); + } +} + +#endif /* SERVER_SUPPORT */ /* * Gets the time-stamp for the file "file" and returns it in space it * allocates @@ -213,6 +307,8 @@ freevers_ts (versp) free ((*versp)->vn_user); if ((*versp)->vn_rcs) free ((*versp)->vn_rcs); + if ((*versp)->vn_tag) + free ((*versp)->vn_tag); if ((*versp)->ts_user) free ((*versp)->ts_user); if ((*versp)->ts_rcs) diff --git a/gnu/usr.bin/cvs/cvsinit/cvsinit.sh b/gnu/usr.bin/cvs/cvsinit/cvsinit.sh index 61f3a10..49c5451 100644 --- a/gnu/usr.bin/cvs/cvsinit/cvsinit.sh +++ b/gnu/usr.bin/cvs/cvsinit/cvsinit.sh @@ -1,18 +1,29 @@ #! /bin/sh : # +#ident "@(#)cvs:$Name: $:$Id: cvsinit.sh,v 1.7 1995/11/14 23:44:18 woods Exp $" # Copyright (c) 1992, Brian Berliner # # You may distribute under the terms of the GNU General Public License as # specified in the README file that comes with the CVS 1.4 kit. -# -# $CVSid: @(#)cvsinit.sh 1.1 94/10/22 $ -# -# This script should be run once to help you setup your site for CVS. + +# This script should be run for each repository you create to help you +# setup your site for CVS. You may also run it to update existing +# repositories if you install a new version of CVS. # this line is edited by Makefile when creating cvsinit.inst CVSLIB="xLIBDIRx" +CVS_VERSION="xVERSIONx" + +# All purpose usage message, also suffices for --help and --version. +if test $# -gt 0; then + echo "cvsinit version $CVS_VERSION" + echo "usage: $0" + echo "(set CVSROOT to the repository that you want to initialize)" + exit 0 +fi + # Make sure that the CVSROOT variable is set if [ "x$CVSROOT" = x ]; then echo "The CVSROOT environment variable is not set." @@ -23,17 +34,16 @@ if [ "x$CVSROOT" = x ]; then echo "plenty of free space." echo "" echo "Please enter the full path for your CVSROOT source repository:" - read CVSROOT + read CVSROOT junk + unset junk remind_cvsroot=yes else - echo "Using $CVSROOT as the source repository." remind_cvsroot=no fi -echo "" # Now, create the $CVSROOT if it is not already there if [ ! -d $CVSROOT ]; then - echo "Hmmm... $CVSROOT does not exist; trying to make it..." + echo "Creating $CVSROOT..." path= for comp in `echo $CVSROOT | sed -e 's,/, ,g'`; do path=$path/$comp @@ -42,7 +52,7 @@ if [ ! -d $CVSROOT ]; then fi done else - echo "Good... $CVSROOT already exists." + true fi # Next, check for $CVSROOT/CVSROOT @@ -52,81 +62,63 @@ if [ ! -d $CVSROOT/CVSROOT ]; then echo "I will rename it to $CVSROOT/CVSROOT for you..." mv $CVSROOT/CVSROOT.adm $CVSROOT/CVSROOT else - echo "Making the $CVSROOT/CVSROOT directory..." + echo "Creating the $CVSROOT/CVSROOT directory..." mkdir $CVSROOT/CVSROOT fi else - echo "Wow!... so does $CVSROOT/CVSROOT." + true fi -echo "" if [ ! -d $CVSROOT/CVSROOT ]; then - echo "You still don't have a $CVSROOT/CVSROOT directory." + echo "Unable to create $CVSROOT/CVSROOT." echo "I give up." exit 1 fi -# Create the special *info files within $CVSROOT/CVSROOT - -# Trump up a simple modules file, if one doesn't exist -if [ -f $CVSROOT/CVSROOT/modules,v ]; then - if [ ! -f $CVSROOT/CVSROOT/modules ]; then - echo "You have a $CVSROOT/CVSROOT/modules,v file," - echo "But no $CVSROOT/CVSROOT/modules file. This is OK." - echo "I'll checkout a fresh copy..." - (cd $CVSROOT/CVSROOT; co -q modules) - echo "" - fi -else - if [ -f $CVSROOT/CVSROOT/modules ]; then - echo "You have a $CVSROOT/CVSROOT/modules file," - echo "But no $CVSROOT/CVSROOT/modules,v file." - echo "I'll create one for you, but otherwise leave it alone..." - else - echo "The $CVSROOT/CVSROOT/modules file does not exist." - echo "Making a simple one for you..." - cat > $CVSROOT/CVSROOT/modules <<"HERE" -# -# The CVS modules file -# -# Three different line formats are valid: -# key -a aliases... -# key [options] directory -# key [options] directory files... -# -# Where "options" are composed of: -# -i prog Run "prog" on "cvs commit" from top-level of module. -# -o prog Run "prog" on "cvs checkout" of module. -# -t prog Run "prog" on "cvs rtag" of module. -# -u prog Run "prog" on "cvs update" of module. -# -d dir Place module in directory "dir" instead of module name. -# -l Top-level directory only -- do not recurse. -# -# And "directory" is a path to a directory relative to $CVSROOT. -# -# The "-a" option specifies an alias. An alias is interpreted as if -# everything on the right of the "-a" had been typed on the command line. -# -# You can encode a module within a module by using the special '&' -# character to interpose another module into the current module. This -# can be useful for creating a module that consists of many directories -# spread out over the entire source repository. -# - -# Convenient aliases -world -a . +# Create the special control files and templates within $CVSROOT/CVSROOT -# CVSROOT support; run mkmodules whenever anything changes. -CVSROOT -i mkmodules CVSROOT -modules -i mkmodules CVSROOT modules -loginfo -i mkmodules CVSROOT loginfo -commitinfo -i mkmodules CVSROOT commitinfo -rcsinfo -i mkmodules CVSROOT rcsinfo -editinfo -i mkmodules CVSROOT editinfo +EXAMPLES="checkoutlist commitinfo cvswrappers editinfo loginfo modules +rcsinfo rcstemplate taginfo wrap unwrap" -# Add other modules here... -HERE +NEWSAMPLE=false +for info in $EXAMPLES; do + if [ -f $CVSROOT/CVSROOT/${info},v ]; then + if [ ! -f $CVSROOT/CVSROOT/$info ]; then + echo "Checking out $CVSROOT/CVSROOT/$info" + echo " from $CVSROOT/CVSROOT/${info},v..." + (cd $CVSROOT/CVSROOT; co -q $info) + fi + else + NEWSAMPLE=true + if [ -f $CVSROOT/CVSROOT/$info ]; then + echo "Checking in $CVSROOT/CVSROOT/${info},v" + echo " from $CVSROOT/CVSROOT/$info..." + else + echo "Creating a sample $CVSROOT/CVSROOT/$info file..." + case $info in + modules) + sed -n -e '/END_REQUIRED_CONTENT/q' \ + -e p $CVSLIB/examples/modules > $CVSROOT/CVSROOT/modules + ;; + rcstemplate) + cp $CVSLIB/examples/$info $CVSROOT/CVSROOT/$info + ;; + wrap|unwrap) + cp $CVSLIB/examples/$info $CVSROOT/CVSROOT/$info + chmod +x $CVSROOT/CVSROOT/$info + ;; + *) + # comment out everything in all the other examples.... + sed -e 's/^\([^#]\)/#\1/' $CVSLIB/examples/$info > $CVSROOT/CVSROOT/$info + ;; + esac + fi + (cd $CVSROOT/CVSROOT; ci -q -u -t/dev/null -m"initial checkin of $info" $info) fi - (cd $CVSROOT/CVSROOT; ci -q -u -t/dev/null -m'initial checkin of modules' modules) +done + +if $NEWSAMPLE ; then + echo "NOTE: You may wish to check out the CVSROOT module and edit any new" + echo "configuration files to match your local requirements." echo "" fi @@ -138,84 +130,18 @@ if grep CVSROOT.adm $CVSROOT/CVSROOT/modules >/dev/null 2>&1; then echo "" fi -# loginfo, like modules, is special-cased -if [ -f $CVSROOT/CVSROOT/loginfo,v ]; then - if [ ! -f $CVSROOT/CVSROOT/loginfo ]; then - echo "You have a $CVSROOT/CVSROOT/loginfo,v file," - echo "But no $CVSROOT/CVSROOT/loginfo file. This is OK." - echo "I'll checkout a fresh copy..." - (cd $CVSROOT/CVSROOT; co -q loginfo) - echo "" - fi -else - if [ -f $CVSROOT/CVSROOT/loginfo ]; then - echo "You have a $CVSROOT/CVSROOT/loginfo file," - echo "But no $CVSROOT/CVSROOT/loginfo,v file." - echo "I'll create one for you, but otherwise leave it alone..." - else - echo "The $CVSROOT/CVSROOT/loginfo file does not exist." - echo "Making a simple one for you..." - # try to find perl; use fancy log script if we can - for perlpath in `echo $PATH | sed -e 's/:/ /g'` x; do - if [ -f $perlpath/perl ]; then - echo "#!$perlpath/perl" > $CVSROOT/CVSROOT/log.pl - cat $CVSLIB/contrib/log.pl >> $CVSROOT/CVSROOT/log.pl - chmod 755 $CVSROOT/CVSROOT/log.pl - cp $CVSLIB/examples/loginfo $CVSROOT/CVSROOT/loginfo - break - fi - done - if [ $perlpath = x ]; then - # we did not find perl anywhere, so make a simple loginfo file - cat > $CVSROOT/CVSROOT/loginfo <<"HERE" -# -# The "loginfo" file is used to control where "cvs commit" log information -# is sent. The first entry on a line is a regular expression which is tested -# against the directory that the change is being made to, relative to the -# $CVSROOT. If a match is found, then the remainder of the line is a filter -# program that should expect log information on its standard input. +# These files are generated from the contrib files. +# FIXME: Is it really wise to overwrite possible local changes like this? +# Normal folks will keep these up to date by modifying the source in +# their CVS module and re-installing CVS, but is everyone OK with that? # -# The filter program may use one and only one % modifier (ala printf). If -# %s is specified in the filter program, a brief title is included (enclosed -# in single quotes) showing the modified file names. # -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. +CONTRIBS="log commit_prep log_accum cln_hist" # -# If the name ALL appears as a regular expression it is always used -# in addition to the first matching regex or DEFAULT. -# -DEFAULT (echo ""; echo $USER; date; cat) >> $CVSROOT/CVSROOT/commitlog -HERE - fi - fi - (cd $CVSROOT/CVSROOT; ci -q -u -t/dev/null -m'initial checkin of loginfo' loginfo) - echo "" -fi - -# The remaining files are generated from the examples files. -for info in commitinfo rcsinfo editinfo; do - if [ -f $CVSROOT/CVSROOT/${info},v ]; then - if [ ! -f $CVSROOT/CVSROOT/$info ]; then - echo "You have a $CVSROOT/CVSROOT/${info},v file," - echo "But no $CVSROOT/CVSROOT/$info file. This is OK." - echo "I'll checkout a fresh copy..." - (cd $CVSROOT/CVSROOT; co -q $info) - echo "" - fi - else - if [ -f $CVSROOT/CVSROOT/$info ]; then - echo "You have a $CVSROOT/CVSROOT/$info file," - echo "But no $CVSROOT/CVSROOT/${info},v file." - echo "I'll create one for you, but otherwise leave it alone..." - else - echo "The $CVSROOT/CVSROOT/$info file does not exist." - echo "Making a simple one for you..." - sed -e 's/^\([^#]\)/#\1/' $CVSLIB/examples/$info > $CVSROOT/CVSROOT/$info - fi - (cd $CVSROOT/CVSROOT; ci -q -u -t/dev/null -m"initial checkin of $info" $info) - echo "" - fi +for contrib in $CONTRIBS; do + echo "Copying the new version of '${contrib}'" + echo " to $CVSROOT/CVSROOT for you..." + cp $CVSLIB/contrib/$contrib $CVSROOT/CVSROOT/$contrib done # XXX - also add a stub for the cvsignore file @@ -224,16 +150,12 @@ done if [ ! -f $CVSROOT/CVSROOT/history ]; then echo "Enabling CVS history logging..." touch $CVSROOT/CVSROOT/history - echo "" + chmod g+w $CVSROOT/CVSROOT/history + echo "(Remove $CVSROOT/CVSROOT/history to disable.)" fi # finish up by running mkmodules echo "All done! Running 'mkmodules' as my final step..." mkmodules $CVSROOT/CVSROOT -# and, if necessary, remind them about setting CVSROOT -if [ $remind_cvsroot = yes ]; then - echo "Remember to set the CVSROOT environment variable in your login script" -fi - exit 0 diff --git a/gnu/usr.bin/cvs/doc/cvs.texinfo b/gnu/usr.bin/cvs/doc/cvs.texinfo index c38166f..ef125f5 100644 --- a/gnu/usr.bin/cvs/doc/cvs.texinfo +++ b/gnu/usr.bin/cvs/doc/cvs.texinfo @@ -1,5 +1,5 @@ \input texinfo @c -*-texinfo-*- -@comment cvs.texinfo,v 1.3 1994/09/15 23:39:26 zoo Exp +@comment cvs.texinfo,v 1.6 1995/10/12 23:39:26 kfogel Exp @comment Documentation for CVS. @comment Copyright (C) 1992, 1993 Signum Support AB @comment Copyright (C) 1993 Free Software Foundation, Inc. @@ -73,12 +73,12 @@ Free Software Foundation instead of in the original English. @sp @center @titlefont{CVS} @sp 2 -@center release 0.9, for @sc{cvs} 1.3+ +@center for @sc{cvs} 1.6+ @comment -release- @sp 3 @center Per Cederqvist @sp 3 -@center last updated 2 Nov 1993 +@center last updated 12 Oct 1995 @comment -date- @comment The following two commands start the copyright page @@ -113,8 +113,9 @@ Free Software Foundation instead of in the original English. @node Top @top -This info manual describes @sc{cvs} and is updated to -release 1.4 or something similar. +This info manual describes how to use and administer +@sc{cvs} and is updated to release 1.4 or something +similar. @end ifinfo @menu @@ -289,8 +290,8 @@ and Michael Brown <@t{brown@@wi.extrel.com}>. @cindex Bugs, known in this manual @cindex Known bugs in this manual -This manual is still very new. Here is a -list of known deficiencies in it: +This manual is known to have room for improvement. +Here is a list of known deficiencies: @itemize @bullet @item @@ -317,7 +318,7 @@ noted by comments in the @file{cvs.texinfo} file. @cindex Errors, reporting (manual) This list is not complete. If you notice any error, omission, or something that is unclear, please send -mail to @t{ceder@@signum.se}. +mail to @t{bug-cvs@@prep.ai.mit.edu}. @end itemize I hope that you will find this manual useful, despite @@ -533,13 +534,20 @@ This section is taken from release 2.3 of the @sc{cvs} @cindex Modules (intro) @cindex Repository (intro) -@sc{cvs} stores all files in a centralized @dfn{repository}: a -directory (such as @file{/usr/local/cvsroot}) which is populated with a -hierarchy of files and directories. +@sc{cvs} stores all files in a centralized +@dfn{repository}: a directory (such as +@file{/usr/local/cvsroot} or +@file{user@@remotehost:/usr/local/cvsroot}) which is +populated with a hierarchy of files and directories. +(@pxref{Remote repositories} for information about +keeping the repository on a remote machine.) Normally, you never access any of the files in the -repository directly. Instead, you use @sc{cvs} commands to -get your own copy of the files, and then work on that copy. +repository directly. Instead, you use @sc{cvs} +commands to get your own copy of the files, and then +work on that copy. When you've finished a set of +changes, you check (or @dfn{commit}) them back into the +repository. The files in the repository are organized in @dfn{modules}. Each module is made up of one or more @@ -894,11 +902,21 @@ Only directories are shown below. +--(other Yoyodyne software) @end example -The @code{$CVSROOT} environment variable should always -be set to an absolute path to the root of the + +There are a couple of different ways to tell @sc{cvs} +where to find the repository. You can name the +repository on the command line explicitly, with the +@code{-d} (for "directory") option: + +@example +cvs -d /usr/local/cvsroot checkout yoyodyne/tc +@end example + + Or you can set the @code{$CVSROOT} environment +variable to an absolute path to the root of the repository, @file{/usr/local/cvsroot} in this example. -With this setup all @code{csh} and @code{tcsh} users -should have this line in their @file{.cshrc} or +To set @code{$CVSROOT}, all @code{csh} and @code{tcsh} +users should have this line in their @file{.cshrc} or @file{.tcshrc} files: @example @@ -914,10 +932,23 @@ CVSROOT=/usr/local/cvsroot export CVSROOT @end example + A repository specified with @code{-d} will +override the @code{$CVSROOT} environment variable. +Once you've checked a working copy out from the +repository, it will remember where its repository is +(the information is recorded in the +@file{CVS/Root} file in the working copy). + +The @code{-d} option and the @file{CVS/Root} file +both override the @code{$CVSROOT} environment variable; +however, @sc{CVS} will complain if the @file{-d} +argument and the @file{CVS/Root} file disagree. + There is nothing magical about the name @file{/usr/local/cvsroot}. You can choose to place the -repository anywhere you like, but @code{$CVSROOT} must -always point to it. +repository anywhere you like. +@xref{Remote repositories} to learn how the repository can be on a +different machine than your working copy of the sources. The repository is split in two parts. @file{$CVSROOT/CVSROOT} contains administrative files for @sc{cvs}. The other directories contain the actual @@ -928,6 +959,7 @@ user-defined modules. * Intro administrative files:: Defining modules * Multiple repositories:: Multiple repositories * Creating a repository:: Creating a repository +* Remote repositories:: Accessing repositories on remote machines @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1089,25 +1121,18 @@ In some situations it is a good idea to have more than one repository, for instance if you have two development groups that work on separate projects without sharing any code. All you have to do to have -several repositories is to set @code{$CVSROOT} to the -repository you want to use at the moment. - -There are disadvantages to having more than one -repository. In @sc{cvs} 1.3 you @emph{must} make sure -that @code{$CVSROOT} always points to the correct -repository. If the same filename is used in two -repositories, and you mix up the setting of -@code{$CVSROOT}, you might lose data. @sc{cvs} 1.4 -solves this problem by saving the repository -information in the local @file{CVS} administration -files. If you try to use the wrong repository, -@sc{cvs} will warn you of the attempt and then exit. +several repositories is to specify the appropriate +repository, using the @code{CVSROOT} environment +variable, the @samp{-d} option to @sc{cvs}, or (once +you have checked out a working directories) by +simply allowing @sc{cvs} to use the repository that was +used to check out the working directory (@pxref{Repository}). Notwithstanding, it can be confusing to have two or more repositories. -All examples in this manual assume that you have a -single repository. +None of the examples in this manual show multiple +repositories. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Creating a repository @@ -1117,6 +1142,118 @@ single repository. See the instructions in the @file{INSTALL} file in the @sc{cvs} distribution. +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Remote repositories +@section Remote repositories +@cindex Repositories, remote +@cindex Remote repositories +@cindex Client/Server Operation + +The repository and your working copy of the sources can +be on different machines. To access a remote +repository, use the following format for its name: + +@example + user@@hostname:/path/to/repository +@end example + +(The @file{user@@} can be omitted if it's the same on +both the local and remote hosts.) + +The details of exactly what needs to be set up depends +on how you are connecting to the server. + +@menu +* Connecting via rsh:: Using the rsh program to connect +* Kerberos authenticated:: Direct connections with kerberos +@end menu + +@node Connecting via rsh +@subsection Connecting with rsh + +@cindex rsh +CVS uses the @file{rsh} protocol to perform these +operations, so the remote user host needs to have a +@file{.rhosts} file which grants access to the local +user. + +For example, suppose you are the user @file{mozart} on +the local machine @file{anklet.grunge.com}, and the +server machine is @file{chainsaw.brickyard.com}. On +chainsaw, put the following line into the file +@file{.rhosts} in @file{bach}'s home directory: + +@example +anklet.grunge.com mozart +@end example + +Then test that rsh is working with + +@example +rsh -l bach chainsaw.brickyard.com echo $PATH +@end example + +@cindex CVS_SERVER +Next you have to make sure that rsh will be able to +find the server. Make sure that the path which rsh +printed in the above example includes the directory +containing a program named @code{cvs} which is the +server. You need to set the path in @file{.bashrc}, +@file{.cshrc}, etc., not @file{.login} or +@file{.profile}. Alternately, you can set the +environment variable @code{CVS_SERVER} on the client +machine to the filename of the server you want to use, +for example @file{/usr/local/bin/cvs-1.6}. + +There is no need to edit @code{inetd.conf} or start a +@sc{cvs} server daemon. + +Continuing our example, supposing you want to access +the module @file{foo} in the repository +@file{/usr/local/cvsroot/}, on machine +@file{chainsaw.brickyard.com}, you are ready to go: + +@example +cvs -d bach@@chainsaw.brickyard.com:/user/local/cvsroot checkout foo +@end example + +@node Kerberos authenticated +@subsection Direct connection with kerberos + +@cindex kerberos +The main disadvantage of using rsh is that all the data +needs to pass through additional programs, so it may be +slower. So if you have kerberos installed you can +connect via a direct @sc{tcp} connection, +authenticating with kerberos (note that the data +transmitted is @emph{not} encrypted). + +To do this, @sc{cvs} needs to be compiled with kerberos +support; when configuring @sc{cvs} it tries to detect +whether kerberos is present or you can use the +@file{--with-krb4} flag to configure. + +@cindex CVS_CLIENT_PORT +You need to edit @code{inetd.conf} on the server +machine to run @code{cvs kserver}. The client uses +port 1999 by default; if you want to use another port +specify it in the @code{CVS_CLIENT_PORT} environment +variable on the client. Set @code{CVS_CLIENT_PORT} to +@samp{-1} to force an rsh connection. + +@cindex kinit +When you want to use @sc{cvs}, get a ticket in the +usual way (generally @code{kinit}); it must be a ticket +which allows you to log into the server machine. Then +you are ready to go: + +@example +cvs -d chainsaw.brickyard.com:/user/local/cvsroot checkout foo +@end example + +If @sc{cvs} fails to connect, it will fall back to +trying rsh. + @c --------------------------------------------------------------------- @node Starting a new project @chapter Starting a project with CVS @@ -1128,7 +1265,7 @@ Since @sc{cvs} 1.x is bad at renaming files and moving them between directories, the first thing you do when you start a new project should be to think through your file organization. It is not impossible---just -awkward---to rename or move files in @sc{cvs} 1.x. +awkward---to rename or move files. @xref{Moving files}. What to do next depends on the situation at hand. @@ -1243,10 +1380,10 @@ directories inside @samp{$CVSROOT} are reasonable. @cindex Module, defining @cindex Modules file, changing -The next step is to define the module in the @file{modules} file. Some -@sc{cvs} commands work without this step, but others (most notably -@code{release}) require that all modules are properly defined in the -@file{modules} file. +The next step is to define the module in the +@file{modules} file. This is not strictly necessary, +but modules can be convenient in grouping together +related files and directories. In simple cases these steps are sufficient to define a module. @@ -1329,6 +1466,7 @@ automating that process. @xref{Informing others}. * Updating a file:: Bringing a file up-to-date * Conflicts example:: An informative example * Informing others:: To cooperate you must inform +* Concurrency:: Simultaneous repository access @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1590,6 +1728,65 @@ message to all developers, or post a message to a local newsgroup. @c -- More text would be nice here. +@node Concurrency +@section Several developers simultaneously attempting to run CVS + +@cindex locks, cvs +If several developers try to run @sc{cvs} at the same +time, one may get the following message: + +@example +[11:43:23] waiting for bach's lock in /usr/local/cvsroot/foo +@end example + +@sc{cvs} will try again every 30 seconds, and either +continue with the operation or print the message again, +if it still needs to wait. If a lock seems to stick +around for an undue amount of time, find the person +holding the lock and ask them about the cvs command +they are running. If they aren't running a cvs +command, look for and remove files starting with +@file{#cvs.tfl}, @file{#cvs.rfl}, or @file{#cvs.wfl} +from the repository. + +Note that these locks are to protect @sc{cvs}'s +internal data structures and have no relationship to +the word @dfn{lock} in the sense used by @sc{rcs}--a +way to prevent other developers from working on a +particular file. + +Any number of people can be reading from a given +repository at a time; only when someone is writing do +the locks prevent other people from reading or writing. + +One might hope for the following property + +@example +If someone commits some changes in one cvs command, +then an update by someone else will either get all the +changes, or none of them. +@end example + +but @sc{cvs} does @emph{not} have this property. For +example, given the files + +@example +a/one.c +a/two.c +b/three.c +b/four.c +@end example + +if someone runs + +@example +cvs ci a/two.c b/three.c +@end example + +and someone else runs @code{cvs update}, the person +running update might get only the change to +@file{b/three.c} and not the change to @file{a/two.c}. + @c --------------------------------------------------------------------- @node Branches @chapter Branches @@ -1650,9 +1847,7 @@ rcsutil.c 5.10 You can use the @code{tag} command to give a symbolic name to a certain revision of a file. You can use the @samp{-v} flag to the @code{status} command to see all tags that a file has, and -which revision numbers they represent. (The output of @code{status} -unfortunately uses the word ``version'' instead of ``revision''.) -@c -- Is this fixed in CVS 1.3.1? +which revision numbers they represent. @cindex Adding a tag @cindex tag, example @@ -1920,6 +2115,7 @@ copy the changes onto another branch. @menu * Merging a branch:: Merging an entire branch +* Merging more than once:: Merging from a branch several times * Merging two revisions:: Merging differences between two revisions @end menu @@ -1936,10 +2132,7 @@ point where the branch forked and newest revision on that branch (into your working copy). @cindex Join -The @samp{-j} stands for ``join''. In previous -versions of @sc{cvs} there was a special command, -@samp{cvs join}, that was used to merge changes between -branches. +The @samp{-j} stands for ``join''. @cindex Branch merge example @cindex Example, branch merge @@ -1947,14 +2140,14 @@ branches. Consider this revision tree: @example -+-----+ +-----+ +-----+ +-----+ +-----+ -! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk -+-----+ +-----+ +-----+ +-----+ +-----+ ++-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ ! ! - ! +---------+ +---------+ +---------+ -Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 ! - +---------+ +---------+ +---------+ + ! +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 ! + +---------+ +---------+ @end example @noindent @@ -1963,14 +2156,14 @@ following example assumes that the module @samp{mod} contains only one file, @file{m.c}. @example -$ cvs checkout mod # @r{Retrieve the latest revision, 1.5} +$ cvs checkout mod # @r{Retrieve the latest revision, 1.4} $ cvs update -j R1fix m.c # @r{Merge all changes made on the branch,} # @r{i.e. the changes between revision 1.2} - # @r{and 1.2.2.3, into your working copy} + # @r{and 1.2.2.2, into your working copy} # @r{of the file.} -$ cvs commit -m "Included R1fix" # @r{Create revision 1.6.} +$ cvs commit -m "Included R1fix" # @r{Create revision 1.5.} @end example A conflict can result from a merge operation. If that @@ -1985,6 +2178,74 @@ $ cvs checkout -j R1fix mod $ cvs commit -m "Included R1fix" @end example +@node Merging more than once +@section Merging from a branch several times + +Continuing our example, the revision tree now looks +like this: + +@example ++-----+ +-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ +-----+ + ! * + ! * + ! +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 ! + +---------+ +---------+ +@end example + +where the starred line represents the merge from the +@samp{R1fix} branch to the main trunk, as just +discussed. + +Now suppose that development continues on the +@samp{R1fix} branch: + +@example ++-----+ +-----+ +-----+ +-----+ +-----+ +! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk ++-----+ +-----+ +-----+ +-----+ +-----+ + ! * + ! * + ! +---------+ +---------+ +---------+ +Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 ! + +---------+ +---------+ +---------+ +@end example + +and then you want to merge those new changes onto the +main trunk. If you just use the @code{cvs update -j +R1fix m.c} command again, @sc{cvs} will attempt to +merge again the changes which you have already merged, +which can have undesirable side effects. + +So instead you need to specify that you only want to +merge the changes on the branch which have not yet been +merged into the trunk. To do that you specify two +@samp{-j} options, and @sc{cvs} merges the changes from +the first revision to the second revision. For +example, in this case the simplest way would be + +@example +cvs update -j 1.2.2.2 -j R1fix m.c # @r{Merge changes from 1.2.2.2 to the} + # @r{head of the R1fix branch} +@end example + +The problem with this is that you need to specify the +1.2.2.2 revision manually. A slightly better approach +might be to use the date the last merge was done: + +@example +cvs update -j R1fix:yesterday -j R1fix m.c +@end example + +Better yet, tag the R1fix branch after every merge into +the trunk, and then use that tag for subsequent merges: + +@example +cvs update -j merged_from_R1fix_to_trunk -j R1fix m.c +@end example + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Merging two revisions @section Merging differences between any two revisions @@ -2006,12 +2267,12 @@ $ cvs update -j 1.5 -j 1.3 backend.c will @emph{remove} all changes made between revision 1.3 and 1.5. Note the order of the revisions! -If you try to use this option with the @code{checkout} -command, remember that the numeric revisions will +If you try to use this option when operating on +multiple files, remember that the numeric revisions will probably be very different between the various files that make up a module. You almost always use symbolic -tags rather than revision numbers with the -@code{checkout} command. +tags rather than revision numbers when operating on +multiple files. @c --------------------------------------------------------------------- @node Recursive behavior @@ -2175,31 +2436,6 @@ certain revision via e.g. @samp{cvs checkout -r @file{Attic} and include any files that contain the specified tag. -This method is simple and works quite well, but it has -some known deficiencies: - -@itemize @bullet -@item -If you remove the file @file{foo.c}, you cannot later -create a new file called @file{foo.c} unless you -manually remove the file @file{Attic/foo.c,v} inside -the repository. On the other hand, if you remove -@file{Attic/foo.c,v} you will of course not be able to -retrieve any revision of the old file @file{foo.c}. - -@item -If the file @file{bar.c} is present in release 1.0 of a -product, and was accidentally removed in release 1.1, -you cannot easily resurrect it to release 1.2. You -have to move the file out of the @file{Attic} manually -inside the repository. (Do a @samp{mv Attic/@var{file} -@var{file}}). -@end itemize - -There is a design for a @dfn{rename database} that will -solve these problems and many others, but it is not yet -implemented. - @c --------------------------------------------------------------------- @node Tracking sources @chapter Tracking third-party sources @@ -2302,9 +2538,10 @@ since yesterday into the working copy. If any conflicts arise during the merge they should be resolved in the normal way (@pxref{Conflicts example}). Then, the modified files may be committed. -@sc{cvs} assumes that you do not import more than one -release of a product per day. If you do, you can always -use something like this instead: +Using a date, as suggested above, assumes that you do +not import more than one release of a product per +day. If you do, you can always use something like this +instead: @example $ cvs checkout -jWDIFF_0_04 -jWDIFF_0_05 wdiff @@ -2320,33 +2557,29 @@ In this case, the two above commands are equivalent. @cindex Renaming files @cindex Files, moving -One of the biggest design flaws with the current release of @sc{cvs} is that -it is very difficult to move a file to a different directory or -rename it. There are workarounds, and they all have their strong and weak -points. (Moving or renaming a directory is even harder. @xref{Moving -directories}). +Moving files to a different directory or renaming them +is not difficult, but some of the ways in which this +works may be non-obvious. (Moving or renaming a +directory is even harder. @xref{Moving directories}). The examples below assume that the file @var{old} is renamed to -@var{new}. Both files reside in the same module, -@var{module}, but not necessarily in the same directory. -The relative path to the module inside the repository is assumed -to be @var{module}. +@var{new}. @menu -* Outside:: Moving outside the repository -* Inside:: Moving the history file -* Rename by copying:: +* Outside:: The normal way to Rename +* Inside:: A tricky, alternative way +* Rename by copying:: Another tricky, alternative way @end menu @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Outside -@section Moving outside the repository +@section The Normal way to Rename -One way to move the file is to copy @var{old} to -@var{new}, and then issue the normal @sc{cvs} commands to -remove @var{old} from the repository, and add @var{new} -to it. (Both @var{old} and @var{new} could contain -relative paths inside the module). +The normal way to move a file is to copy @var{old} to +@var{new}, and then issue the normal @sc{cvs} commands +to remove @var{old} from the repository, and add +@var{new} to it. (Both @var{old} and @var{new} could +contain relative paths, for example @file{foo/bar.c}). @example $ mv @var{old} @var{new} @@ -2355,25 +2588,17 @@ $ cvs add @var{new} $ cvs commit -m "Renamed @var{old} to @var{new}" @var{old} @var{new} @end example -@noindent -Advantages: +This is the simplest way to move a file, it is not +error-prone, and it preserves the history of what was +done. Note that to access the history of the file you +must specify the old or the new name, depending on what +portion of the history you are accessing. For example, +@code{cvs log @var{old}} will give the log up until the +time of the rename. -@itemize @bullet -@item -Checking out old revisions works correctly. -@end itemize - -@noindent -Disadvantages: - -@itemize @bullet -@item -You cannot easily see the history of the file across the rename. -@item -Unless you use the @samp{-r rev} (@pxref{commit -options}) flag when @var{new} is committed its revision -numbers will start at 1.0 again. -@end itemize +When @var{new} is committed its revision numbers will +start at 1.0 again, so if that bothers you, use the +@samp{-r rev} option to commit (@pxref{commit options}) @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node Inside @@ -2384,7 +2609,7 @@ files inside the repository. Read this entire section before trying it out! @example -$ cd $CVSROOT/module +$ cd $CVSROOT/@var{module} $ mv @var{old},v @var{new},v @end example @@ -2421,15 +2646,15 @@ commands while you move it. @node Rename by copying @section Copying the history file -This is probably the best way to do the renaming. It is -safe, but not without drawbacks. +This way also involves direct modifications to the +repository. It is safe, but not without drawbacks. @example # @r{Copy the @sc{rcs} file inside the repository} -$ cd $CVSROOT/module +$ cd $CVSROOT/@var{module} $ cp @var{old},v @var{new},v # @r{Remove the old file} -$ cd ~/module +$ cd ~/@var{module} $ rm @var{old} $ cvs remove @var{old} $ cvs commit @var{old} @@ -2449,6 +2674,8 @@ Advantages: @itemize @bullet @item +@c FIXME: Is this true about -D now that we have death +@c support? See 5B.3 in the FAQ. Checking out old revisions works correctly, as long as you use @samp{-r@var{tag}} and not @samp{-D@var{date}} to retrieve the revisions. @@ -3014,15 +3241,13 @@ Arguments to the commands. @end table There is unfortunately some confusion between -@code{cvs_options} and @code{command_options}. For -example, @samp{-q} can often (but not always) be given -as both a @code{cvs_option} and a -@code{command_option}. @samp{-l}, when given as a -@code{cvs_option}, only affects some of the commands. -When it is given as a @code{command_option} is has a -different meaning, and is accepted by more commands. -In other words, do not take the above categorization -too seriously. Look at the documentation instead. +@code{cvs_options} and @code{command_options}. +@samp{-l}, when given as a @code{cvs_option}, only +affects some of the commands. When it is given as a +@code{command_option} is has a different meaning, and +is accepted by more commands. In other words, do not +take the above categorization too seriously. Look at +the documentation instead. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node ~/.cvsrc @@ -3033,12 +3258,11 @@ too seriously. Look at the documentation instead. There are some @code{command_options} that are used so often that you might have set up an alias or some other means to make sure you always specify that option. One -example @footnote{being the one that drove the -implementation of the .cvsrc support} is that many -people find the default output of the @samp{diff} -command to be very hard to read, and that either -context diffs or unidiffs are much easier to -understand. +example (the one that drove the implementation of the +.cvsrc support, actually) is that many people find the +default output of the @samp{diff} command to be very +hard to read, and that either context diffs or unidiffs +are much easier to understand. The @file{~/.cvsrc} file is a way that you can add default options to @code{cvs_commands} within cvs, @@ -3053,9 +3277,10 @@ added to the command arguments @emph{before} any options from the command line. If a command has two names (e.g., @code{checkout} and -@code{co}), only the name used on the command line will -be used to match against the file. So if this is the -contents of the user's @file{~/.cvsrc} file: +@code{co}), the official name, not necessarily the one +used on the command line, will be used to match against +the file. So if this is the contents of the user's +@file{~/.cvsrc} file: @example log -N @@ -3065,17 +3290,17 @@ co -P @end example @noindent -the command @samp{cvs checkout foo} would not have the -@samp{-P} option added to the arguments, while -@samp{cvs co foo} would. +the command @samp{cvs checkout foo} would have the +@samp{-P} option added to the arguments, as well as +@samp{cvs co foo}. With the example file above, the output from @samp{cvs diff foobar} will be in unidiff format. @samp{cvs diff --c foobar} will provide context diffs, as usual. Since -@code{diff} doesn't have an option to specify use of -the "old" format, you would need to use the @samp{-f} -option to @samp{cvs} to turn off use of the -@file{~/.cvsrc} options. +-c foobar} will provide context diffs, as usual. +Getting "old" format diffs would be slightly more +complicated, because @code{diff} doesn't have an option +to specify use of the "old" format, so you would need +@samp{cvs -f diff foobar}. @@ -3103,8 +3328,7 @@ specified as an absolute pathname. @item -d @var{cvs_root_directory} Use @var{cvs_root_directory} as the root directory pathname of the repository. Overrides the setting of -the @code{$CVSROOT} environment variable. This parameter -should be specified as an absolute pathname. +the @code{$CVSROOT} environment variable. @xref{Repository}. @cindex EDITOR, overriding @cindex Overriding EDITOR @@ -3330,18 +3554,13 @@ Pipe the files retrieved from the repository to standard output, rather than writing them in the current directory. Available with the @code{checkout} and @code{update} commands. -@item -Q -Cause the command to be really quiet; the command will only -generate output for serious problems. Available with the following -commands: @code{checkout}, @code{import}, @code{export}, -@code{rdiff}, @code{rtag}, @code{tag}, and @code{update}. - -@item -q -Cause the command to be somewhat quiet; informational messages, -such as reports of recursion through subdirectories, are -suppressed. Available with the following commands: -@code{checkout}, @code{import}, @code{export}, @code{rtag}, -@code{tag}, and @code{update}. +@item -W +Specify file names that should be filtered. You can +use this option repeatedly. The spec can be a file +name pattern of the same type that you can specify in +the @file{.cvswrappers} file. +Avaliable with the following commands: @code{import}, +and @code{update}. @item -r @var{tag} Use the revision specified by the @var{tag} argument instead of the @@ -3358,9 +3577,10 @@ future update commands, until you specify otherwise. The tag can be either a symbolic or numeric tag. @xref{Tags}. -Specifying the @samp{-q} option along with the @samp{-r} option is often -useful, to suppress the warning messages when the @sc{rcs} history file does -not contain the specified tag. +Specifying the @samp{-q} global option along with the +@samp{-r} command option is often useful, to suppress +the warning messages when the @sc{rcs} history file +does not contain the specified tag. @strong{Warning:} this is not the same as the overall `cvs -r' option, which you can specify to the left of a cvs command! @@ -3517,6 +3737,12 @@ all its options and arguments to the @code{rcs} command; it does no filtering or other processing. This command @emph{does} work recursively, however, so extreme care should be used. +If there is a group whose name matches a compiled in +value which defaults to @code{cvsadmin}, only members +of that group can use @code{cvs admin}. To disallow +@code{cvs admin} for all users, create a group with no +users in it. + @menu * admin options:: admin options * admin examples:: admin examples @@ -3639,7 +3865,7 @@ working files. @cindex Outdating revisions @cindex Saving space @item -o@var{range} -Useful, but dangerous, with @sc{cvs} (see below). +Potentially useful, but dangerous, with @sc{cvs} (see below). Deletes (@dfn{outdates}) the revisions given by @var{range}. A range consisting of a single revision number means that revision. A range consisting of a @@ -3660,9 +3886,9 @@ cannot be specified symbolically if it is a branch. Make sure that no-one has checked out a copy of the revision you outdate. Strange things will happen if he starts to edit it and tries to check it back in. For -this reason, you should never use this option to take -back a bogus commit unless you work alone. Instead, -you should fix the file and commit a new revision. +this reason, this option is not a good way to take back +a bogus commit; commit a new revision undoing the bogus +change instead (@pxref{Merging two revisions}). @item -q Run quietly; do not print diagnostics. @@ -3861,7 +4087,7 @@ sub-directory may have a different name, but you can be sure that it will be a sub-directory, and that @code{checkout} will show the relative path leading to each file as it is extracted into your private work -area (unless you specify the @samp{-Q} option). +area (unless you specify the @samp{-Q} global option). Running @code{checkout} on a directory that was already built by a prior @code{checkout} is also permitted, and @@ -3915,12 +4141,6 @@ Prune empty directories. @item -p Pipe files to the standard output. -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -r @var{tag} Use revision @var{tag}. This option is sticky, and implies @samp{-P}. @end table @@ -3950,28 +4170,26 @@ also use @samp{-N}, the paths created under @var{dir} will be as short as possible. @item -j @var{tag} -Merge the changes made between the resulting revision -and the revision that it is based on (e.g., if -@var{tag} refers to a branch, @sc{cvs} will merge all -changes made on that branch into your working file). - -With two @samp{-j @var{tag}} options, @sc{cvs} will merge in the -changes between the two respective revisions. This can -be used to undo changes made between two revisions -(@pxref{Merging two revisions}) in your working copy, -or to move changes between different branches. +With two @samp{-j} options, merge changes from the +revision specified with the first @samp{-j} option to +the revision specified with the second @samp{j} option, +into the working directory. + +With one @samp{-j} option, merge changes from the +ancestor revision to the revision specified with the +@samp{-j} option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the @samp{-j} option. In addition, each -j option can contain an optional date specification which, when used with branches, can limit the chosen revision to one within a specific date. An optional date is specified by adding a colon -(:) to the tag. An example might be what @code{import} -tells you to do when you have just imported sources -that have conflicts with local changes: +(:) to the tag: +@samp{-j@var{Symbolic_Tag}:@var{Date_Specifier}}. -@example -$ cvs checkout -jTAG:yesterday -jTAG module -@end example +@xref{Merging}. @item -N Only useful together with @samp{-d @var{dir}}. With this @@ -4302,12 +4520,6 @@ co(1). @item -l Local; run only in current working directory. -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -R Examine directories recursively. This option is on by default. @@ -4381,7 +4593,7 @@ $ cvs diff -u | less @itemize @bullet @item -Synopsis: export [-flNnQq] -r rev|-D date [-d dir] module@dots{} +Synopsis: export [-flNn] -r rev|-D date [-d dir] module@dots{} @item Requires: repository. @item @@ -4432,12 +4644,6 @@ Local; run only in current working directory. @item -n Do not run any checkout program. -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -R Export directories recursively. This is on by default. @@ -4693,6 +4899,12 @@ If the file @file{$CVSROOT/CVSROOT/cvsignore} exists, any files whose names match the specifications in that file will also be ignored. +If the file @file{$CVSROOT/CVSROOT/cvswrappers} exists, +any file whose names match the specifications in that +file will be treated as packages and the appropriate +filtering will be performed on the file/directory +before being imported, @xref{Wrappers}. + The outside source is saved in a first-level @sc{rcs} branch, by default 1.1.1. Updates are leaves of this branch; for example, files from the first imported @@ -4716,20 +4928,13 @@ the leaves created each time you execute @code{import}. @node import options @appendixsubsec import options -These standard options are supported by @code{import} -(@pxref{Common options}, for a complete description of -them): +This standard option is supported by @code{import} +(@pxref{Common options}, for a complete description): @table @code @item -m @var{message} Use @var{message} as log information, instead of invoking an editor. - -@item -Q -Really quiet. - -@item -q -Somewhat quiet. @end table There are three additional special options. @@ -4766,6 +4971,14 @@ default), specify `-I !'. that you can specify in the @file{.cvsignore} file. @xref{cvsignore}. @c -- Is this really true? + +@item -W @var{spec} +Specify file names that should be filtered during +import. You can use this option repeatedly. + +@var{spec} can be a file name pattern of the same type +that you can specify in the @file{.cvswrappers} +file. @xref{Wrappers}. @end table @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -5005,12 +5218,6 @@ recent revision (instead of ignoring the file). @item -l Local; don't descend subdirectories. -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -r @var{tag} Use revision @var{tag}. @end table @@ -5083,7 +5290,7 @@ File bar.h,v changed from revision 1.29.2.1 to 1.2 @itemize @bullet @item -release [-dQq] modules@dots{} +release [-d] modules@dots{} @item Requires: Working directory. @item @@ -5121,18 +5328,7 @@ history log. @node release options @appendixsubsec release options -Only these standard options are supported by @code{release}. - -@table @code -@item -Q -Really quiet. - -@item -q -Somewhat quiet. -@end table - -In addition to the above, it supports one additional -flag. +The @code{release} command supports one command option: @table @code @item -d @@ -5197,7 +5393,8 @@ Note that no warning message like this is printed for spurious directories that @sc{cvs} encounters. The directory, and all its contents, are silently ignored. -@c FIXME -- this should be fixed for CVS 1.4 +@c FIXME -- CVS should be fixed to print "? foo" for +@c such spurious directories @end table @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -5327,7 +5524,7 @@ U oj.c @itemize @bullet @item -rtag [-falnRQq] [-b] [-d] [-r tag | -Ddate] symbolic_tag modules@dots{} +rtag [-falnR] [-b] [-d] [-r tag | -Ddate] symbolic_tag modules@dots{} @item Requires: repository. @item @@ -5382,12 +5579,6 @@ Do not run any tag program that was specified with the @samp{-t} flag inside the @file{modules} file. (@pxref{modules}). -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -R Commit directories recursively. This is on by default. @@ -5439,7 +5630,7 @@ module). @itemize @bullet @item -status [-lR] [-v] [-Q] [files@dots{}] +status [-lR] [-v] [files@dots{}] @item Requires: working directory, repository. @item @@ -5473,10 +5664,6 @@ Local; run only in current working directory. @item -R Commit directories recursively. This is on by default. - -@item -Q -Really quiet. Do not print empty sticky parts. This -option is not available in @sc{cvs} 1.3. @end table There is one additional option: @@ -5507,7 +5694,7 @@ to. @itemize @bullet @item -tag [-lQqR] [-b] [-d] symbolic_tag [files@dots{}] +tag [-lR] [-b] [-d] symbolic_tag [files@dots{}] @item Requires: working directory, repository. @item @@ -5568,12 +5755,6 @@ Local; run only in current working directory. @item -R Commit directories recursively. This is on by default. - -@item -Q -Really quiet. - -@item -q -Somewhat quiet. @end table Two special options are available: @@ -5611,7 +5792,7 @@ be valuable. @itemize @bullet @item -update [-AdflPpQqR] [-d] [-r tag|-D date] files@dots{} +update [-AdflPpR] [-d] [-r tag|-D date] files@dots{} @item Requires: repository, working directory. @item @@ -5660,7 +5841,7 @@ this file in this working directory will use the same to see the sticky options. @xref{status}. @item -l -Local; run only in current working directory. +Local; run only in current working directory. @xref{Recursive behavior}. @item -P Prune empty directories. @@ -5668,14 +5849,9 @@ Prune empty directories. @item -p Pipe files to the standard output. -@item -Q -Really quiet. - -@item -q -Somewhat quiet. - @item -R -Commit directories recursively. This is on by default. +Operate recursively. This is on by default. +@xref{Recursive behavior}. @item -r tag Retrieve revision @var{tag}. This option is sticky, @@ -5734,22 +5910,26 @@ Use @samp{-I !} to avoid ignoring any files at all. @xref{cvsignore}, for other ways to make @sc{cvs} ignore some files. -@item -j@var{branch} -Merge the changes made between the resulting revision -and the revision that it is based on (e.g., if the tag -refers to a branch, @sc{cvs} will merge all changes made in -that branch into your working file). +@item -W@var{spec} +Specify file names that should be filtered during +update. You can use this option repeatedly. -With two @samp{-j} options, @sc{cvs} will merge in the -changes between the two respective revisions. This can -be used to remove a certain delta from your working -file; if the file @file{foo.c} is based on -revision 1.6 and you want to remove the changes made -between 1.3 and 1.5, you might do: +@var{spec} can be a file name pattern of the same type +that you can specify in the @file{.cvswrappers} +file. @xref{Wrappers}. -@example -$ cvs update -j1.5 -j1.3 foo.c # @r{note the order@dots{}} -@end example +@item -j@var{revision} +With two @samp{-j} options, merge changes from the +revision specified with the first @samp{-j} option to +the revision specified with the second @samp{j} option, +into the working directory. + +With one @samp{-j} option, merge changes from the +ancestor revision to the revision specified with the +@samp{-j} option, into the working directory. The +ancestor revision is the common ancestor of the +revision which the working directory is based on, and +the revision specified in the @samp{-j} option. In addition, each -j option can contain an optional date specification which, when used with branches, can @@ -5757,6 +5937,9 @@ limit the chosen revision to one within a specific date. An optional date is specified by adding a colon (:) to the tag: @samp{-j@var{Symbolic_Tag}:@var{Date_Specifier}}. + +@xref{Merging}. + @end table @c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -5862,6 +6045,7 @@ file, which defines the modules inside the repository. @menu * modules:: Defining modules +* Wrappers:: Treat directories as files * commit files:: The commit support files * commitinfo:: Pre-commit checking * editinfo:: Specifying how log messages are created @@ -5977,6 +6161,12 @@ module, in your working directory. Name the working directory something other than the module name. +@cindex Export program +@item -e @var{prog} +Specify a program @var{prog} to run whenever files in a +module are exported. @var{prog} runs with a single +argument, the module name. + @cindex Checkin program @item -i @var{prog} Specify a program @var{prog} to run whenever files in a @@ -6022,6 +6212,83 @@ this module. @end table @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@node Wrappers +@appendixsec The cvswrappers file +@cindex cvswrappers (admin file) +@cindex CVSWRAPPERS, environment variable +@cindex Wrappers + +Wrappers are essentially +directories that are to be treated as "files." This +package allows such wrappers to be "processed" on the +way in and out of CVS. The intended use is to wrap up +a wrapper into a single tar, such that that tar can be +treated as a single binary file in CVS. Apparently +this is particularly useful on NEXTSTEP. To solve +the problem effectively, it was also necessary to be +able to prevent rcsmerge application at appropriate +times. + +The file @file{cvswrappers} defines the script that will be +run on a file when its name matches a regular +expresion. There are two scripts that can be run on a +file or directory. +@c FIXME: Is this talking about comb and uncom? If so, +@c mention them by name +A script to filter the directory/file before it gets +checked in and another that is run when the +file/directory gets checked out. + +The @file{cvswrappers} also specifies the merge +methodology that should be used when the file is +updated, that is should a MERGE or a straight COPY of +the diferences be used when checking into the +repository. + +The basic format of the file @file{cvswrappers} is given as +such: + +@example +wildcard [option value][option value]... + +where option is one of +-f from cvs filter value: path tofilter +-t to cvs filter value: path to filter +-m update methodology value: MERGE or COPY + +and value is a single-quote delimited value. +@end example + +@example +*.nib -f 'uncom %s' -t 'comb %s %s' -m 'COPY' +*.rtfd -f 'uncom %s' -t 'comb %s %s' -m 'COPY' +@end example + +@noindent +The above example of a @file{cvswrappers} file +states that all files/directories that end with a @code{.nib} +should be filtered with the @file{comb} program before +checking the file into the repository. The file should +be filtered though the @file{uncom} program when the +file is checked out of the repository. The +@file{cvswrappers} file also states that a @code{COPY} +methodology should be used when updating the files in +the repository (that is no merging should be performed). + +@noindent +The @file{comb} filter is called with two arguments, +the first is the name of the file/directory to filter +and the second is the pathname to where the resulting +filtered file should be placed. + +@noindent +The @file{uncom} filter is called with one argument, +which is the name of the file to filter from. The end +result of the @file{uncom} filter will be a +file/directory in the users current working directory, +that represents the source before being filtered. + +@c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node commit files @appendixsec The commit support files @cindex Commit files @@ -6094,9 +6361,9 @@ character @samp{#} are treated as comments. Long lines unfortunately can @emph{not} be broken in two parts in any way. -Whenever one of the regular expressions matches a -directory name in the repository, the rest of the line -is used. +The first regular expression that matches the current +directory name in the repository is used. The rest of the line +is used as a file name or command-line as appropriate. @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node commitinfo @@ -6122,10 +6389,10 @@ repository is appended to the template, followed by the file names of any files involved in the commit (added, removed, and modified files). -All lines with a regular expression matching the -relative path to the module will be used. If any of -the commands return a non-zero exit status the commit -will be aborted. +The first line with a regular expression matching the +relative path to the module will be used. If the +command returns a non-zero exit status the commit will +be aborted. @cindex DEFAULT in commitinfo If the repository name does not match any of the @@ -6133,9 +6400,14 @@ regular expressions in this file, the @samp{DEFAULT} line is used, if it is specified. @cindex ALL in commitinfo -If the name @samp{ALL} appears as a regular expression -it is always used in addition to any matching regular -expression or @samp{DEFAULT}. +All occurances of the name @samp{ALL} appearing as a +regular expression are used in addition to the first +matching regular expression or the name @samp{DEFAULT}. + +Note: when @sc{CVS} is accessing a remote repository, +@file{commitinfo} will be run on the @emph{remote} +(i.e., server) side, not the client side (@pxref{Remote +repositories}). @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node editinfo @@ -6166,20 +6438,30 @@ with the @file{rcsinfo} file, which can be used to specify a log message template. Each line in the @file{editinfo} file consists of a -regular expression and a command-line template. The template must -include a program name, and can include any number of -arguments. The full path to the current log message -template file is appended to the template. - -One thing that should be noted is that the ALL keyword -is not supported. If more than one matching line is -found, the last one is used. This can be useful for -specifying a default edit script in a module, and then -overriding it in a subdirectory. +regular expression and a command-line template. The +template must include a program name, and can include +any number of arguments. The full path to the current +log message template file is appended to the template. + +One thing that should be noted is that the @samp{ALL} +keyword is not supported. If more than one matching +line is found, the first one is used. This can be +useful for specifying a default edit script in a +module, and then overriding it in a subdirectory. + +@cindex DEFAULT in editinfo +If the repository name does not match any of the +regular expressions in this file, the @samp{DEFAULT} +line is used, if it is specified. If the edit script exits with a non-zero exit status, the commit is aborted. +Note: when @sc{CVS} is accessing a remote repository, +@file{editinfo} will be run on the @emph{remote} +(i.e., server) side, not the client side (@pxref{Remote +repositories}). + @menu * editinfo example:: Editinfo example @end menu @@ -6263,15 +6545,20 @@ If the repository name does not match any of the regular expressions in this file, the @samp{DEFAULT} line is used, if it is specified. -If the name @samp{ALL} appears as a regular expression -it is always used in addition to any matching regular expression or -@samp{DEFAULT}. +All occurances of the name @samp{ALL} appearing as a +regular expression are used in addition to the first +matching regular expression or @samp{DEFAULT}. -All matching regular expressions are used. +The first matching regular expression is used. @xref{commit files}, for a description of the syntax of the @file{loginfo} file. +Note: when @sc{CVS} is accessing a remote repository, +@file{loginfo} will be run on the @emph{remote} +(i.e., server) side, not the client side (@pxref{Remote +repositories}). + @menu * loginfo example:: Loginfo example @end menu @@ -6326,9 +6613,9 @@ If the repository name does not match any of the regular expressions in this file, the @samp{DEFAULT} line is used, if it is specified. -If the name @samp{ALL} appears as a regular expression -it is always used in addition to the first matching -regular expression or @samp{DEFAULT}. +All occurances of the name @samp{ALL} appearing as a +regular expression are used in addition to the first +matching regular expression or @samp{DEFAULT}. The log message template will be used as a default log message. If you specify a log message with @samp{cvs @@ -6339,6 +6626,11 @@ template. @xref{editinfo example}, for an example @file{rcsinfo} file. +Note: when @sc{CVS} is accessing a remote repository, +@file{rcsinfo} will be run on the @emph{remote} +(i.e., server) side, not the client side (@pxref{Remote +repositories}). + @c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @node cvsignore @appendixsec Ignoring files via cvsignore @@ -6439,13 +6731,15 @@ set up the repository. If you want to set up another repository, the easiest way to get a reasonable set of working administrative -files is to get the source to @sc{cvs}, and run the -@code{cvsinit} shell script. It will set up an empty -repository in the directory defined by the environment -variable @code{$CVSROOT}. (@code{cvsinit} is careful to -never overwrite any existing files in the repository, -so no harm is done if you run @code{cvsinit} on -an already set-up repository.) +files is to run the @code{cvsinit} shell script. It +will set up an empty repository in the directory +defined by the environment variable @code{$CVSROOT}. +(@code{cvsinit} is careful to never overwrite any +existing files in the repository, so no harm is done if +you run @code{cvsinit} on an already set-up +repository. In fact, running it on an already set-up +repository is the best way to update the various +scripts from the @samp{contrib} directory.) @c --------------------------------------------------------------------- @node Environment variables @@ -6462,6 +6756,10 @@ that affect @sc{cvs}. A whitespace-separated list of file name patterns that @sc{cvs} should ignore. @xref{cvsignore}. +@item $CVSWRAPPERS +A whitespace-separated list of file name patterns that +@sc{cvs} should treat as wrappers. @xref{Wrappers}. + @cindex CVSREAD @item $CVSREAD If this is set, @code{checkout} and @code{update} will @@ -6477,18 +6775,11 @@ kept). This information must be available to @sc{cvs} for most commands to execute; if @code{$CVSROOT} is not set, or if you wish to override it for one invocation, you can supply it on the command line: @samp{cvs -d cvsroot -cvs_command@dots{}} You may not need to set -@code{$CVSROOT} if your @sc{cvs} binary has the right path -compiled in. - -If your site has several repositories, you must be -careful to set @code{$CVSROOT} to the appropriate one -when you use @sc{cvs}, even if you just run @samp{cvs -update} inside an already checked-out module. Future -releases of @sc{cvs} will probably store information about -which repository the module came from inside the -@file{CVS} directory, but version 1.3 relies totally on -@code{$CVSROOT}. +cvs_command@dots{}} Once you have checked out a working +directory, @sc{cvs} stores the appropriate root (in +the file @file{CVS/Root}), so normally you only need to +worry about this when initially checking out a working +directory. @cindex EDITOR @cindex CVSEDITOR @@ -6627,10 +6918,6 @@ no way to see how the tag was assigned yesterday). @unnumbered Index @cindex Index -If you cannot find what you are looking for here write -to <@t{ceder@@signum.se}> so that an entry can be added -to the next release of this manual. - @printindex cp @summarycontents diff --git a/gnu/usr.bin/cvs/examples/commitinfo b/gnu/usr.bin/cvs/examples/commitinfo index 7e602e2..3964798 100644 --- a/gnu/usr.bin/cvs/examples/commitinfo +++ b/gnu/usr.bin/cvs/examples/commitinfo @@ -1,5 +1,5 @@ # -# $Id: commitinfo,v 1.2 1992/03/31 04:19:47 berliner Exp $ +#ident "@(#)cvs/examples:$Name: $:$Id: commitinfo,v 1.3 1995/11/14 23:30:05 woods Exp $" # # The "commitinfo" file is used to control pre-commit checks. # The filter on the right is invoked with the repository and a list @@ -7,15 +7,21 @@ # cause the commit to be aborted. # # The first entry on a line is a regular expression which is tested -# against the directory that the change is being committed to, relative to the -# $CVSROOT. If a match is found, then the remainder of the line is the -# name of the filter to run. +# against the directory that the change is being committed to, relative +# to the $CVSROOT. For the first match that is found, then the remainder +# of the line is the name of the filter to run. # # If the repository name does not match any of the regular expressions in this # file, the "DEFAULT" line is used, if it is specified. # -# If the name ALL appears as a regular expression it is always used -# in addition to the first matching regex or DEFAULT. +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". # -^cvs checkforcvsid -DEFAULT checkforid +# NOTE: contrib/commit_prep usage: +# -r - record directories affected by commit for use with contrib/log_accum +# -c - check for things like "$Id" near head of file, no "$Log", etc. +# +^apc $CVSROOT/CVSROOT/commit_prep -r -c +^misc $CVSROOT/CVSROOT/commit_prep -r +CVSROOT $CVSROOT/CVSROOT/commit_prep -r -c +DEFAULT $CVSROOT/CVSROOT/commit_prep diff --git a/gnu/usr.bin/cvs/examples/editinfo b/gnu/usr.bin/cvs/examples/editinfo index fb3f4e7..976a986 100644 --- a/gnu/usr.bin/cvs/examples/editinfo +++ b/gnu/usr.bin/cvs/examples/editinfo @@ -1,5 +1,5 @@ # -# $Id: editinfo,v 1.1 1992/03/21 06:49:39 berliner Exp $ +#ident "@(#)cvs/examples:$Name: $:$Id: editinfo,v 1.2 1995/11/14 23:30:07 woods Exp $" # # The "editinfo" file is used to allow verification of logging # information. It works best when a template (as specified in the @@ -27,4 +27,6 @@ # supported. There can be only one entry that matches a given # repository. # +# Note there is no "edit" example script currently available.... +# DEFAULT $CVSROOT/CVSROOT/edit "%s" diff --git a/gnu/usr.bin/cvs/examples/loginfo b/gnu/usr.bin/cvs/examples/loginfo index d9f82da..1d68e7d 100644 --- a/gnu/usr.bin/cvs/examples/loginfo +++ b/gnu/usr.bin/cvs/examples/loginfo @@ -1,20 +1,39 @@ # -# $CVSid: @(#)loginfo 1.5 92/03/31 $ +#ident "@(#)cvs/examples:$Name: $:$Id: loginfo,v 1.4 1995/11/14 23:30:08 woods Exp $" # # The "loginfo" file is used to control where "cvs commit" log information # is sent. The first entry on a line is a regular expression which is tested # against the directory that the change is being made to, relative to the -# $CVSROOT. If a match is found, then the remainder of the line is a filter -# program that should expect log information on its standard input. +# $CVSROOT. For the first match that is found, then the remainder of the +# line is a filter program that should expect log information on its standard +# input. # -# The filter program may use one and only one % modifier (ala printf). If -# %s is specified in the filter program, a brief title is included (enclosed -# in single quotes) showing the modified file names. +# If the repository name does not match any of the regular expressions in the +# first field of this file, the "DEFAULT" line is used, if it is specified. # -# If the repository name does not match any of the regular expressions in this -# file, the "DEFAULT" line is used, if it is specified. +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". # -# If the name ALL appears as a regular expression it is always used -# in addition to the first matching regex or DEFAULT. +# The filter program may use one and only one "%s" modifier (ala printf). If +# such a "%s" is specified in the filter program, a brief title is included +# (as one argument, enclosed in single quotes) showing the relative directory +# name and listing the modified file names. # -DEFAULT $CVSROOT/CVSROOT/log.pl %s $CVSROOT/CVSROOT/commitlog +# NOTE: contrib/log usage: (currently requires perl) +# -d - turn debugging on.... +# -m mailto - send mail to "mailto" (multiple -m's permitted) +# -f logfile - required arg: save messages to logfile +# %s - may follow other options at the end of the line +# +# NOTE: contrib/log_accum usage: (currently requires perl) +# (must have 'commit_prep -r' in commitinfo) +# -d - turn debugging on.... +# -M modulename - use this as the module name (necessary for sub-dirs) +# -m mailto - send mail to "mailto" (multiple -m's permitted) [optional] +# -f logfile - save messages to logfile [optional] +# %s - must follow other options at the end of the line +# +# without perl you could do this: +#DEFAULT (echo ""; who am i; date; cat) >> $CVSROOT/CVSROOT/commitlog +# +DEFAULT $CVSROOT/CVSROOT/log -f $CVSROOT/CVSROOT/commitlog %s diff --git a/gnu/usr.bin/cvs/examples/modules b/gnu/usr.bin/cvs/examples/modules index 891ae3d..ac8cd4d 100644 --- a/gnu/usr.bin/cvs/examples/modules +++ b/gnu/usr.bin/cvs/examples/modules @@ -1,6 +1,7 @@ # -# CVS Modules file for Prisma sources -# $CVSid: @(#)modules 1.5 92/03/31 $ +# The CVS Modules File +# +#ident "@(#)cvs/examples:$Name: $:$Id: modules,v 1.4 1995/11/14 23:28:48 woods Exp $" # # Three different line formats are valid: # key -a aliases... @@ -10,6 +11,7 @@ # Where "options" are composed of: # -i prog Run "prog" on "cvs commit" from top-level of module. # -o prog Run "prog" on "cvs checkout" of module. +# -e prog Run "prog" on "cvs export" of module. # -t prog Run "prog" on "cvs rtag" of module. # -u prog Run "prog" on "cvs update" of module. # -d dir Place module in directory "dir" instead of module name. @@ -28,14 +30,27 @@ # Convenient aliases world -a . -kernel -a sys lang/adb sparcsim # CVSROOT support CVSROOT -i /usr/local/bin/mkmodules CVSROOT +commitinfo -i /usr/local/bin/mkmodules CVSROOT commitinfo +cvswrappers -i /usr/local/bin/mkmodules CVSROOT cvswrappers +editinfo -i /usr/local/bin/mkmodules CVSROOT editinfo modules -i /usr/local/bin/mkmodules CVSROOT modules loginfo -i /usr/local/bin/mkmodules CVSROOT loginfo -commitinfo -i /usr/local/bin/mkmodules CVSROOT commitinfo rcsinfo -i /usr/local/bin/mkmodules CVSROOT rcsinfo +rcstemplate -i /usr/local/bin/mkmodules CVSROOT rcstemplate +taginfo -i /usr/local/bin/mkmodules CVSROOT taginfo + +# Add more modules here +# +# END_REQUIRED_CONTENT (this comment for cvsinit) +# +# The remainder was for the Prisma OS sources +# + +# another convenient alias +kernel -a sys lang/adb sparcsim # The "sys" entry exists only to make symbolic links after checkout sys -o sys/tools/make_links sys diff --git a/gnu/usr.bin/cvs/examples/rcsinfo b/gnu/usr.bin/cvs/examples/rcsinfo index 21a7b47..c90e9e0 100644 --- a/gnu/usr.bin/cvs/examples/rcsinfo +++ b/gnu/usr.bin/cvs/examples/rcsinfo @@ -1,18 +1,18 @@ # -# $Id: rcsinfo,v 1.3 1992/04/10 18:59:14 berliner Exp $ +#ident "@(#)cvs/examples:$Name: $:$Id: rcsinfo,v 1.3 1995/11/14 23:30:10 woods Exp $" # # The "rcsinfo" file is used to control templates with which the editor # is invoked on commit and import. # # The first entry on a line is a regular expression which is tested # against the directory that the change is being made to, relative to the -# $CVSROOT. If a match is found, then the remainder of the line is the -# name of the file that contains the template. +# $CVSROOT. For the first match that is found, then the remainder of the +# line is the name of the file that contains the template. # # If the repository name does not match any of the regular expressions in this # file, the "DEFAULT" line is used, if it is specified. # -# If the name ALL appears as a regular expression it is always used -# in addition to the first matching regex or DEFAULT. +# If the name "ALL" appears as a regular expression it is always used +# in addition to the first matching regex or "DEFAULT". # -DEFAULT /src/master/CVSROOT/rcstemplate +DEFAULT $CVSROOT/CVSROOT/rcstemplate diff --git a/gnu/usr.bin/cvs/lib/argmatch.c b/gnu/usr.bin/cvs/lib/argmatch.c index 327a27d..cc360ee 100644 --- a/gnu/usr.bin/cvs/lib/argmatch.c +++ b/gnu/usr.bin/cvs/lib/argmatch.c @@ -21,6 +21,8 @@ #include "config.h" #endif +#include <sys/types.h> + #include <stdio.h> #ifdef STDC_HEADERS #include <string.h> @@ -39,7 +41,7 @@ argmatch (arg, optlist) char **optlist; { int i; /* Temporary index in OPTLIST. */ - int arglen; /* Length of ARG. */ + size_t arglen; /* Length of ARG. */ int matchind = -1; /* Index of first nonexact match. */ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ diff --git a/gnu/usr.bin/cvs/lib/config.h b/gnu/usr.bin/cvs/lib/config.h index cd1b857..ce06c62 100644 --- a/gnu/usr.bin/cvs/lib/config.h +++ b/gnu/usr.bin/cvs/lib/config.h @@ -1,11 +1,20 @@ /* config.h. Generated automatically by configure. */ /* config.h.in. Generated automatically from configure.in by autoheader. */ +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + /* Define if using alloca.c. */ /* #undef C_ALLOCA */ /* Define if type char is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ /* #undef __CHAR_UNSIGNED__ */ +#endif /* Define to empty if the keyword does not work. */ /* #undef const */ @@ -17,21 +26,33 @@ /* Define to `int' if <sys/types.h> doesn't define. */ /* #undef gid_t */ +/* Define if you have alloca, as a function or macro. */ +#define HAVE_ALLOCA 1 + /* Define if you have <alloca.h> and it should be used (not on Ultrix). */ /* #undef HAVE_ALLOCA_H */ /* Define if you support file names longer than 14 characters. */ #define HAVE_LONG_FILE_NAMES 1 +/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + /* Define if utime(file, NULL) sets file's timestamp to the present. */ #define HAVE_UTIME_NULL 1 +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + /* Define if on MINIX. */ /* #undef _MINIX */ /* Define to `int' if <sys/types.h> doesn't define. */ /* #undef mode_t */ +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef pid_t */ + /* Define if the system does not provide POSIX.1 features except with this defined. */ /* #undef _POSIX_1_SOURCE */ @@ -54,15 +75,43 @@ */ /* #undef STACK_DIRECTION */ +/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 -/* Define if your <sys/time.h> declares struct tm. */ -/* #undef TM_IN_SYS_TIME */ +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 /* Define to `int' if <sys/types.h> doesn't define. */ /* #undef uid_t */ +/* Define if you have MIT Kerberos version 4 available. */ +/* #undef HAVE_KERBEROS */ + +/* Define if you want CVS to be able to be a remote repository client. */ +#define CLIENT_SUPPORT 1 + +/* Define if you want CVS to be able to serve repositories to remote + clients. */ +#define SERVER_SUPPORT 1 + +/* the path to the gnu diff program on your system */ +#define DIFF "/usr/bin/diff -a" + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* Define if you have the connect function. */ +/* #undef HAVE_CONNECT */ + +/* Define if you have the fchdir function. */ +#define HAVE_FCHDIR 1 + /* Define if you have the fchmod function. */ #define HAVE_FCHMOD 1 @@ -75,6 +124,12 @@ /* Define if you have the ftruncate function. */ #define HAVE_FTRUNCATE 1 +/* Define if you have the getpagesize function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define if you have the krb_get_err_text function. */ +/* #undef HAVE_KRB_GET_ERR_TEXT */ + /* Define if you have the mkfifo function. */ #define HAVE_MKFIFO 1 @@ -84,12 +139,33 @@ /* Define if you have the setvbuf function. */ #define HAVE_SETVBUF 1 +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the sigblock function. */ +#define HAVE_SIGBLOCK 1 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define if you have the sigsetmask function. */ +#define HAVE_SIGSETMASK 1 + +/* Define if you have the sigvec function. */ +#define HAVE_SIGVEC 1 + +/* Define if you have the timezone function. */ +#define HAVE_TIMEZONE 1 + /* Define if you have the vfork function. */ #define HAVE_VFORK 1 /* Define if you have the vprintf function. */ #define HAVE_VPRINTF 1 +/* Define if you have the <direct.h> header file. */ +/* #undef HAVE_DIRECT_H */ + /* Define if you have the <dirent.h> header file. */ #define HAVE_DIRENT_H 1 @@ -99,6 +175,9 @@ /* Define if you have the <fcntl.h> header file. */ #define HAVE_FCNTL_H 1 +/* Define if you have the <io.h> header file. */ +/* #undef HAVE_IO_H */ + /* Define if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 @@ -111,23 +190,44 @@ /* Define if you have the <string.h> header file. */ #define HAVE_STRING_H 1 +/* Define if you have the <sys/bsdtypes.h> header file. */ +/* #undef HAVE_SYS_BSDTYPES_H */ + /* Define if you have the <sys/dir.h> header file. */ /* #undef HAVE_SYS_DIR_H */ /* Define if you have the <sys/ndir.h> header file. */ /* #undef HAVE_SYS_NDIR_H */ +/* Define if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the <sys/resource.h> header file. */ +#define HAVE_SYS_RESOURCE_H 1 + /* Define if you have the <sys/select.h> header file. */ #define HAVE_SYS_SELECT_H 1 +/* Define if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + /* Define if you have the <sys/timeb.h> header file. */ #define HAVE_SYS_TIMEB_H 1 -/* Define if you have the <sys/wait.h> header file. */ -#define HAVE_SYS_WAIT_H 1 - /* Define if you have the <unistd.h> header file. */ #define HAVE_UNISTD_H 1 /* Define if you have the <utime.h> header file. */ #define HAVE_UTIME_H 1 + +/* Define if you have the inet library (-linet). */ +/* #undef HAVE_LIBINET */ + +/* Define if you have the nsl library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define if you have the nsl_s library (-lnsl_s). */ +/* #undef HAVE_LIBNSL_S */ + +/* Define if you have the socket library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ diff --git a/gnu/usr.bin/cvs/lib/error.c b/gnu/usr.bin/cvs/lib/error.c index 6734c02..0398103 100644 --- a/gnu/usr.bin/cvs/lib/error.c +++ b/gnu/usr.bin/cvs/lib/error.c @@ -18,26 +18,20 @@ /* David MacKenzie */ /* Brian Berliner added support for CVS */ +#include "cvs.h" + #ifndef lint -static char rcsid[] = "$CVSid: @(#)error.c 1.13 94/09/30 $"; +static const char rcsid[] = "$CVSid: @(#)error.c 1.13 94/09/30 $"; +USE(rcsid); #endif /* not lint */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include <stdio.h> -/* turn on CVS support by default, since this is the CVS distribution */ -#define CVS_SUPPORT - -#ifdef CVS_SUPPORT -#if __STDC__ -void Lock_Cleanup(void); -#else -void Lock_Cleanup(); -#endif /* __STDC__ */ -#endif /* CVS_SUPPORT */ +/* If non-zero, error will use the CVS protocol to stdout to report error + messages. This will only be set in the CVS server parent process; + most other code is run via do_cvs_command, which forks off a child + process and packages up its stderr in the protocol. */ +int error_use_protocol; #ifdef HAVE_VPRINTF @@ -74,6 +68,20 @@ void exit (); extern char *strerror (); +typedef void (*fn_returning_void) (); + +/* Function to call before exiting. */ +static fn_returning_void cleanup_fn; + +fn_returning_void +error_set_cleanup (arg) + fn_returning_void arg; +{ + fn_returning_void retval = cleanup_fn; + cleanup_fn = arg; + return retval; +} + /* Print the program name and error message MESSAGE, which is a printf-style format string with optional args. If ERRNUM is nonzero, print its corresponding system error message. @@ -81,60 +89,58 @@ extern char *strerror (); /* VARARGS */ void #if defined (HAVE_VPRINTF) && __STDC__ -error (int status, int errnum, char *message, ...) +error (int status, int errnum, const char *message, ...) #else error (status, errnum, message, va_alist) int status; int errnum; - char *message; + const char *message; va_dcl #endif { + FILE *out = stderr; extern char *program_name; -#ifdef CVS_SUPPORT extern char *command_name; -#endif #ifdef HAVE_VPRINTF va_list args; #endif -#ifdef CVS_SUPPORT + if (error_use_protocol) + { + out = stdout; + printf ("E "); + } + if (command_name && *command_name) if (status) - fprintf (stderr, "%s [%s aborted]: ", program_name, command_name); + fprintf (out, "%s [%s aborted]: ", program_name, command_name); else - fprintf (stderr, "%s %s: ", program_name, command_name); + fprintf (out, "%s %s: ", program_name, command_name); else - fprintf (stderr, "%s: ", program_name); -#else - fprintf (stderr, "%s: ", program_name); -#endif + fprintf (out, "%s: ", program_name); #ifdef HAVE_VPRINTF VA_START (args, message); - vfprintf (stderr, message, args); + vfprintf (out, message, args); va_end (args); #else #ifdef HAVE_DOPRNT - _doprnt (message, &args, stderr); + _doprnt (message, &args, out); #else - fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); + fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif #endif if (errnum) - fprintf (stderr, ": %s", strerror (errnum)); - putc ('\n', stderr); - fflush (stderr); + fprintf (out, ": %s", strerror (errnum)); + putc ('\n', out); + fflush (out); if (status) { -#ifdef CVS_SUPPORT - Lock_Cleanup(); -#endif + if (cleanup_fn) + (*cleanup_fn) (); exit (status); } } -#ifdef CVS_SUPPORT - /* Print the program name and error message MESSAGE, which is a printf-style format string with optional args to the file specified by FP. If ERRNUM is nonzero, print its corresponding system error message. @@ -175,11 +181,8 @@ fperror (fp, status, errnum, message, va_alist) fflush (fp); if (status) { -#ifdef CVS_SUPPORT - Lock_Cleanup(); -#endif + if (cleanup_fn) + (*cleanup_fn) (); exit (status); } } - -#endif /* CVS_SUPPORT */ diff --git a/gnu/usr.bin/cvs/lib/getdate.y b/gnu/usr.bin/cvs/lib/getdate.y index 5769e9c..baa1731 100644 --- a/gnu/usr.bin/cvs/lib/getdate.y +++ b/gnu/usr.bin/cvs/lib/getdate.y @@ -92,6 +92,14 @@ struct timeb { #define bcopy(from, to, len) memcpy ((to), (from), (len)) #endif +#if defined (STDC_HEADERS) +#include <stdlib.h> +#endif + +#if defined (HAVE_ALLOCA_H) +#include <alloca.h> +#endif + extern struct tm *gmtime(); extern struct tm *localtime(); diff --git a/gnu/usr.bin/cvs/lib/getopt.c b/gnu/usr.bin/cvs/lib/getopt.c index 446a8e4..f1d8dfa 100644 --- a/gnu/usr.bin/cvs/lib/getopt.c +++ b/gnu/usr.bin/cvs/lib/getopt.c @@ -47,6 +47,10 @@ #include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif + /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling @@ -494,7 +498,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { - if (nameend - nextchar == strlen (p->name)) + if (nameend - nextchar == (int) strlen (p->name)) { /* Exact match found. */ pfound = p; diff --git a/gnu/usr.bin/cvs/lib/hash.c b/gnu/usr.bin/cvs/lib/hash.c index afc554d..8ac9323 100644 --- a/gnu/usr.bin/cvs/lib/hash.c +++ b/gnu/usr.bin/cvs/lib/hash.c @@ -10,8 +10,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)hash.c 1.19 94/09/23 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)hash.c 1.19 94/09/23 $"; +USE(rcsid); #endif /* global caches */ @@ -23,7 +23,7 @@ static void freenode_mem PROTO((Node * p)); /* hash function */ static int hashp (key) - char *key; + const char *key; { unsigned int h = 0; unsigned int g; @@ -254,7 +254,7 @@ addnode (list, p) Node * findnode (list, key) List *list; - char *key; + const char *key; { Node *head, *p; @@ -277,7 +277,7 @@ findnode (list, key) int walklist (list, proc, closure) List *list; - int (*proc) (); + int (*proc) PROTO ((Node *, void *)); void *closure; { Node *head, *p; @@ -298,7 +298,7 @@ walklist (list, proc, closure) void sortlist (list, comp) List *list; - int (*comp) (); + int (*comp) PROTO ((const Node *, const Node *)); { Node *head, *remain, *p, *q; @@ -363,7 +363,8 @@ nodetypestring (type) return("<trash>"); } -int +static int printnode PROTO ((Node *, void *)); +static int printnode (node, closure) Node *node; void *closure; diff --git a/gnu/usr.bin/cvs/lib/hash.h b/gnu/usr.bin/cvs/lib/hash.h index 8e10e81..e30511a 100644 --- a/gnu/usr.bin/cvs/lib/hash.h +++ b/gnu/usr.bin/cvs/lib/hash.h @@ -44,23 +44,12 @@ struct list }; typedef struct list List; -struct entnode -{ - char *version; - char *timestamp; - char *options; - char *tag; - char *date; - char *conflict; -}; -typedef struct entnode Entnode; - List *getlist PROTO((void)); -Node *findnode PROTO((List * list, char *key)); +Node *findnode PROTO((List * list, const char *key)); Node *getnode PROTO((void)); int addnode PROTO((List * list, Node * p)); -int walklist PROTO((List * list, int PROTO((*proc)) PROTO((Node *n, void *closure)), void *closure)); +int walklist PROTO((List * list, int (*)(Node *n, void *closure), void *closure)); void dellist PROTO((List ** listp)); void delnode PROTO((Node * p)); void freenode PROTO((Node * p)); -void sortlist PROTO((List * list, int PROTO((*comp))())); +void sortlist PROTO((List * list, int (*)(const Node *, const Node *))); diff --git a/gnu/usr.bin/cvs/lib/myndbm.c b/gnu/usr.bin/cvs/lib/myndbm.c index 33ef49c..fef3265 100644 --- a/gnu/usr.bin/cvs/lib/myndbm.c +++ b/gnu/usr.bin/cvs/lib/myndbm.c @@ -18,8 +18,8 @@ #ifdef MY_NDBM #ifndef lint -static char rcsid[] = "$CVSid: @(#)myndbm.c 1.7 94/09/23 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)myndbm.c 1.7 94/09/23 $"; +USE(rcsid); #endif static void mydbm_load_file (); diff --git a/gnu/usr.bin/cvs/lib/sighandle.c b/gnu/usr.bin/cvs/lib/sighandle.c index 1db4177..a225983 100644 --- a/gnu/usr.bin/cvs/lib/sighandle.c +++ b/gnu/usr.bin/cvs/lib/sighandle.c @@ -37,6 +37,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include "system.h" #include <sys/types.h> #include <stdio.h> @@ -54,10 +55,6 @@ char *malloc(); #endif /* __STDC__ */ #endif /* STDC_HEADERS */ -#ifdef _MINIX -#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */ -#endif - /* Define the highest signal number (usually) */ #ifndef SIGMAX #define SIGMAX 64 @@ -78,7 +75,7 @@ static struct SIG_hlist **SIG_handlers; /* Define array of default signal vectors */ -#ifdef POSIX +#ifdef POSIX_SIGNALS static struct sigaction *SIG_defaults; #else #ifdef BSD_SIGNALS @@ -90,7 +87,7 @@ static RETSIGTYPE (**SIG_defaults)(); /* Critical section housekeeping */ static int SIG_crSectNest = 0; /* Nesting level */ -#ifdef POSIX +#ifdef POSIX_SIGNALS static sigset_t SIG_crSectMask; /* Signal mask */ #else static int SIG_crSectMask; /* Signal mask */ @@ -103,14 +100,14 @@ static int SIG_crSectMask; /* Signal mask */ static int SIG_init() { int i; -#ifdef POSIX +#ifdef POSIX_SIGNALS sigset_t sigset_test; #endif if (SIG_defaults && SIG_handlers) /* already allocated */ return (0); -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigfillset(&sigset_test); for (i = 1; i < SIGMAX && sigismember(&sigset_test, i) == 1; i++) ; @@ -175,7 +172,7 @@ RETSIGTYPE (*fn)(); { int val; struct SIG_hlist *this; -#ifdef POSIX +#ifdef POSIX_SIGNALS struct sigaction act; sigset_t sigset_mask, sigset_omask; #else @@ -191,7 +188,7 @@ RETSIGTYPE (*fn)(); val = 0; /* Block this signal while we look at handler chain */ -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigemptyset(&sigset_mask); (void) sigaddset(&sigset_mask, sig); (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask); @@ -220,7 +217,7 @@ RETSIGTYPE (*fn)(); if (SIG_handlers[sig] == (struct SIG_hlist *) NULL) { -#ifdef POSIX +#ifdef POSIX_SIGNALS act.sa_handler = SIG_handle; (void) sigemptyset(&act.sa_mask); act.sa_flags = 0; @@ -257,7 +254,7 @@ RETSIGTYPE (*fn)(); } /* Unblock the signal */ -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL); #else #ifdef BSD_SIGNALS @@ -280,7 +277,7 @@ RETSIGTYPE (*fn)(); int val; struct SIG_hlist *this; struct SIG_hlist *last; -#ifdef POSIX +#ifdef POSIX_SIGNALS sigset_t sigset_mask, sigset_omask; #else #ifdef BSD_SIGNALS @@ -295,7 +292,7 @@ RETSIGTYPE (*fn)(); last = (struct SIG_hlist *) NULL; /* Block this signal while we look at handler chain */ -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigemptyset(&sigset_mask); (void) sigaddset(&sigset_mask, sig); (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask); @@ -330,7 +327,7 @@ RETSIGTYPE (*fn)(); /* Restore default behavior if there are no registered handlers */ if (SIG_handlers[sig] == (struct SIG_hlist *) NULL) { -#ifdef POSIX +#ifdef POSIX_SIGNALS val = sigaction(sig, &SIG_defaults[sig], (struct sigaction *) NULL); #else @@ -344,7 +341,7 @@ RETSIGTYPE (*fn)(); } /* Unblock the signal */ -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL); #else #ifdef BSD_SIGNALS @@ -365,7 +362,7 @@ void SIG_beginCrSect() { if (SIG_crSectNest == 0) { -#ifdef POSIX +#ifdef POSIX_SIGNALS sigset_t sigset_mask; (void) sigfillset(&sigset_mask); @@ -394,7 +391,7 @@ void SIG_endCrSect() SIG_crSectNest--; if (SIG_crSectNest == 0) { -#ifdef POSIX +#ifdef POSIX_SIGNALS (void) sigprocmask(SIG_SETMASK, &SIG_crSectMask, NULL); #else #ifdef BSD_SIGNALS diff --git a/gnu/usr.bin/cvs/lib/subr.c b/gnu/usr.bin/cvs/lib/subr.c index d3d40b1..228581c 100644 --- a/gnu/usr.bin/cvs/lib/subr.c +++ b/gnu/usr.bin/cvs/lib/subr.c @@ -11,248 +11,11 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)subr.c 1.64 94/10/07 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)subr.c 1.64 94/10/07 $"; +USE(rcsid); #endif -#ifdef _MINIX -#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */ -#endif - -#ifdef HAVE_VPRINTF -#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) -#include <stdarg.h> -#define VA_START(args, lastarg) va_start(args, lastarg) -#else -#include <varargs.h> -#define VA_START(args, lastarg) va_start(args) -#endif -#else -#define va_alist a1, a2, a3, a4, a5, a6, a7, a8 -#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; -#endif - -/* - * I don't know of a convenient way to test this at configure time, or else - * I'd certainly do it there. - */ -#if defined(NeXT) -#define LOSING_TMPNAM_FUNCTION -#ifndef _POSIX_SOURCE -/* - * NeXT doesn't define these without _POSIX_SOURCE, - * but that changes a lot of things. - */ -#define WEXITSTATUS(x) ((x).w_retcode) -#define WTERMSIG(x) ((x).w_termsig) -#endif -#endif - -static void run_add_arg PROTO((char *s)); -static void run_init_prog PROTO((void)); - extern char *getlogin (); -extern char *strtok (); - -/* - * Copies "from" to "to". mallocs a buffer large enough to hold the entire - * file and does one read/one write to do the copy. This is reasonable, - * since source files are typically not too large. - */ -void -copy_file (from, to) - char *from; - char *to; -{ - struct stat sb; - struct utimbuf t; - int fdin, fdout; - char *buf; - - if (trace) - (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to); - if (noexec) - return; - - if ((fdin = open (from, O_RDONLY)) < 0) - error (1, errno, "cannot open %s for copying", from); - if (fstat (fdin, &sb) < 0) - error (1, errno, "cannot fstat %s", from); - if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) - error (1, errno, "cannot create %s for copying", to); - if (sb.st_size > 0) - { - buf = xmalloc ((int) sb.st_size); - if (read (fdin, buf, (int) sb.st_size) != (int) sb.st_size) - error (1, errno, "cannot read file %s for copying", from); - if (write (fdout, buf, (int) sb.st_size) != (int) sb.st_size -#ifdef HAVE_FSYNC - || fsync (fdout) == -1 -#endif - ) - { - error (1, errno, "cannot write file %s for copying", to); - } - free (buf); - } - (void) close (fdin); - if (close (fdout) < 0) - error (1, errno, "cannot close %s", to); - - /* now, set the times for the copied file to match those of the original */ - memset ((char *) &t, 0, sizeof (t)); - t.actime = sb.st_atime; - t.modtime = sb.st_mtime; - (void) utime (to, &t); -} - -/* FIXME-krp: these functions would benefit from caching the char * & - stat buf. */ - -/* - * Returns non-zero if the argument file is a directory, or is a symbolic - * link which points to a directory. - */ -int -isdir (file) - char *file; -{ - struct stat sb; - - if (stat (file, &sb) < 0) - return (0); - return (S_ISDIR (sb.st_mode)); -} - -/* - * Returns non-zero if the argument file is a symbolic link. - */ -int -islink (file) - char *file; -{ -#ifdef S_ISLNK - struct stat sb; - - if (lstat (file, &sb) < 0) - return (0); - return (S_ISLNK (sb.st_mode)); -#else - return (0); -#endif -} - -/* - * Returns non-zero if the argument file exists. - */ -int -isfile (file) - char *file; -{ - struct stat sb; - - if (stat (file, &sb) < 0) - return (0); - return (1); -} - -/* - * Returns non-zero if the argument file is readable. - * XXX - must be careful if "cvs" is ever made setuid! - */ -int -isreadable (file) - char *file; -{ - return (access (file, R_OK) != -1); -} - -/* - * Returns non-zero if the argument file is writable - * XXX - muct be careful if "cvs" is ever made setuid! - */ -int -iswritable (file) - char *file; -{ - return (access (file, W_OK) != -1); -} - -/* - * Open a file and die if it fails - */ -FILE * -open_file (name, mode) - char *name; - char *mode; -{ - FILE *fp; - - if ((fp = fopen (name, mode)) == NULL) - error (1, errno, "cannot open %s", name); - return (fp); -} - -/* - * Open a file if allowed and return. - */ -FILE * -Fopen (name, mode) - char *name; - char *mode; -{ - if (trace) - (void) fprintf (stderr, "-> fopen(%s,%s)\n", name, mode); - if (noexec) - return (NULL); - - return (fopen (name, mode)); -} - -/* - * Make a directory and die if it fails - */ -void -make_directory (name) - char *name; -{ - struct stat buf; - - if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode))) - error (0, 0, "%s already exists but is not a directory", name); - if (!noexec && mkdir (name, 0777) < 0) - error (1, errno, "cannot make directory %s", name); -} - -/* - * Make a path to the argument directory, printing a message if something - * goes wrong. - */ -void -make_directories (name) - char *name; -{ - char *cp; - - if (noexec) - return; - - if (mkdir (name, 0777) == 0 || errno == EEXIST) - return; - if (errno != ENOENT) - { - error (0, errno, "cannot make path to %s", name); - return; - } - if ((cp = strrchr (name, '/')) == NULL) - return; - *cp = '\0'; - make_directories (name); - *cp++ = '/'; - if (*cp == '\0') - return; - (void) mkdir (name, 0777); -} /* * malloc some data and die if it fails @@ -263,7 +26,14 @@ xmalloc (bytes) { char *cp; - if ((cp = malloc (bytes)) == NULL) + /* Parts of CVS try to xmalloc zero bytes and then free it. Some + systems have a malloc which returns NULL for zero byte + allocations but a free which can't handle NULL, so compensate. */ + if (bytes == 0) + bytes = 1; + + cp = malloc (bytes); + if (cp == NULL) error (1, 0, "can not allocate %lu bytes", (unsigned long) bytes); return (cp); } @@ -295,7 +65,7 @@ xrealloc (ptr, bytes) */ char * xstrdup (str) - char *str; + const char *str; { char *s; @@ -306,143 +76,16 @@ xstrdup (str) return (s); } -/* - * Change the mode of a file, either adding write permissions, or removing - * all write permissions. Adding write permissions honors the current umask - * setting. - */ +/* Remove trailing newlines from STRING, destructively. */ void -xchmod (fname, writable) - char *fname; - int writable; +strip_trailing_newlines (str) + char *str; { - struct stat sb; - mode_t mode, oumask; + int len; + len = strlen (str) - 1; - if (stat (fname, &sb) < 0) - { - if (!noexec) - error (0, errno, "cannot stat %s", fname); - return; - } - if (writable) - { - oumask = umask (0); - (void) umask (oumask); - mode = sb.st_mode | ((S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask); - } - else - { - mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH); - } - - if (trace) - (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode); - if (noexec) - return; - - if (chmod (fname, mode) < 0) - error (0, errno, "cannot change mode of file %s", fname); -} - -/* - * Rename a file and die if it fails - */ -void -rename_file (from, to) - char *from; - char *to; -{ - if (trace) - (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to); - if (noexec) - return; - - if (rename (from, to) < 0) - error (1, errno, "cannot rename file %s to %s", from, to); -} - -/* - * link a file, if possible. - */ -int -link_file (from, to) - char *from, *to; -{ - if (trace) - (void) fprintf (stderr, "-> link(%s,%s)\n", from, to); - if (noexec) - return (0); - - return (link (from, to)); -} - -/* - * unlink a file, if possible. - */ -int -unlink_file (f) - char *f; -{ - if (trace) - (void) fprintf (stderr, "-> unlink(%s)\n", f); - if (noexec) - return (0); - - return (unlink (f)); -} - -/* - * Compare "file1" to "file2". Return non-zero if they don't compare exactly. - * - * mallocs a buffer large enough to hold the entire file and does two reads to - * load the buffer and calls memcmp to do the cmp. This is reasonable, since - * source files are typically not too large. - */ - -/* richfix: this *could* exploit mmap. */ - -int -xcmp (file1, file2) - char *file1; - char *file2; -{ - register char *buf1, *buf2; - struct stat sb; - off_t size; - int ret, fd1, fd2; - - if ((fd1 = open (file1, O_RDONLY)) < 0) - error (1, errno, "cannot open file %s for comparing", file1); - if ((fd2 = open (file2, O_RDONLY)) < 0) - error (1, errno, "cannot open file %s for comparing", file2); - if (fstat (fd1, &sb) < 0) - error (1, errno, "cannot fstat %s", file1); - size = sb.st_size; - if (fstat (fd2, &sb) < 0) - error (1, errno, "cannot fstat %s", file2); - if (size == sb.st_size) - { - if (size == 0) - ret = 0; - else - { - buf1 = xmalloc ((int) size); - buf2 = xmalloc ((int) size); - if (read (fd1, buf1, (int) size) != (int) size) - error (1, errno, "cannot read file %s for comparing", file1); - if (read (fd2, buf2, (int) size) != (int) size) - error (1, errno, "cannot read file %s for comparing", file2); - ret = memcmp(buf1, buf2, (int) size); - free (buf1); - free (buf2); - } - } - else - ret = 1; - (void) close (fd1); - (void) close (fd2); - return (ret); + while (str[len] == '\n') + str[len--] = '\0'; } /* @@ -451,7 +94,7 @@ xcmp (file1, file2) void free_names (pargc, argv) int *pargc; - char *argv[]; + char **argv; { register int i; @@ -470,7 +113,7 @@ free_names (pargc, argv) void line2argv (pargc, argv, line) int *pargc; - char *argv[]; + char **argv; char *line; { char *cp; @@ -488,14 +131,13 @@ line2argv (pargc, argv, line) */ int numdots (s) - char *s; + const char *s; { - char *cp; int dots = 0; - for (cp = s; *cp; cp++) + for (; *s; s++) { - if (*cp == '.') + if (*s == '.') dots++; } return (dots); @@ -518,394 +160,18 @@ getcaller () if (uid == (uid_t) 0) { /* super-user; try getlogin() to distinguish */ - if (((name = getenv("LOGNAME")) || (name = getenv("USER")) || - (name = getlogin ())) && *name) + if (((name = getlogin ()) || (name = getenv("LOGNAME")) || + (name = getenv("USER"))) && *name) return (name); } if ((pw = (struct passwd *) getpwuid (uid)) == NULL) { - (void) sprintf (uidname, "uid%d", (unsigned long) uid); + (void) sprintf (uidname, "uid%lu", (unsigned long) uid); return (uidname); } return (pw->pw_name); } -/* - * To exec a program under CVS, first call run_setup() to setup any initial - * arguments. The options to run_setup are essentially like printf(). The - * arguments will be parsed into whitespace separated words and added to the - * global run_argv list. - * - * Then, optionally call run_arg() for each additional argument that you'd like - * to pass to the executed program. - * - * Finally, call run_exec() to execute the program with the specified arguments. - * The execvp() syscall will be used, so that the PATH is searched correctly. - * File redirections can be performed in the call to run_exec(). - */ -static char *run_prog; -static char **run_argv; -static int run_argc; -static int run_argc_allocated; - -/* VARARGS */ -#if defined (HAVE_VPRINTF) && (defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)) -void -run_setup (char *fmt,...) -#else -void -run_setup (fmt, va_alist) - char *fmt; - va_dcl - -#endif -{ -#ifdef HAVE_VPRINTF - va_list args; - -#endif - char *cp; - int i; - - run_init_prog (); - - /* clean out any malloc'ed values from run_argv */ - for (i = 0; i < run_argc; i++) - { - if (run_argv[i]) - { - free (run_argv[i]); - run_argv[i] = (char *) 0; - } - } - run_argc = 0; - - /* process the varargs into run_prog */ -#ifdef HAVE_VPRINTF - VA_START (args, fmt); - (void) vsprintf (run_prog, fmt, args); - va_end (args); -#else - (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); -#endif - - /* put each word into run_argv, allocating it as we go */ - for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t")) - run_add_arg (cp); -} - -void -run_arg (s) - char *s; -{ - run_add_arg (s); -} - -/* VARARGS */ -#if defined (HAVE_VPRINTF) && (defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)) -void -run_args (char *fmt,...) -#else -void -run_args (fmt, va_alist) - char *fmt; - va_dcl - -#endif -{ -#ifdef HAVE_VPRINTF - va_list args; - -#endif - - run_init_prog (); - - /* process the varargs into run_prog */ -#ifdef HAVE_VPRINTF - VA_START (args, fmt); - (void) vsprintf (run_prog, fmt, args); - va_end (args); -#else - (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); -#endif - - /* and add the (single) argument to the run_argv list */ - run_add_arg (run_prog); -} - -static void -run_add_arg (s) - char *s; -{ - /* allocate more argv entries if we've run out */ - if (run_argc >= run_argc_allocated) - { - run_argc_allocated += 50; - run_argv = (char **) xrealloc ((char *) run_argv, - run_argc_allocated * sizeof (char **)); - } - - if (s) - run_argv[run_argc++] = xstrdup (s); - else - run_argv[run_argc] = (char *) 0;/* not post-incremented on purpose! */ -} - -static void -run_init_prog () -{ - /* make sure that run_prog is allocated once */ - if (run_prog == (char *) 0) - run_prog = xmalloc (10 * 1024); /* 10K of args for _setup and _arg */ -} - -int -run_exec (stin, stout, sterr, flags) - char *stin; - char *stout; - char *sterr; - int flags; -{ - int shin, shout, sherr; - int mode_out, mode_err; -#if defined(NeXT) && !defined(_POSIX_SOURCE) - union wait status; -#else - int status; -#endif - int rc = -1; - int rerrno = 0; - int pid, w; - -#ifdef POSIX - sigset_t sigset_mask, sigset_omask; - struct sigaction act, iact, qact; - -#else -#ifdef BSD_SIGNALS - int mask; - struct sigvec vec, ivec, qvec; - -#else - RETSIGTYPE (*istat) (), (*qstat) (); -#endif -#endif - - if (trace) - { - (void) fprintf (stderr, "-> system("); - run_print (stderr); - (void) fprintf (stderr, ")\n"); - } - if (noexec && (flags & RUN_REALLY) == 0) - return (0); - - /* make sure that we are null terminated, since we didn't calloc */ - run_add_arg ((char *) 0); - - /* setup default file descriptor numbers */ - shin = 0; - shout = 1; - sherr = 2; - - /* set the file modes for stdout and stderr */ - mode_out = mode_err = O_WRONLY | O_CREAT; - mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC); - mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC); - - if (stin && (shin = open (stin, O_RDONLY)) == -1) - { - rerrno = errno; - error (0, errno, "cannot open %s for reading (prog %s)", - stin, run_argv[0]); - goto out0; - } - if (stout && (shout = open (stout, mode_out, 0666)) == -1) - { - rerrno = errno; - error (0, errno, "cannot open %s for writing (prog %s)", - stout, run_argv[0]); - goto out1; - } - if (sterr && (flags & RUN_COMBINED) == 0) - { - if ((sherr = open (sterr, mode_err, 0666)) == -1) - { - rerrno = errno; - error (0, errno, "cannot open %s for writing (prog %s)", - sterr, run_argv[0]); - goto out2; - } - } - - /* Make sure we don't flush this twice, once in the subprocess. */ - fflush (stdout); - fflush (stderr); - - /* The output files, if any, are now created. Do the fork and dups */ -#ifdef HAVE_VFORK - pid = vfork (); -#else - pid = fork (); -#endif - if (pid == 0) - { - if (shin != 0) - { - (void) dup2 (shin, 0); - (void) close (shin); - } - if (shout != 1) - { - (void) dup2 (shout, 1); - (void) close (shout); - } - if (flags & RUN_COMBINED) - (void) dup2 (1, 2); - else if (sherr != 2) - { - (void) dup2 (sherr, 2); - (void) close (sherr); - } - - /* dup'ing is done. try to run it now */ - (void) execvp (run_argv[0], run_argv); - error (0, errno, "cannot exec %s", run_argv[0]); - _exit (127); - } - else if (pid == -1) - { - rerrno = errno; - goto out; - } - - /* the parent. Ignore some signals for now */ -#ifdef POSIX - if (flags & RUN_SIGIGNORE) - { - act.sa_handler = SIG_IGN; - (void) sigemptyset (&act.sa_mask); - act.sa_flags = 0; - (void) sigaction (SIGINT, &act, &iact); - (void) sigaction (SIGQUIT, &act, &qact); - } - else - { - (void) sigemptyset (&sigset_mask); - (void) sigaddset (&sigset_mask, SIGINT); - (void) sigaddset (&sigset_mask, SIGQUIT); - (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask); - } -#else -#ifdef BSD_SIGNALS - if (flags & RUN_SIGIGNORE) - { - memset ((char *) &vec, 0, sizeof (vec)); - vec.sv_handler = SIG_IGN; - (void) sigvec (SIGINT, &vec, &ivec); - (void) sigvec (SIGQUIT, &vec, &qvec); - } - else - mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT)); -#else - istat = signal (SIGINT, SIG_IGN); - qstat = signal (SIGQUIT, SIG_IGN); -#endif -#endif - - /* wait for our process to die and munge return status */ -#ifdef POSIX - while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR) - ; -#else - while ((w = wait (&status)) != pid) - { - if (w == -1 && errno != EINTR) - break; - } -#endif - if (w == -1) - { - rc = -1; - rerrno = errno; - } - else if (WIFEXITED (status)) - rc = WEXITSTATUS (status); - else if (WIFSIGNALED (status)) - { - if (WTERMSIG (status) == SIGPIPE) - error (1, 0, "broken pipe"); - rc = 2; - } - else - rc = 1; - - /* restore the signals */ -#ifdef POSIX - if (flags & RUN_SIGIGNORE) - { - (void) sigaction (SIGINT, &iact, (struct sigaction *) NULL); - (void) sigaction (SIGQUIT, &qact, (struct sigaction *) NULL); - } - else - (void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *) NULL); -#else -#ifdef BSD_SIGNALS - if (flags & RUN_SIGIGNORE) - { - (void) sigvec (SIGINT, &ivec, (struct sigvec *) NULL); - (void) sigvec (SIGQUIT, &qvec, (struct sigvec *) NULL); - } - else - (void) sigsetmask (mask); -#else - (void) signal (SIGINT, istat); - (void) signal (SIGQUIT, qstat); -#endif -#endif - - /* cleanup the open file descriptors */ - out: - if (sterr) - (void) close (sherr); - out2: - if (stout) - (void) close (shout); - out1: - if (stin) - (void) close (shin); - - out0: - if (rerrno) - errno = rerrno; - return (rc); -} - -void -run_print (fp) - FILE *fp; -{ - int i; - - for (i = 0; i < run_argc; i++) - { - (void) fprintf (fp, "%s", run_argv[i]); - if (i != run_argc - 1) - (void) fprintf (fp, " "); - } -} - -FILE * -Popen (cmd, mode) - char *cmd, *mode; -{ - if (trace) - (void) fprintf (stderr, "-> Popen(%s,%s)\n", cmd, mode); - if (noexec) - return (NULL); - return (popen (cmd, mode)); -} - #ifdef lint #ifndef __GNUC__ /* ARGSUSED */ @@ -1031,19 +297,26 @@ gca (rev1, rev2) return (xstrdup (gca)); } -#ifdef LOSING_TMPNAM_FUNCTION -char *tmpnam(char *s) +/* + * Sanity checks and any required fix-up on message passed to RCS via '-m'. + * RCS 5.7 requires that a non-total-whitespace, non-null message be provided + * with '-m'. + */ +char * +make_message_rcslegal (message) + char *message; { - static char value[L_tmpnam+1]; + if ((message == NULL) || (*message == '\0') || isspace (*message)) + { + char *t; + + if (message) + for (t = message; *t; t++) + if (!isspace (*t)) + return message; - if (s){ - strcpy(s,"/tmp/cvsXXXXXX"); - mktemp(s); - return s; - }else{ - strcpy(value,"/tmp/cvsXXXXXX"); - mktemp(s); - return value; + return "*** empty log message ***\n"; } + + return message; } -#endif diff --git a/gnu/usr.bin/cvs/lib/system.h b/gnu/usr.bin/cvs/lib/system.h index 1f35065..20539de 100644 --- a/gnu/usr.bin/cvs/lib/system.h +++ b/gnu/usr.bin/cvs/lib/system.h @@ -17,45 +17,162 @@ /* $CVSid: @(#)system.h 1.18 94/09/25 $ */ +#ifdef __GNUC__ +#ifndef alloca +#define alloca __builtin_alloca +#endif +#else +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#else +#ifdef _AIX +/* AIX alloca decl has to be the first thing in the file, bletch! */ + #pragma alloca +#else /* not _AIX */ +#ifdef ALLOCA_IN_STDLIB + /* then we need do nothing */ +#else +char *alloca (); +#endif /* not ALLOCA_IN_STDLIB */ +#endif /* not _AIX */ +#endif /* not HAVE_ALLOCA_H */ +#endif /* not __GNUS__ */ + #include <sys/types.h> #include <sys/stat.h> -#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */ -#ifndef mode_t -#define mode_t unsigned short -#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISREG +#undef S_ISFIFO +#undef S_ISLNK +#undef S_ISSOCK +#undef S_ISMPB +#undef S_ISMPC +#undef S_ISNWK #endif + +/* Not all systems have S_IFMT, but we probably want to use it if we + do. See ChangeLog for a more detailed discussion. */ + #if !defined(S_ISBLK) && defined(S_IFBLK) -#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +# if defined(S_IFMT) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(m) ((m) & S_IFBLK) +# endif #endif + #if !defined(S_ISCHR) && defined(S_IFCHR) -#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# if defined(S_IFMT) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(m) ((m) & S_IFCHR) +# endif #endif + #if !defined(S_ISDIR) && defined(S_IFDIR) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# if defined(S_IFMT) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# else +# define S_ISDIR(m) ((m) & S_IFDIR) +# endif #endif + #if !defined(S_ISREG) && defined(S_IFREG) -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# if defined(S_IFMT) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# else +# define S_ISREG(m) ((m) & S_IFREG) +# endif #endif + #if !defined(S_ISFIFO) && defined(S_IFIFO) -#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# if defined(S_IFMT) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) ((m) & S_IFIFO) +# endif #endif + #if !defined(S_ISLNK) && defined(S_IFLNK) -#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# if defined(S_IFMT) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(m) ((m) & S_IFLNK) +# endif #endif + #if !defined(S_ISSOCK) && defined(S_IFSOCK) -#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# if defined(S_IFMT) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(m) ((m) & S_IFSOCK) +# endif #endif + #if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ -#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) -#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +# if defined(S_IFMT) +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +# else +# define S_ISMPB(m) ((m) & S_IFMPB) +# define S_ISMPC(m) ((m) & S_IFMPC) +# endif #endif + #if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ -#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +# if defined(S_IFMT) +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +# else +# define S_ISNWK(m) ((m) & S_IFNWK) +# endif #endif + #if !defined(HAVE_MKFIFO) #define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0)) #endif +#ifdef NEED_DECOY_PERMISSIONS /* OS/2, really */ + +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#define S_IRGRP S_IREAD +#define S_IWGRP S_IWRITE +#define S_IXGRP S_IEXEC +#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +#define S_IROTH S_IREAD +#define S_IWOTH S_IWRITE +#define S_IXOTH S_IEXEC +#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) + +#else /* ! NEED_DECOY_PERMISSIONS */ + +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +/* Read, write, and execute by owner. */ +#define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) + +#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */ +#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */ +#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */ +/* Read, write, and execute by group. */ +#define S_IRWXG (S_IRWXU >> 3) + +#define S_IROTH (S_IRGRP >> 3) /* Read by others. */ +#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */ +#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */ +/* Read, write, and execute by others. */ +#define S_IRWXO (S_IRWXG >> 3) +#endif /* !def S_IRUSR */ +#endif /* NEED_DECOY_PERMISSIONS */ + #if defined(POSIX) || defined(HAVE_UNISTD_H) #include <unistd.h> #include <limits.h> @@ -63,25 +180,38 @@ off_t lseek (); #endif -#ifdef TM_IN_SYS_TIME -#include <sys/time.h> +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> #else -#include <time.h> +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#ifdef HAVE_DIRECT_H +#include <direct.h> #endif -#ifndef HAVE_SYS_TIMEB_H +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +#ifdef HAVE_SYS_TIMEB_H +#include <sys/timeb.h> +#else struct timeb { time_t time; /* Seconds since the epoch */ unsigned short millitm; /* Field not used */ -#ifdef timezone - short tzone; -#else short timezone; -#endif short dstflag; /* Field not used */ }; -#else -#include <sys/timeb.h> #endif #if !defined(HAVE_FTIME) && !defined(HAVE_TIMEZONE) @@ -106,10 +236,17 @@ extern long timezone; ** PATH_MAX in terms of MAXPATHLEN. ** 3. If neither is defined, include limits.h and check for ** PATH_MAX again. +** 3.1 If we now have PATHSIZE, define PATH_MAX in terms of that. +** and ignore the rest. Since _POSIX_PATH_MAX (checked for +** next) is the *most* restrictive (smallest) value, if we +** trust _POSIX_PATH_MAX, several of our buffers are too small. ** 4. If PATH_MAX is still not defined but _POSIX_PATH_MAX is, ** then define PATH_MAX in terms of _POSIX_PATH_MAX. ** 5. And if even _POSIX_PATH_MAX doesn't exist just put in ** a reasonable value. +** *. All in all, this is an excellent argument for using pathconf() +** when at all possible. Or better yet, dynamically allocate +** our buffers and use getcwd() not getwd(). ** ** This works on: ** Sun Sparc 10 SunOS 4.1.3 & Solaris 1.2 @@ -118,12 +255,16 @@ extern long timezone; ** IBM RS6000 AIX 3.2 ** Dec Alpha OSF 1 ???? ** Intel 386 BSDI BSD/386 +** Intel 386 SCO OpenServer Release 5 ** Apollo Domain 10.4 ** NEC SVR4 */ -/* On MOST systems this will get you MAXPATHLEN */ +/* On MOST systems this will get you MAXPATHLEN. + Windows NT doesn't have this file, tho. */ +#ifdef HAVE_SYS_PARAM_H #include <sys/param.h> +#endif #ifndef PATH_MAX # ifdef MAXPATHLEN @@ -131,20 +272,28 @@ extern long timezone; # else # include <limits.h> # ifndef PATH_MAX -# ifdef _POSIX_PATH_MAX -# define PATH_MAX _POSIX_PATH_MAX -# else -# define PATH_MAX 1024 -# endif /* _POSIX_PATH_MAX */ -# endif /* PATH_MAX */ +# ifdef PATHSIZE +# define PATH_MAX PATHSIZE +# else /* no PATHSIZE */ +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else +# define PATH_MAX 1024 +# endif /* no _POSIX_PATH_MAX */ +# endif /* no PATHSIZE */ +# endif /* no PATH_MAX */ # endif /* MAXPATHLEN */ #endif /* PATH_MAX */ - - -#ifdef HAVE_UTIME_H +/* The NeXT (without _POSIX_SOURCE, which we don't want) has a utime.h + which doesn't define anything. It would be cleaner to have configure + check for struct utimbuf, but for now I'm checking NeXT here (so I don't + have to debug the configure check across all the machines). */ +#if defined (HAVE_UTIME_H) && !defined (NeXT) #include <utime.h> +#elif defined (HAVE_SYS_UTIME_H) +# include <sys/utime.h> #else #ifndef ALTOS struct utimbuf @@ -179,12 +328,31 @@ int utime (); #define bzero(s, n) memset ((s), 0, (n)) #endif /* bzero */ -#else /* not STDC_HJEADERS and not HAVE_STRING_H */ +#else /* not STDC_HEADERS and not HAVE_STRING_H */ #include <strings.h> /* memory.h and strings.h conflict on some systems. */ #endif /* not STDC_HEADERS and not HAVE_STRING_H */ #include <errno.h> + +/* Not all systems set the same error code on a non-existent-file + error. This tries to ask the question somewhat portably. + On systems that don't have ENOTEXIST, this should behave just like + x == ENOENT. "x" is probably errno, of course. */ + +#ifdef ENOTEXIST +# ifdef EOS2ERR +# define existence_error(x) \ + (((x) == ENOTEXIST) || ((x) == ENOENT) || ((x) == EOS2ERR)) +# else +# define existence_error(x) \ + (((x) == ENOTEXIST) || ((x) == ENOENT)) +# endif +#else +# define existence_error(x) ((x) == ENOENT) +#endif + + #ifdef STDC_HEADERS #include <stdlib.h> #else @@ -201,6 +369,29 @@ char *getcwd (); char *getwd (); #endif +/* check for POSIX signals */ +#if defined(HAVE_SIGACTION) && defined(HAVE_SIGPROCMASK) +# define POSIX_SIGNALS +#endif + +/* MINIX 1.6 doesn't properly support sigaction */ +#if defined(_MINIX) +# undef POSIX_SIGNALS +#endif + +/* If !POSIX, try for BSD.. Reason: 4.4BSD implements these as wrappers */ +#if !defined(POSIX_SIGNALS) +# if defined(HAVE_SIGVEC) && defined(HAVE_SIGSETMASK) && defined(HAVE_SIGBLOCK) +# define BSD_SIGNALS +# endif +#endif + +/* Under OS/2, this must be included _after_ stdio.h; that's why we do + it here. */ +#ifdef USE_OWN_TCPIP_H +#include "tcpip.h" +#endif + #ifdef HAVE_FCNTL_H #include <fcntl.h> #else @@ -220,23 +411,22 @@ char *getwd (); #define R_OK 4 #endif -/* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */ -#if defined(DIRENT) || defined(_POSIX_VERSION) -#include <dirent.h> -#define NLENGTH(dirent) (strlen((dirent)->d_name)) -#else /* not (DIRENT or _POSIX_VERSION) */ -#define dirent direct -#define NLENGTH(dirent) ((dirent)->d_namlen) -#ifdef HAVE_SYS_NDIR_H -#include <sys/ndir.h> -#endif -#ifdef HAVE_SYS_DIR_H -#include <sys/dir.h> -#endif -#ifdef HAVE_NDIR_H -#include <ndir.h> -#endif -#endif /* not (DIRENT or _POSIX_VERSION) */ +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif /* Convert B 512-byte blocks to kilobytes if K is nonzero, otherwise return it unchanged. */ @@ -260,3 +450,47 @@ char *getwd (); #define S_IWOTH 0000002 /* write permission, other */ #endif +/* Under MS-DOS and its derivatives (like Windows NT), mkdir takes only one + argument; permission is handled very differently on those systems than in + in Unix. So we leave such systems a hook on which they can hang their + own definitions. */ +#ifndef CVS_MKDIR +#define CVS_MKDIR mkdir +#endif + +/* Some file systems are case-insensitive. If FOLD_FN_CHAR is + #defined, it maps the character C onto its "canonical" form. In a + case-insensitive system, it would map all alphanumeric characters + to lower case. Under Windows NT, / and \ are both path component + separators, so FOLD_FN_CHAR would map them both to /. */ +#ifndef FOLD_FN_CHAR +#define FOLD_FN_CHAR(c) (c) +#define fnfold(filename) (filename) +#define fncmp strcmp +#endif + +/* Different file systems have different path component separators. + For the VMS port we might need to abstract further back than this. */ +#ifndef ISDIRSEP +#define ISDIRSEP(c) ((c) == '/') +#endif + + +/* On some systems, lines in text files should be terminated with CRLF, + not just LF, and the read and write routines do this translation + for you. LINES_CRLF_TERMINATED is #defined on such systems. + - OPEN_BINARY is the flag to pass to the open function for + untranslated I/O. + - FOPEN_BINARY_READ is the string to pass to fopen to get + untranslated reading. + - FOPEN_BINARY_WRITE is the string to pass to fopen to get + untranslated writing. */ +#if LINES_CRLF_TERMINATED +#define OPEN_BINARY (O_BINARY) +#define FOPEN_BINARY_READ ("rb") +#define FOPEN_BINARY_WRITE ("wb") +#else +#define OPEN_BINARY (0) +#define FOPEN_BINARY_READ ("r") +#define FOPEN_BINARY_WRITE ("w") +#endif diff --git a/gnu/usr.bin/cvs/lib/version.c b/gnu/usr.bin/cvs/lib/version.c index 071b112..2eb66cd 100644 --- a/gnu/usr.bin/cvs/lib/version.c +++ b/gnu/usr.bin/cvs/lib/version.c @@ -13,8 +13,22 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)version.c 1.15 94/10/03 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)version.c 1.15 94/10/03 $"; +USE(rcsid); #endif -char *version_string = "\nConcurrent Versions System (CVS) 1.4 Alpha-2\n"; +char *version_string = "\nConcurrent Versions System (CVS) 1.6.3"; + +#ifdef CLIENT_SUPPORT +#ifdef SERVER_SUPPORT +char *config_string = " (client/server)\n"; +#else +char *config_string = " (client)\n"; +#endif +#else +#ifdef SERVER_SUPPORT +char *config_string = " (server)\n"; +#else +char *config_string = "\n"; +#endif +#endif diff --git a/gnu/usr.bin/cvs/lib/wait.h b/gnu/usr.bin/cvs/lib/wait.h index 2e47773..db60434 100644 --- a/gnu/usr.bin/cvs/lib/wait.h +++ b/gnu/usr.bin/cvs/lib/wait.h @@ -17,6 +17,9 @@ #ifdef HAVE_SYS_WAIT_H #include <sys/types.h> /* For pid_t. */ +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> /* for rusage */ +#endif #include <sys/wait.h> #else #define WIFSTOPPED(w) (((w) & 0xff) == 0x7f) diff --git a/gnu/usr.bin/cvs/mkmodules/mkmodules.c b/gnu/usr.bin/cvs/mkmodules/mkmodules.c index d5611de..9b0e7bd 100644 --- a/gnu/usr.bin/cvs/mkmodules/mkmodules.c +++ b/gnu/usr.bin/cvs/mkmodules/mkmodules.c @@ -14,8 +14,8 @@ #include "cvs.h" #ifndef lint -static char rcsid[] = "$CVSid: @(#)mkmodules.c 1.45 94/09/30 $"; -USE(rcsid) +static const char rcsid[] = "$CVSid: @(#)mkmodules.c 1.45 94/09/30 $"; +USE(rcsid); #endif #ifndef DBLKSIZ @@ -25,6 +25,7 @@ USE(rcsid) char *program_name, *command_name; char *Rcsbin = RCSBIN_DFLT; +char *CVSroot = CVSROOT_DFLT; int noexec = 0; /* Here only to satisfy use in subr.c */ int trace = 0; /* Here only to satisfy use in subr.c */ @@ -42,9 +43,8 @@ static void write_dbmfile PROTO((char *temp)); int main (argc, argv) int argc; - char *argv[]; + char **argv; { - extern char *getenv (); char temp[PATH_MAX]; char *cp, *last, *fname; #ifdef MY_NDBM @@ -64,19 +64,20 @@ main (argc, argv) "a %s file can be used to validate log messages"}, {CVSROOTADM_COMMITINFO, "a %s file can be used to configure 'cvs commit' checking"}, + {CVSROOTADM_TAGINFO, + "a %s file can be used to configure 'cvs tag' checking"}, {CVSROOTADM_IGNORE, "a %s file can be used to specify files to ignore"}, {CVSROOTADM_CHECKOUTLIST, "a %s file can specify extra CVSROOT files to auto-checkout"}, + {CVSROOTADM_WRAPPER, + "a %s file can be used to specify files to treat as wrappers"}, {NULL, NULL}}; /* * Just save the last component of the path for error messages */ - if ((program_name = strrchr (argv[0], '/')) == NULL) - program_name = argv[0]; - else - program_name++; + program_name = last_component (argv[0]); if (argc != 2) mkmodules_usage (); @@ -164,9 +165,15 @@ main (argc, argv) /* * File format: * [<whitespace>]<filename><whitespace><error message><end-of-line> + * + * comment lines begin with '#' */ - for (; fgets (line, sizeof (line), fp) != NULL;) + while (fgets (line, sizeof (line), fp) != NULL) { + /* skip lines starting with # */ + if (line[0] == '#') + continue; + if ((last = strrchr (line, '\n')) != NULL) *last = '\0'; /* strip the newline */ @@ -329,12 +336,14 @@ write_dbmfile (temp) (void) fclose (fp); if (err) { - char dotdir[50], dotpag[50]; + char dotdir[50], dotpag[50], dotdb[50]; (void) sprintf (dotdir, "%s.dir", temp); (void) sprintf (dotpag, "%s.pag", temp); + (void) sprintf (dotdb, "%s.db", temp); (void) unlink_file (dotdir); (void) unlink_file (dotpag); + (void) unlink_file (dotdb); error (1, 0, "DBM creation failed; correct above errors"); } } @@ -343,29 +352,36 @@ static void rename_dbmfile (temp) char *temp; { - char newdir[50], newpag[50]; - char dotdir[50], dotpag[50]; - char bakdir[50], bakpag[50]; + char newdir[50], newpag[50], newdb[50]; + char dotdir[50], dotpag[50], dotdb[50]; + char bakdir[50], bakpag[50], bakdb[50]; (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES); (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES); + (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES); (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES); (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES); + (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES); (void) sprintf (newdir, "%s.dir", temp); (void) sprintf (newpag, "%s.pag", temp); + (void) sprintf (newdb, "%s.db", temp); (void) chmod (newdir, 0666); (void) chmod (newpag, 0666); + (void) chmod (newdb, 0666); /* don't mess with me */ SIG_beginCrSect (); (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */ (void) unlink_file (bakpag); + (void) unlink_file (bakdb); (void) rename (dotdir, bakdir); /* mv modules.dir .#modules.dir */ (void) rename (dotpag, bakpag); /* mv modules.pag .#modules.pag */ + (void) rename (dotdb, bakdb); /* mv modules.db .#modules.db */ (void) rename (newdir, dotdir); /* mv "temp".dir modules.dir */ (void) rename (newpag, dotpag); /* mv "temp".pag modules.pag */ + (void) rename (newdb, dotdb); /* mv "temp".db modules.db */ /* OK -- make my day */ SIG_endCrSect (); @@ -403,6 +419,14 @@ Lock_Cleanup () { } +int server_active = 0; + +void +server_cleanup (sig) + int sig; +{ +} + static void mkmodules_usage () { |