BRL-CAD Concurrent Versions System Policy and Guidelines ======================================================== ********************************************************************** NOTE: THESE INSTRUCTIONS ARE OUT-OF-DATE AS BRL-CAD NOW USES SUBVERSION INSTEAD OF CVS. CONSULT WITH CAUTION AS SOME PORTIONS ARE STILL RELEVANT BUT OTHERS THAT ARE CVS-SPECIFIC ARE NOT. ********************************************************************** The document includes a set of requirements and recommended procedures for how to effectively use CVS with this project. Included are details on checking code in and out, effective use of commit messages, tag naming conventions, branch management, making releases, and more. Basic knowledge of CVS, its capabilities, and to a lesser extent its limitations are beyond the scope of this document. See the CVS website and the "References" section for more information. That said, the following general rules should always be adhered to: 1. Code committed against HEAD should at least be tested and compile for the developer committing changes. "Don't break it." 2. There is a STABLE branch that may only have code committed against it that a) compiles on all relevant platforms, b) does not detrimentally degrade performance, and c) runs correctly passing available validation tests. 3. Branches should be used for experimental work that would otherwise leave HEAD in an inconsistent, non-compiling, or untestable state. Branches are not merged into HEAD until the code has been tested for correct and proper functionality. 4. Commit and update frequently. Large or complicated changes should be broken up into smaller succinct commits as best possible to make reviewing easier. Commit working code early and commit often. 5. Be consistent and informative. Tags need to follow a consistent naming convention. Commit messages need to be informative. Source code changes should follow existing style and be usefully commented. TABLE OF CONTENTS ----------------- Introduction Table of Contents Overview Checking out sources from HEAD from STABLE from a branch/revision Checking in sources commit messages to HEAD to STABLE to a branch/revision Tag naming convention for releases for branches Making a release creating a maintenance branch applying release patches Merging a branch merging a branch into HEAD merging HEAD into a branch Making a branch Usage Tips References OVERVIEW -------- Code organizational sanity and consistency are of paramount importance. As CVS can both help and hinder efforts to keep the sources organized, the policy and guidelines outlined herein overview the manner in which CVS should be used with this project. This CVS policy and guidelines document should be used in combination with a developer's guide that covers other aspects of code contributions and developer behavior such as how and where to provide patches, what coding styles should be used, and how to make releases. Likewise, exact semantics of testing policy, requirements for migrating code from HEAD to STABLE, basic CVS usage, and release policy are outside the scope of this document. The intent of this document is to cover the broad aspects of how CVS is to be organized and used. This includes branch management, tag naming conventions, checking sources in and out, and other relevant usage policy. CVS branches should be used to separate out the various types of source code revisions. These include: 1) current or "active" sources 2) stable sources 3) releases 4) experimental sources The CVS HEAD is for active development. A separate STABLE branch exists to hold a revision of the sources that is validated and tested across platforms. Releases should only made off of stable revisions of the source code. When a release is made, a maintenance branch may and should exist for making patch releases. Finally, various experimental and developer undertakings that may benefit by being isolated from changes to HEAD or STABLE, or would likewise be disruptive to normal HEAD functionality, may live on a branch of their own. It's very important that the main CVS HEAD revision be a "mostly" stable code base so that at any given time users may download the latest sources, get the source code to compile with minimal effort, and have the core components mostly function as expected. See the "Checking in sources to HEAD" section for more details. Some users and developers will need from time to time to obtain a relatively recent version of the sources that is certain or at least expected to "work". This is what the STABLE branch is for. See the "Checking in sources to STABLE" section for more information. CHECKING OUT SOURCES -------------------- There are two basic ways to check out the sources: anonymously or as a developer. Developers have full read-write access to the source code. Anonymous users have read-only access; and the version of the sources checked out may actually lag the most recent commits by several hours. Regardless of whether the sources are being checked out by a developer or anonymously, CVS_RSH should be set to "ssh". Anonymous users will need to login first: cvs -d :pserver:anonymous@brlcad.brlcad.cvs.sf.net:/cvsroot/brlcad login Once logged in, the access method for anonymous users will be via :pserver: (provide an empty password if prompted). Developers will checkout using the default implicit :ext: method and will need to provide an authorized username. Be sure to use the "-P" checkout option to remove (prune) empty directories. The "-z9" global CVS option is encouraged to minimize network utilization. FROM HEAD: As anonymous: cvs -z9 -d :pserver:anonymous@brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P brlcad As a developer: cvs -z9 -d @brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P brlcad FROM STABLE: Checking out from STABLE is like checking out from any specific revision or branch -- provide the tag name upon checkout: As anonymous: cvs -z9 -d :pserver:anonymous@brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P -r STABLE brlcad As a developer: cvs -z9 -d @brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P -r STABLE brlcad FROM A BRANCH/REVISION: The process for checking out a given revision number is pretty much a matter of knowing which revision tag name is desired, and then checking out with that symbolic tag name: As anonymous: cvs -z9 -d :pserver:anonymous@brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P -r brlcad As a developer: cvs -z9 -d @brlcad.cvs.sf.net:/cvsroot/brlcad checkout -P -r brlcad If the tag names are not known, you can check out HEAD or STABLE and perform a "cvs log" on any file to see the known tag names: cd brlcad cvs status -v README Once you find the revision or branch tag name in question, you can "cvs update" to that revision: cvs -z9 update -r See the CVS manual documentation for more information. CHECKING IN SOURCES ------------------- Commit access is generally provided on a case-by-case basis as the need arises after careful consideration of the individual's ability to follow the project's coding style, follow this CVS commit policy, and work productively with the other developers. If you have to ask for commit access, you probably won't get it. So don't ask. Provide patches and interact with the existing developers. If you are given commit access, you are expected to read and follow this policy and its guidelines as well as the developer's guide. COMMIT MESSAGES: When code is committed to CVS, a commit message should be provided that appropriately describes the change in a succinct and useful manner. It's important to remember that CVS commit comments are stored per-file. Care should be taken to not be vague, undescriptive, or ambiguous often tailoring commit messages to individual files even if the change was made as part of a larger modification. While the comments need to be descriptive and informative, they should also be as short and to the point as possible. The CVS commit comments are not the appropriate place for legal disclaimers, usage documentation, extensive debugging details, business politics, or mini novels of who/what/when/where/why. They should be no more than a few lines long at most, normally only consisting of a short single-line statement. CVS commit comments are for documenting changes so that other developers may derive a clue about what the change was without necessarily needing to look at the actual source differences. Instead of making generic statements about fixing or improving the source code, succinctly describe the change in a manner that may be useful for someone else reading the comment that is not familiar with the change. If the changes are merely formatting or whitespace adjustments, a simple commit message of "ws" or "M-x indent-region" is generally understood and sufficient. If a file is being moved/renamed, the comment should include both where the file is moved from and where it was moved to in the message, e.g. "moved from librt/bool.c to src/librt/bool.c". TO HEAD: It's very important that the main CVS HEAD revision be a "mostly" stable code base so that at any given time users may download the latest source, get it to compile with minimal effort, and have the core components mostly function as expected. HEAD is where active development takes places, so this necessarily means that there may be small windows of opportunity where the sources do not compile, but those should not persist. Likewise, since the developers potentially and often have specific or relatively independent development efforts, the CVS commits of one developer should be made in a means that will have limited or understood impact on other developers. This minimally means that CVS commits need to at least work for the developer committing the changes. If it's discovered that the changes break other major supported platforms, the broken state should not be allowed to linger. Effort and arrangements need to be made to either resolve the problem promptly or the changes should be reverted. TO STABLE: Code being committed to STABLE require special attention and just a little more detail in their commit messages. Some users and developers will need from time to time to obtain a relatively recent version of the sources but a version that is certain or at least expected to "work". This shall be known as the STABLE branch. For code to make it to the STABLE branch, it needs to pass several criteria and commits should be well documented indicating the nature, purpose, and impact of the change. These criteria include the following: a) Compiles -- The code must compile on all supported platforms. This generally requires coordination with other developers if access to other platforms is limited. b) Improves -- Without reasonable justification, the changes must not degrade performance, be inconsistent in style or behavior of the sources, or break support for existing functionality. c) Passes -- The changes must successfully pass any available validation tests. If the tests are flawed or simply require modification, justification and explanation should be provided along with fixes to the tests before submitting code that would otherwise not pass validation. TO A BRANCH/REVISION: Checking in code to a branch other than STABLE generally do not necessarily need to follow the same requirements of compiling cleaning or "working" until those changes are merged back in to either STABLE or HEAD unless the branch is a release maintenance branch. Release maintenance branches generally follow the same commit rules as checking into HEAD. As it's not possible to commit against a non-branch checkout (such as a release tag export like "rel-7-0"), changes to a revision that is otherwise immutable can still be made by performing a "diff" against a clean checkout of that same revision (unmodified) and then applying the patch to a maintenance branch or to the CVS HEAD. See the "Applying release patches" under "Making a release" for examples. TAG NAMING CONVENTIONS ---------------------- [-][-][-] ::= { rel, ansi, jra, sean, ppc, ... } The is normally something short and concise. For release tags, it is "rel". Other branch keywords may indicate functionality, developers, etc. ::= [-[-]] (e.g., 6-0-2 and 5-2) The numbering convention is fairly standard and common practice: is the major revision number and changes relatively infrequently. Major releases contain significant architectural changes and are not generally backwards compatible with existing code or data to some extent. They may require data and/or code to be converted in order to upgrade. is the minor revision number a usually changes on a fairly regular basis. As new features are added and releases are made, the minor revision number changes to reflect those additions. Odd minor numbers indicate a developer revision. Even minor numbers indicate a release revision. Minor releases may or may not be backwards compatible. is the patch revision number and is used to make bug fixes on an as-needed basis. These changes are not usually as extensively tested as minor and major releases, but they should always be backwards compatible. := YYYYMMDD (e.g., 20021231) When creating tags, the date a tag is made is sometimes noted and useful or even recommended. The format of the date is YYYYMMDD and should correspond to tag creation dates. Branch and release tags should not include a date in the tag name. For branches the comment is "branch". For releases, it is left blank. For other miscellaneous tags, the comment is up to the person tagging. Note that hyphens (-) are used, not underscores. Periods (.) are never used because they upset CVS's branch naming. Tag names (including branches) should be in lower case only. Upper case is reserved for HEAD and STABLE. Non-branch tags are used for 1) marking a release, 2) merging branches, and 3) miscellaneous tags (e.g., to remember a specific starting or stopping point). FOR RELEASES: Release tags are used to indicate a revision of the source code that was made publicly available. It should match the sources released, bugs and all, so that proper and useful diffs may be made. The format for release tags is: rel- ::= -[-] The revision number should match the version reported by the source code. Examples: rel-6-0 rel-7-2-5 FOR BRANCHES: Branches are symbolic tags that are used to denote and allow separate isolated revisions of code. The following format should be used for branch tags: [-]-branch ::= describes the purpose of the branch. If the branch is a maintenance branch, the keyword should match the revision's keyword and revision. Examples: rel-7-0-branch ansi-branch photonmap-branch FORMAT FOR MERGES: When joining a branch that has a large set of changes into HEAD, there should be a tag before and after the merge. When joining HEAD into a branch, tagging is optional. Joins into head should be tagged, though. On HEAD before the merge, use the following tag format: premerge[-][-]- Example: premerge-20040404-ansi On HEAD after the merge, use the following tag format: postmerge[-][-]- Example: postmerge-20040315-windows The should be a simple identifying keyword. The branch keyword being merged suffices quite well. A date is preferred. If development on a particular branch is considered "final", it should be marked with a "freeze" tag on the branch: [-][-]-freeze Example: windows-20040315-freeze If applying patches directly to the CVS HEAD where there is potential to need to revert the changes, using "pre" and "post" as the tag comment is appropriate. [-][-]-pre [-][-]-post Examples: hartley-6-0-pre ctj-4-5-post offsite-5-3-pre FORMAT FOR MISCELLANEOUS TAGS: For everything else, the basic structure of the tags still applies. Lower case, dash-separated, and hopefully succinctly informative tag names should be used. Dates are highly recommended as CVS tags are not associated by date, but rather to the individual revision of a file. [-][-][-] When in doubt, tag. It's easy to remove or rename tags, but it can be rather complicated to create one after the fact. MAKING A RELEASE ---------------- Making a release is generally a matter of making sure that all desired code changes are committed and well tested. Code committed to HEAD should be well tested by performing any validation steps required to migrate those changes to STABLE. Once HEAD is verified, it may be joined into STABLE, which should likewise be thoroughly tested. Once all the code is verified, the release should be tagged: cvs tag rel- Revision numbers should follow a numbering convention consistent with the developer guidelines. Minor and patch revision numbers that are odd numbered are used to denote developer revisions of the source code. Release tags should use even numbers for the minor and patch revision numbers. See the "Tag naming convention" section for more details. CREATING A MAINTENANCE BRANCH: If minor maintenance is expected to continue or development on HEAD needs to continue in an incompatible manner, a maintenance branch should be made: cvs rtag -r rel- -b rel--branch That will create a branch named rel--branch anchored at the same revision of the sources as rel-. APPLYING RELEASE PATCHES: In general, unified diff patches should be requested from people providing patches whenever possible. For people working with a cvs checkout, generating a diff is generally a simple matter of running: cvs diff -u > my.patch This will create a unified diff patch file called "my.patch" that is trivially applied to a checkout using the patch command: patch -p0 < my.patch Assuming there have not been overlapping/conflicting changes, patch should say on the first attempt that all chunks were successfully applied. If not, there will be reject files that will have changes that need to be manually reviewed and resolved.. Users who are perhaps working off of a source tarball instead of a CVS checkout can fairly easily generate a unified diff patch by comparing their modified tree against an unmodified source tree: diff -u brlcad brlcad.modified > my.patch In this example, brlcad is an unmodified top-level source directory and "brlcad.modified" is a top-level source directory that has been modified. That same patch file may then be applied to a checkout and committed to CVS. If there is an active maintenance branch that a patch was started from, it is generally easier to apply the patch to the maintenance branch first and then join the maintenance branch into HEAD per the usual means to join branch changes into HEAD instead of merging directly into the CVS HEAD. HEAD will have often changed too significantly and may result in patch being unable to resolve chunks. See the patch manual documentation for more details. Failing any of the above, patches may also be applied manually. This is often even desirable to become more familiar with the changes being made providing an opportunity for a healthy peer review. MERGING A BRANCH ---------------- CVS branches can be both very useful or difficult. Proper usage of branches generally requires a firm understanding of CVS otherwise complications and mistakes are bound to happen as there are several usage pitfalls. Following the directions shown below should help avoid the pitfalls and assist in making effective use of a CVS branch. Regardless if the branch is a maintenance branch or the STABLE branch, the fundamental "action" in performing a merge is called a CVS join. You can join code committed to one branch revision into another branch revision by passing the "-j" option to the cvs update command. It's important to note that joins occur from code that is already committed. Once the code is joined, it may be committed to CVS. If you have changes on a branch that have not yet been committed, they should first be committed before attempting the join. MERGING A BRANCH INTO HEAD: Merging branches in CVS is generally a matter of "joining" in the changes from one the branch revision into another branch revision. HEAD can itself be considered a branch. If, for example, you have a maintenance branch named "rel-7-0-branch" that has had changes applied to it and you wish to apply those same changes to HEAD you would checkout HEAD: cvs -z9 -d checkout -kk -P brlcad The join the changes from the desired branch: cvs update -kk -dP cvs tag premerge--- cvs update -kk -dP -j rel-7-0-branch [ resolve conflicts & test ] cvs commit cvs tag postmerge--- The comment should contain or be the keyword of the branch being merged in. For example, "premerge-20040315-windows" indicates a merge that occurred on March 15th, 2004, from the windows-branch. If there are any conflicts, they would of course need to be manually resolved. Once any conflicts are resolved and the sources are tested for functionality, the changes could then be committed to HEAD. Be careful to notice any file additions or deletions as CVS, generally speaking, will not manage those for you until they are committed. In both the checkout and the join update, the -kk flag was used to cause CVS to not expand the CVS variables such as \$Revision\$ and \$Id\$. If those keyword variables are expanded, you're likely to have many conflicts depending on time-stamps. If the branch being checked in an extensive set of changes, testing is essential. It is also a good idea to warn other developers of what you are planning to do. You should indicate what parts of the system will be affected and coordinate in detail with other developers as appropriate. MERGING HEAD INTO A BRANCH: Merging changes from HEAD into a branch (such as STABLE) is not really any different than the scenario described for merging a branch into HEAD. Again, HEAD itself can be simply considered a branch too. For example with an existing branch checkout: cvs update -kk cvs update -kk -j HEAD [resolve conflicts & test ] cvs commit The important step to realize is that your working checkout needs to be the revision you want to join changes into. When in doubt, commit all your changes to the appropriate branch, delete all your sources, and checkout fresh. "cvs status README" will tell you what revision you have checked out. It will be listed as a "Sticky Tag". MAKING A BRANCH --------------- Making a branch is a matter of using the "-b" option when tagging. Branch tags are special symbolic CVS tags that allow for isolated changes and controlled merging of modifications. CASE 1: You have no checked out source code revision: If you want to use the latest sources on the CVS HEAD as the starting point for your branch: cvs rtag -b -branch brlcad cvs checkout -r -branch brlcad If you want to use some other tag as the starting point for your branch: cvs rtag -b -r -branch brlcad cvs checkout -r -branch brlcad CASE 2: You have a checked out revision with all changes committed and you want to tag it and start using it as a branch: cvs tag -b -branch cvs update -dP -r -branch CVS will denote the tag as a "sticky" tag to indicate which version is currently checked out. You can verify this by using 'cvs status', for example: cvs status README To see all tags that have been set for a particular file in addition to checkout status, use the "-v" status option: cvs status -v README or via the generally more detailed file log: cvs log README USAGE TIPS ---------- 0) Checkout with the -P option to prune empty directories: cvs checkout -P brlcad 1) Be sure to perform an "update -dP" to check for new directories from time to time and to prune empty ones. cvs update -dP 2) Update and commit frequently. Frequent updates are a good thing. Perform them often to minimize conflicts. 4) A convenient way to check what files have been modified when you are certain that you've not added files: cvs -q update | grep -v \? That performs a quiet update (the directory tree isn't output) and ignores lines that have a question mark (normally build files). 3) To avoid having to specify the repository for a checkout, you can set your CVSROOT environment variable to the location of the repository: For anonymous users: export CVSROOT=:pserver:anonymous@brlcad.cvs.sf.net:/cvsroot/brlcad For developers with accounts: export CVSROOT=@brlcad.cvs.sf.net:/cvsroot/brlcad 5) CVS does not manage directories so care must be taken when creating new directories or moving things around. Do NOT add directories that include non-static information as part of the directory name. Examples include adding directories that include a version in their name (e.g. libtcl8), indicate age (e.g. newRt), or contain other volatile data. 6) If you need to move a directory, rethink why and try not to. If you still need to move a directory, .. try to find another way to cope. If you still REALLY need to move a directory, a little find scripting can make it easy. In case you didn't get the hint DO NOT MOVE/RENAME DIRECTORIES unless there's a _very_ good reason. This implicitly means that extra care and thought should be made when naming new directories to be added to the repository. 7) It is possible to move directories using a little find scripting. Assuming you've not got build files cluttering things up and are ready to simply move files to a new location in CVS, an otherwise complicated task becomes mostly simple with a few find scripts. Here is an example where a directory ./foo/ is moved to a new location ./src/foo2/: cd foo # copy the files to their new home find . -type d -not -name CVS -exec mkdir -p ../src/foo2/{} \; find . -type f -not -regex '.*/CVS/.*' -exec cp -p {} ../src/foo2/{} \; cd ../src # add the new directories to cvs find foo2 -type d -not -name CVS -exec cvs add {} \; find foo2 -type f -not -regex '.*/CVS/.*' -exec cvs add {} \; # commit the new sources cvs commit -m "moved from foo to src/foo" foo2 cd .. # delete the old source subtree find foo -type f -not -regex '.*/CVS/.*' -exec rm {} \; find foo -type d -not -name CVS -exec rm -f {} \; -exec cvs delete {} \; cvs commit -m "moved from foo to src/foo" foo cvs update -P foo If you don't have CVS keys set up and have too many files to move, each find script could be passed to xargs so that only a single password is prompted instead of one per file. Since moving/renamed directories in CVS is "bad", though, that's left as an exercise to the reader. 8) When renaming files, be sure to indicate both where the file was moved from and where it was moved to and/or the name before the move and the name after the move. Do this both on committing the file deletion and on committing the file addition. This way, people reading the file's log will know where to find the rest of the file's history. 9) If experiencing trouble consolidating conflicts that involve CVS "$" variables, set the keyword sticky flag which will tell CVS to not perform the keyword variable expansions. Perform a "cvs update -kk" and the conflicts should go away. 10) Denote binary files as binary in CVS via the CVSROOT/cvswrappers administrative file or via the "-kb" option when adding files. In the CVSROOT/cvswrappers file, the following line would denote files ending in ".pix" as binary: *.pix -k 'b' You can likewise denote arbitrary files as binary via the "-kb" option on commit: cvs add -kb new_file_name_here If the file has already been added to CVS, it may be administratively marked as a binary file after the fact using the "cvs admin" command: cvs admin -kb some_existing_file cvs update -A some_existing_file REFERENCES ---------- This CVS policy and guidelines document was in part derived off some of the principal ideas of other projects' usage of CVS such as the Linux Documentation Project, the XFree86 DRI project, Zope, FreeBSD, and AT&T's SML/NJ among others. Version Management with CVS https://www.cvshome.org/docs/manual/ CVS Best Practices http://www.magic-cauldron.com/cm/cvs-bestpractices/index.html XFree86 Direct Rendering Infrastructure CvsPolicy http://dri.sourceforge.net/cgi-bin/moin.cgi/CvsPolicy http://dri.sourceforge.net/doc/cvspolicy.txt Zope http://dev.zope.org/CVS/ZopeReleasePolicy FreeBSD http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/cvs-tags.html AT&T SML/NJ http://www.smlnj.org/DEV/policy.html http://cm.bell-labs.com/cm/cs/what/smlnj/DEV/policy.html --- This document was initially written in 2002 and later almost completely rewritten in 2004 by Christopher Sean Morrison.