Efficiency. Hard-link-like technology (probably in database) is used for the many virtual copies of things which don't differ from the original: "cheap copies". SERVERS Can use svn+ssh, which is really non-server (ssh invokes individual "svnserve -t" processes for individual OS users). Otherwise, seems that the dedicated svnserve is better than the Apache module, except for the exception that the entire repository is readable to all users. I.e., no fine-grained read control (probably doable with scripts exactly as for cvs). svn+ssh IS REALLY NO SERVER AT ALL! svn+ssh://host/path/to/repos/project FSFS vs. Berkeley DB. FSFS "comes with the subversion package" and uses it by default with "svnadmin create..." for svn 1.2. Can specify bdb with --fs-type switch. The rest of this document assumes svn+ssh. Ssh clients invoke svnserve with -t switch, and optionally -r switch for virtual root, which both restricts like chroot, and has clients specify project roots relative to the virtual root instead of /. Either have to make sure all server-side accounts have sharable umask (in .profile or .ssh/config?); or have all users share one account and maintain one .ssh/authorized_keys file with public keys for all users. Each record like: command="svnserve -t -r /repos/root --tunnel-user=harry"SSHOPTS TYPE KEY username where SSHOPTS are ",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty" (the last field is just a comment). svnadmin and svnlook are server-side only, non-network commands that take normal filepaths, not URLs, as args. Relationship of repositories and projects is same as CVS, except that with svn, revision numbering is global to the repository. RECOMMENDED: Each project root contains only 3 subdirs: /trunk /branches /tags. The project roots can reside anywhere under (or at) the repos root. You just "svn mkdir /proj/root/trunk /proj/root/branches /proj/root/tags". INSTALLATION: # If using svn+ssh, create a dedicated svn user and group. # I like /home/svn as home dir, and /srv/svn as parent of repositories. # Create repos: (defaults to FSFS type). Have one dir with all repos under it. # (unless will never have > 1). svnadmin create /local/path/to/repositories/svnrepos # Create a project: svn mkdir --parents file:///tmp/svnrepos/svnproj/trunk file:///tmp/svnrepos/svnproj/branches file:///tmp/svnrepos/svnproj/tags # N.b.!!!!!! The indicated DIRs and FILEs under the repos do not exist. # The project home directory, like /tmp/svnrepos/svnproj, does # not exist. # Check out empty proj trunk to new working dir as subdir of $PWD: svn co file:///tmp/svnrepos/svnproj/trunk svnproj-trunk (the path between the repos root and /trunk is VIRTUAL and corresponds to your mkdir commands above). (last arg is just name of local directory to create). # Populate your working dir (.../svnproj-trunk). cp... # Since "svn add"s of directories recurse through all files and dirs, just # (if you don't want that, use -N switch) cd .../svnproj-trunk svn add * svn status # Just to check svn commit # See COMMITTING below COMMITTING Either have SVN_EDITOR variable exported and set to any synchronous editor (CLI or gui); or use -m switch, for comment. N.b., update/status differ from CVS! "svn status" shows LOCAL UPDATES ONLY (unless -u switch given). "svn update" will ALWAYS UPDATE from server. (no --dry-run option) "svn status -u:" 3 cols: STATUS * 1234 filename * if there is a server mod to get; # is WORKING COPY version. STATUS OUTPUT COLUMNS Col 1 char #1: file mod Col 1 char #2: prop mod Field 2 if -v: Working rev. (server # when last updated) Field 3/4 if -v: Last commit rev. and committer Note that there isn't an easy way to see the server-side update revision. Can only see your working copy update revision + a * if server has something later. Specify URLs instead of filepaths to base commands (like "svn add") in order to work directly on repository and auto-commit, just like CVS r* commands. I think that "mkdir d && svn add d" === "svn mkdir d". Neither commits (unless URL is specified). Unlike the CVS subdirectory, the .svn subdirectory holds a pristine copy of each file as it exists on the server. .cvsignore: Use svn:ignore property instead* These are directory-specific!! svn propset svn:ignore 'patterns' dir... Since v1.8 there is also svn:global-ignores, which works just as you would want. Keyword expansion is off by default. Can turn it on for non-binary file types (like by filename extensions). N.b. the Svn docs also call revision constants like "HEAD" "keywords". I'm talking about $Keyword$ style keywords here. URL (head), Author (last), Date (last), Rev (last), Id (compilation). (Can use either "Rev" or "Revision"). N.b. there is no keyword suitable for substituting the filename in @(#) or elsewhere (closest is URL, which doesn't cut it). HMM, WOULD $Id$ work here? I'm trying it... Set property svn:keywords.* In "config" file (in config dirs, see below). In [miscellany] section: Id Date Author URL Rev enable-auto-props = yes In [auto-props] section: *.txt = svn:keywords=" Id Date Author URL Rev " etc. Notice the white space at start and end of string, to avoid bugs. TENTATIVE for comment templates: @(#)$Id$ ... @version $Revision$ Copy & pastable property set commands: svn propset svn:keywords ' Id Date Author URL Rev ' filepattern... svn propset svn:eol-style native filepattern... svn propset svn:executable '*' filepattern... VERIFIED!: $Revision$ always does stop the latest revision with a CHANGE to that file, and it works in exports too. * Use "svn proplist -v file..." to list values of properties. Hooks: integration scripts. Samples for authorizing and emailing notifications, but they suck. They get only Repos root and transaction ID as args. No other env. stderr is only sent (or at least shown) if return exit status. CONFIG FILES $HOME/.subversion dir for each user. Main file $HOME/.subversion/config. Several limitations of property file-spec patterns. Leading and trailing whitespace properly trimmed from all names and values (except that white-space at beginning of lines means continuation, of course). Global config dir is supposed to be /etc/subversion, but SuSE doesn't have it. Eclipse's Subversive plugin does not load $HOME/.subversion/config by default, and does a terrible job importing it. See my Eclipse docs for details. CHECK OUT: svn co svn+ssh://svn@loki/svnproj/trunk svnplay -N == CVS -l (current directory only; non-recursive). ECLIPSE. Use the Subclipse plugin, which supports all protocols other than file:///. Subclipse depends on 2 lower-level APIs. Docs say to not enter username or password when defining repositories (in order for caching to work best). However, savings aren't remembered across Eclipse invocations unless you do enter user and key location and "Save information". ??? I guess, ignore their advice. See my HTML HOWTO. NEW: Moving resources doesn't work. Copy then delete instead! CLIENTs Subversion command-line. Binaries for Linux, Windows, et. al. No longer available??? JavaSVN. Pure Java command-line client. SmartSVN. Pure Java GUI build on JavaSVN. Foundation free. Pro $. Looks like only important things Foundation missing are: ignore support, status -u support. Can't tell how good Tag support is. Pro $50 first user, $40/ea after that. When defining repositories, if enter URL, don't enter user@, must do that on the SSH page. Anonymous/user on main page don't matter. Set external editor with Edit/Preferences/External-Tools Enter new file patterns like that for cmd.exe, but set Command to whatever you want, and Arguments as required. SVN Web Client, by Polarion. Requires HTTP SVN protocol. STATUS -v output revision columns are: working-rev last-pulled-committed-ver+author. STATUS -u output N.b. the last-pulled-committed-ver+author is NOT ... what? REVISIONS Every file in the repository is always at the same integer revision level, and these increment repos-globally even for commits of unrelated projects HEAD = The current (highest) revision for all files NOW. This is the actual count of "commits" done ever in this repository. This is shown at the bottom of "svn status -u" (Status against...). BASE* = The working revision of a file (I think). This is the rev of the pristine copy in .svn. COMMITTED* = last committed revision <= BASE. (I.e., last locally known committed revision). PREV* = COMMITTED - 1 * These revision labels only apply to local resources (not URLs). REVISION VISIBILITY The revision of a file that the client has is the Working revision. These are updated from server whenever you run "svn update". Even if you do "svn update" immediately before a "commit", after the commit, your committed (files) will be at HEAD, while the others will be at HEAD-1. To see everything at HEAD level after a commit, do another update. Generally, you want to do "update" before any "commits", because svn will not allow "commit" to commit changes to a server-modified file. When editing a file, the revision of the file remains the BASE for that file. The revision won't increment until you commit. DATES. Can specify a date like '{15:30}' or '{2002-02-17 15:30}' anyplace where a revision is allowed. (Subversion just commits the date to the HEAD revision number at that time). CONFLICTS Have to deal with conflicts when merging and when updating (when you have uncommitted edits). Definitions mine: WHICH for your uncommitted work file theirs: WHICH for incoming file (what you would get with "revert") Conflicted non-committed file marked with status C. Non-interactive: Use switch --non-interactive with svn update|merge. (After-the-fact, for individual files you can specify 'p' Postpone action). For non-interactive, you get the main working file edited up with <<>>, and you also have temp copies of original "theirs" and "mine" files. You can't commit until you "resolve". svn resolve --accept C-file... # Also removes temp files. ACTIONs: working The working file that you have manually fixed. base Revert-ish. Version you last "checked out?" before editing. -full Accept file with no in-file merging. -conflict Accept conflicts and merged non-conflicting. Interactive. You are given a CLI menu for conflict res actions. Actions: s: Show all actions (a.o.t. most-commonly-used for current state) e: Open file with synchronous editor $SVN_EDIT When done, you'll want to "r" if successful or "p" otherwise. p: Postpone. Do nothing so you can resolve non-interactively. dc: Display-Conflict. Show diff. mc/tc: -Conflict. Accept non-conflict merges + conflicting mf/tf: -Full. Accept file. No in-file merging. BRANCHING. Create a branch: svn copy svn+http://.../trunk svn+http://.../branches/newbranch (N.b. unlike "cp" or "rsync", the final source element 'trunk' will not be created at the destination). A branch is just a dir-branch made from a copy of another branch, and from the branch's perspective the history in the parent branch is visible until the point of the fork (from which point, only the history in this branch is visible). Therefore, branches are just identified by the branches subdirectory. MERGING: To merge mods of one branch into another, you run a command which effectively does a diff in branch A and updates WORKING FILES in $PWD! svn merge [-r r1:r2] src-url1[@r1] [src-url2[@r2]] [dest-dir] (Most commonly will want to UPDATE-MERGE. See below about that). svn merge -r 343:344 http://svn.example.com/repos/calc/trunk See CONFLICTS section. Can tell merge to ignore ancestry (like if you are comparing branches from independent external imports). UPDATE-MERGE ENTIRE BRANCH SINCE FORK. Repeatedly for Features Branches. "svn merge parent-source-url" (since e.g. svn merge ^/patterns/trunk Find fork point with "svn log -v --stop-on-copy svn+ssh://.../branches/x". "svn merge -r 341:405 http://.../branches/x". To do same from last merge point, examine log the same way again to find start point. Can delete branches after merges if not going to do more work in them (and more incremental merges). I think manual says that these are soft-deleted automatically. FEATURES BRANCHES. Instead of merging changes since fork point, the side branch is regularly updated with changes from the trunk. When the branch work is finished, it's updated with all remaining trunk mods, then merge is used to update the trunk to the branch state: "svn merge svn+ssh://.../trunk@HEAD svn_ssh://.../branches/x@HEAD [$TRUNKDIR]" Much much easier with --reintegrate switch: svn merge ^/calc/trunk svn commit -m 'Final merge from trunk to my-calc-branch' cd .../trunk svn update svn merge --reintegrate ^/calc/branches/my-calc-branch svn commit -m 'Merged my-calc-branch back into trunk' BACKING OUT PREVIOUS WORK IMPORTANT: -c X === -r (X-1:X) (i.e. revision CHANGE resulting in X) Use merge with reverse-order revision range. To back out a single revision, 303: svn merge -c -303... # Backs out revision 303. === svn merge -r 303:302 To back out more: svn merge -r 303:302 # FROM last revision YOU DO NOT WANT # TO last revision BEFORE THAT, that YOU DO WANT RESURRECTING files Use "svn log -v" to find file name and revision. Use "svn copy -r" to resurrect. SWITCHING updates a dir according to another branch (i.e., changes it to that other branch). TAG = A branch that you don't commit to. Create by "svn copying" to create a new branch, like above. Or "svn copy working-dir svn+ssh://tags/newtag", to make it out of a totally customized working directory. EOLs. \n's internal (Subversion calls \n's LF). svn:eol-style of native does what you would think. EXPORTS are r/w. Can't fix by using umask either because the recursive writes fail. :( Switch --dry-run = CVS's -n. No need to tag starting point of branches. Can always determine that by running "svn log --stop-on-copy svn+ssh://brach/root" (if run on working copy root, make sure to update first). "svn cat" is like "cvs update -p". !!! DOES NOT DO WHAT YOU WANT!!!: This diffs from v 2908 to 297 of each of the two resources. Closest to "cvs -q rdiff -s..." (only works with Gnu diff: svn diff --diff-cmd diff -x -q -r298:297 svn+ssh://svn@iscm/admc/mmo/trunk svn+ssh://svn@iscm/admc/mmo/branches/ACTION_REFACTOR With scripting: svn diff -r298:297 svn+ssh://svn@iscm/admc/mmo/trunk svn+ssh://svn@iscm/admc/mmo/branches/ACTION_REFACTOR | grep ^Index: This lists all of the COMMITS between rev 1 and rev2: svn log -q -v -r1:2 svn+ssh://svn@iscm/admc/projroot This lists diffs (very verbosely) between b1/r1 and b2/r2 svn diff -r1:2 svn+ssh://svn@iscm/admc/branches/r1 svn+ssh://svn@iscm/admc/branches/r2 To fix file permissions (including executable bit), you must remove the file and re-create it. There is an svn:executable property, but if that works at all, it will only work to turn the execute bit(s) on. To create new project FOR MY CUSTOM SYSTEM. On the server host, as the repository owner Create the user ACL file, like /home/svn/repositories/admc/authorizations/users.bgi Create the project base (virtual) dir, using a file:/// URL, like svn mkdir -m 'Project for Standalone BGI stuff' file:///home/svn/repositories/admc/bgi Then use external svn:ssh URLs for everything else, starting with svn mkdir -m 'Trunk for bgi project' svn+ssh://svn@iscm/admc/bgi/trunk cvs rdiff -s... clone: (One line for each file added or deleted, 2 for mods) svn diff $SVNURL/first $SVNURL/second | perl -nwe 'print if /^(\Q+++\E)|(\Q---\E)/ && !/revision 0\)$/;' logs file file creation deletions are logged with the directory artifact, not the artifact being added/removed. Must use "log -v.. dir" to show the artifacts. "log" shows log entries for copy points although no content change occurs. "log -v" shows files modified by commits. GOTCHA! You must update your repos to see all commits with log [-v]. If you use --stop-on-copy, you will see the original copy to the current location, but nothing at all before. It's significant that you can do "diff", "cat", etc. on revisions before the artifact moved/copied to the current location. E.g., if "log --stop-on-copy" shows that copied to current location at rev 100, you can still "svn cat" at rev 50 if the artifact was added at 50 or before. For recursive log reports, make sure to "update" before running "log". I don't know why, but without updating the entire branch, only selective commits are displayed (even though missing entries were committed from this same work area). svn mkdir --parents... --parents script is like -p switch to "mkdir -p...". TO ls/log/cat removed artifacts, the -r switch doesn't work. Must use artifactname@REV (presumably or -rREV artifactname) syntax. -rX and resource@X are generally interchangeable. Seems that Subversion assumes all files with non-null mime-type that is not text/* are binary, and svn:eol-style settings on these fail. Specifically you can set svn:mime-type non-text/* and then set svn:eol-style. Work-around seems to be to MANUALLY set the mime-type on these, since if you set it via auto-props, it's impossible to set the eol-style. Quite ambiguously, svn usually does not mean RCS keywords by "keywords", but the following magic revision variables: HEAD BASE COMMITTED PREV INTER-PROJECT SWITCHING You can assemble a working area using switches (which show up with a S status column), and copy the whole work branch to a revision or tag, but this new copy will be created with copies of the switched-to locations (it will not retain any actual switches). There must be an actual file or directory node in the base project to be taken over as the switch point, and it must be committed to the repos. Use file to link to file, dir to link to dir, because reversion doesn't work right for file-> dir linking. May need to use "svn switch --ignore-ancestry..." to do this. Can write usage instructions inside link files, or in README files within link directories. "svn ls -v" corresponds to "ls -l". FILE MODIFICATION TIMES Subversion info times do not correspond to any time on client machine, but "commit times". Export timestamps are really last commit times for the files-- what one would expect. Checkout/Update times, however, always reflect the time that this client updated this file. TORTOISE config file. %APPDATA%/Roaming/Subversion/config When you propget to redirect to a file (most importantly, for svn:ignore), make sure to use the --strict switch to eliminate extra formatting characters. NEW FEATURE >= v1.6 ^/ = the server repository root. Can ascertain with "svn info". Note that if the work area was checked out from ROOT/x/y, ^/ still equals ROOT/. >= v1.4 Merge tracking. Can just "svn merge /source/branch" to update-merge. >= ?. merge --reintegrate switch. Repository Maintenance 'svnadmin recover' only unlocks a database. Determine DB type: cat .../repositories/repos_name/db/fs-type Third party script 'fsfsverify.py' is fsfs-type-specific (and still did not work for me). svnadmin verify [-q] [-r R] [-r R:HEAD] path/to/repos_name # useful exit stat. NOT 100% RELIABLE since does not complain about some revisions that will not back up successfully! svnadmin dump [-q] [-r R] [-r R:HEAD] path/to/repos_name > path/to/file.svndump The -q effects what writes to stderr. Update repository for workspace: Pre-v1.7: svn switch --relocate Post-v1.7: svn relocate svnkit. SUCKS! Config files at ~/.subversion or ~/Application Data/Subversion tortoiseSVN for command-line SUCK!! REVISIONS -r4 == '-r 4') == -r 4:HEAD == changes AFTER r4. I.e. '-r' DOES NOT INCLUDE r4 CHANGE! -c4 == -r 3:4 == changes for r4