Subversion

Through the use YUM, I have installed subversion:

Name   : subversion
Arch   : i386
Version: 1.2.1
Release: 0.1.1.fc2.rf
Size   : 7.82 MB
Group  : Development/Tools
Repo   : Locally Installed
Summary: A Concurrent Versioning system similar to, but better than, CVS.
Description:
 Subversion is a concurrent version control system which enables one or more
users to collaborate in developing and maintaining a hierarchy of files and
directories while keeping a history of all changes.  Subversion only stores
the differences between versions, instead of every complete file.  Subversion
also keeps a log of who, when, and why changes occured.

I am going to use subversion to ease the development of Ruby on Rails projects. Installing Ruby on Rails is the subject of another section.

Extending the YUM repository list

You will not find the latest version of subversion packaged in Fedora Core 2. Fortunately, there are some serious packagers out there:

http://dag.wieers.com/home-made/apt/FAQ.php#B4

To have the YUM installer look in other repositories, go to: /etc/yum.conf, and add (for example) the following entry:

[dag]
name=Dag RPM Repository for Fedora Core 
baseurl=http://apt.sw.be/fedora/$releasever/en/$basearch/dag
gpgcheck=0
enabled=1

The next time you issue the command yum info subversion, yum will look for updates and new packages in the url you just added.

Installing Apache 2

Subversion works best in combination with Apache 2. Since I already have Apache 1.3 installed, this requires some fine tuning.

Apache 2 must be made to listen only to port 8080, which will be the port we are going to use for Subversion. As an added bonus, we could do some php5 experiments on Apache 2.

First, I installed Apache2 through Yum:

[root@1038 root]# yum install httpd
Gathering header information file(s) from server(s)
Server: Fedora Linux 2 - i386 - core
Server: Dag RPM Repository for Fedora Core
Server: Fedora Linux 2 - i386 - freshrpms
Server: Fedora Linux 2 - i386 - updates
Finding updated packages
Downloading needed headers
Resolving dependencies
Dependencies resolved
I will do the following:
[install: httpd 2.0.51-2.9.i386]
Is this ok [y/N]: y
Downloading Packages
Getting httpd-2.0.51-2.9.i386.rpm
httpd-2.0.51-2.9.i386.rpm 100% |=========================| 905 kB    00:00
Running test transaction:
Test transaction complete, Success!
httpd 100 % done 1/1
Installed:  httpd 2.0.51-2.9.i386
Transaction(s) Complete

Here's the location of the daemon:

[root@1038 root]# find / -name httpd
/etc/httpd
/etc/logrotate.d/httpd
/etc/rc.d/init.d/httpd
/home/httpd
/var/tmp/bind/httpd
/var/tmp/.i/.i/httpd
/var/tmp/www/httpd
/var/log/httpd
/www/bin/httpd
/usr/lib/httpd
/usr/src/apache_1.3.33/src/httpd
/usr/sbin/httpd
[root@1038 root]# cd /usr/sbin

The location of the httpd.conf file:

[root@1038 sbin]# find / -name httpd.conf
/etc/httpd/conf/httpd.conf
/www/conf/httpd.conf

And the control utility:

[root@1038 sbin]# find / -name apachectl
/www/bin/apachectl
/usr/src/apache_1.3.33/src/support/apachectl
/usr/sbin/apachectl
[root@1038 sbin]# /usr/sbin/apachectl start

This works.

Installing mod_dav_svn

According to the documentation in a book (http://svnbook.red-bean.com/en/1.1/svn-book.html#svn-ch-6-sect-4.1) about Subversion, you also need a special module, mod_dav_svn:

[root@1038 modules]# yum install mod_dav_svn
Gathering header information file(s) from server(s)
Server: Fedora Linux 2 - i386 - core
Server: Dag RPM Repository for Fedora Core
Server: Fedora Linux 2 - i386 - freshrpms
Server: Fedora Linux 2 - i386 - updates
Finding updated packages
Downloading needed headers
Resolving dependencies
Dependencies resolved
I will do the following:
[install: mod_dav_svn 1.2.1-0.1.1.fc2.rf.i386]
Is this ok [y/N]: y
Downloading Packages
Getting mod_dav_svn-1.2.1-0.1.1.fc2.rf.i386.rpm
mod_dav_svn-1.2.1-0.1.1.f 100% |=========================|  60 kB    00:00
Running test transaction:
Test transaction complete, Success!
mod_dav_svn 100 % done 1/1
Installed:  mod_dav_svn 1.2.1-0.1.1.fc2.rf.i386
Transaction(s) Complete

Neon

Apparently, you also need Neon, “An HTTP and WebDAV client library”, but this was already installed on my system:

[root@1038 modules]# yum info neon
Gathering header information file(s) from server(s)
Server: Fedora Linux 2 - i386 - core
Server: Dag RPM Repository for Fedora Core
Server: Fedora Linux 2 - i386 - freshrpms
Server: Fedora Linux 2 - i386 - updates
Finding updated packages
Downloading needed headers
Looking in Available Packages:
 
Looking in Installed Packages:
Name   : neon
Arch   : i386
Version: 0.24.7
Release: 2.1
Size   : 190.44 kB
Group  : Applications/Publishing
Repo   : Locally Installed
Summary: An HTTP and WebDAV client library
Description:
 neon is an HTTP and WebDAV client library, with a C interface;
providing a high-level interface to HTTP and WebDAV methods along
with a low-level interface for HTTP request handling.  neon
supports persistent connections, proxy servers, basic, digest and
Kerberos authentication, and has complete SSL support.

Configuring subversion

Configuring subversion starts with configuring Apache, through httpd.conf. In my case, I have also Apache 1.3 running. So I need my Apache 2 installation to listen only to port 8080:

Listen 8080

With regard to subversion, you need to make sure the necessary Apache modules are loaded:

LoadModule dav_module modules/mod_dav.so
LoadModule dav_svn_module modules/mod_dav_svn.so

Then, Apache must be told to delegate all svn related stuff to somewhere else:

<Location /svn>
  DAV svn
 
  # any "/svn/foo" URL will map to a repository /home/svn/foo
  SVNParentPath /home/svn
</Location>

This means that all repositories will end up under /home/svn. From the book:

This is a particularly convenient syntax in that, unlike the use of the SVNPath directive, you don't have to restart Apache in order to create and network new repositories.

Backups

I have created my SVN Parent Path under home, because all contents of home are automatically part of backup. My backup scripts simply compress and copy files to a tar archive. The problem is that such a backup system could lead to corrupted backups if you use it to backup databases.

So, to avoid any problems, I do not use the (non-default) BerkeleyDB database for my repositories, but instead FSFS:

'An FSFS repository stores a revision tree in a single file, and so all of a repository's revisions can be found in a single subdirectory full of numbered files. Transactions are created in separate subdirectories. When complete, a single transaction file is created and moved to the revisions directory, thus guaranteeing that commits are atomic. And because a revision file is permanent and unchanging, the repository also can be backed up while “hot” (..).'

One of the main advantages of FSFS (over BerkeleyDB) is that it is not very sensitive to interruptions (such as those caused by making backups).

Security

SVN is controlled by Apache. There is, as far as I can see, no need for any user but Apache to gain access to the repositories, so after creating a repository, I issue the command:

[root@1038 svn]# chown apache:apache -R repos

I have also secured the /home/svn directory, using standard Apache authentication tools:

[root@1038 svn]# htpasswd -cm /etc/svn-auth-file onno
New password:
Re-type new password:
Adding password for user onno
  • First time: use -c to create the file
  • Use -m to use MD5 encryption of the password, which is more secure

And in Apache's httpd.conf:

<Location /svn>
  DAV svn
 
  # any "/svn/foo" URL will map to a repository /home/svn/foo
  SVNParentPath /home/svn  
  AuthType Basic  
  ## AuthName is an arbitrary name that you give for the authentication domain
  AuthName "Subversion repository"  
  AuthUserFile /etc/svn-auth-file  
  ## tell Apache that all requests require an authenticated user
  Require valid-user
 
</Location>

Using subversion for a new project

Create a new repository:

[root@1038 sbin]# svnadmin create /home/svn/repos
[root@1038 sbin]# ls /home/svn/repos
conf  dav  db  format  hooks  locks  README.txt

This creates a directory structure containing a database for your repository. Do not forget to set permissions and ownership. Usually, apache is the user (if you're accessing svn through http), so you'd do something like:

[root@1038 svn]# chown -R apache:apache smallcount

To delete a repository, simply delete the parent directory and all its contents:

[root@1038 sbin]# rm -Rf /home/svn/repos

Warning: all contents will be gone forever!

About Subversion clients

You can use any client you like, but for Windows TortoiseSVN is an excellent choice. Use this tool to populate your repository by clicking “import” in the context menu (assuming of course that you already have a subversioned project). As you can see from this example, this particular client assumes that we are thinking from the perspective of the SVN server. After all, from my point of view, I am exporting data to the server.

Populating a repository: creating a new project

To actually fill your repository with data which should be under version control, use TortoiseSVN on your local machine. Rightclick the folder which is the data source. Then choose “import” (on the CLI, move into the folder which contains your data source, then type svn import . repository_url -m “Import” –username user). Now, following the example above, under repository fill in:

http://yourdomain.com:8080/svn/repos

Start your project with the following directory structure:

repos
repos/tags
repos/branches
repos/trunk

(Or use 'my_project' instead of repos, if that's an easier example).

Make these directories on your local system (in a temporary directory) and then “import” them to the “repos” repository. Using the command line:

$ svn import -m 'New import' /home/me/repos http://yourdomain.com:8080/svn/repos

(Do NOT forget to use svnadmin first, on the svn server, before attempting this!)

Your main development version will be in trunk. Create a “repos” directory on your local system, and do a “checkout”: rightclick on the local “repos” directory (still assuming you're using TortoiseSVN) and choose “checkout”. In the address bar, still following our example, fill in this:

http://yourdomain.com:8080/svn/repos/trunk

As you can see, we have descended into the repository's trunk directory, because locally we're working on the development version, which goes into “trunk”.

Importing an Existing Project into Repository

If you already have an existing application which you want to bring into subversion, use this:

svn import -m 'Importing actual application' /home/me/repos http://yourdomain.com:8080/svn/repos/trunk

Of course, this is exactly the same command, except that your now specifying the trunk directory, which is where you want your development version to be.

Getting data out of a repository: checkout

Extracting the data from a repository is called “checking out”. In TortoiseSVN, simply rightclick on the destination folder on your local machine. Then choose “SVN Checkout”, and under repository (still following the example) fill in:

http://yourdomain.com:8080/svn/repos

On the command line (e.g. on your Linux machine) you would use the svn client to do a checkout. Because you're not clicking on any subdirectory, you have to specify the destination:

svn checkout http://yourdomain.com:8080/svn/myweb/trunk/ myweb

(Or use

svn checkout http://yourdomain.com:8080/svn/myweb/trunk/ .

if you are already inside the myweb directory)

This command will pour the contents of trunk into the ”myweb” directory. Using Linux, do not forget to specify ownership and permissions afterwards.

Updating a working copy

Move into the top level directory which contains your working copy. The execute this command:

svn update

Always run svn update before you commit, to make sure your file is up to date. Otherwise, you risk overwriting changes that other people have done in the meantime.

Clarification: a working copy is on the “client side”. Here, you use svn as a client tool.

Having svn ignore configuration files and such

Some files must never be updated, for example those containing local configuration settings. Here is an example how you use svn to ignore these files:

svn propset svn:ignore database.yml config

This means: add the ignore property for the file “database.yml” to the directory “config”.

To check which files are ignored, issue this command:

svn status --no-ignore

Here is some example output:

[root@1038 woordenweb]# svn status --no-ignore
I      tmp/sessions/ruby_sess.2278e7e31b8ecb57
I      tmp/sessions/ruby_sess.1a93b2ffb6b792db
I      tmp/sessions/ruby_sess.00da84c455b6ee76
I      tmp/sessions/ruby_sess.8aef2d83126b8a7b
I      log/fastcgi.crash.log
I      log/development.log
I      config/database.yml
M      public/dispatch.cgi
M      public/dispatch.rb
M      public/dispatch.fcgi

And to ignore all files in a directory:

svn propset svn:ignore "*" public/

Or all files in subdirectories:

svn propset svn:ignore "*" tmp/sessions tmp/cache tmp/sockets

Or all *.log files in a directory called “log”:

svn propset svn:ignore "*.log" log/

Or all *.marks files in all directories (through the use of a wildcard and the -R recursive option):

svn propset svn:ignore -R "*.marks" */

See also How to Use Rails With Subversion.

Excluding multiple directories

To ignore multiple types of files and folders, you must have a newline delimited list of values for the svn propset command. Because this can not be done with the commandline (well, I do not know how to do it anyway), we create a text file with on each line the signature of a file and/or folder to ignore. For example, we have a textfile with following content:

obj
bin

We save this file in the target folder and name it “ignore.txt”, and then issue the command in the target folder

[TargetFolder]>svn propset svn:ignore -F ignore.txt .

Do not forget the final dot, it means that the target folder is the current folder. With the above command all folders with names obj and bin in the target folder will be ignored by Subversion.

Another example:

onno@onno-desktop:~/rails/games/public/images$ cat > temp.txt
thumbnails
picturesonno@onno-desktop:~/rails/games/public/images$ ls -lah
total 36K
drwxr-xr-x 7 onno onno 4.0K 2008-08-28 21:24 .
drwxr-xr-x 6 onno onno 4.0K 2008-08-26 16:49 ..
drwxr-xr-x 4 onno onno 4.0K 2008-08-27 21:39 dummies
drwxr-xr-x 2 onno onno 4.0K 2007-06-27 09:18 kropper
drwxr-xr-x 3 onno onno 4.0K 2008-08-25 22:31 pictures
-rw-r--r-- 1 onno onno 1.8K 2008-07-18 10:18 rails.png
drwxr-xr-x 6 onno onno 4.0K 2008-08-28 21:23 .svn
-rw-r--r-- 1 onno onno   19 2008-08-28 21:24 temp.txt
drwxr-xr-x 3 onno onno 4.0K 2008-08-26 14:43 thumbnails
onno@onno-desktop:~/rails/games/public/images$ svn propset svn:ignore -F temp.txt .
property 'svn:ignore' set on '.'
onno@onno-desktop:~/rails/games/public/images$ svn commit -m "Ignoring both dirs thumbnails and pictures in svn"
Sending        images

Committed revision 54.
onno@onno-desktop:~/rails/games/public/images$ svn status
?      kropper
?      temp.txt
onno@onno-desktop:~/rails/games/public/images$ 

Anonymous, Readonly Repository

If you publish your Rails plugins, you may want to restrict access to the svn repository: only you are allowed to make commits. Everybody else has just readonly (checkout) rights. Here's how to configure Apache:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file

  # For any operations other than these, require an authenticated user.
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
  </LimitExcept>
</Location>

This is the same procedure used for normal authentication. The only difference is that here, you're saying: “for any other operations than readonly, require an authenticated user”.


Personal Tools