Subversion for FreeBSD Ports

This page based on the Subversion Primer (Chapter 4) in the FreeBSD Committer's Guide and will be merged back once we successfully switched to SVN.

More information could found at http://svnbook.red-bean.com/en/1.7/index.html

In this guide the checkout directory refers to /usr/ports. This may not fit with your environment so please use an appropriate path instead of /usr/ports.

The first is to check out directly from the main repository:

% svn checkout svn+ssh://svn.freebsd.org/ports/head /usr/ports

If your FreeBSD login name is different from your login name on your local machine, you must either include it in the URL (for example svn+ssh://jarjar@svn.freebsd.org/ports/head), or add an entry to your ~/.ssh/config in the form:

Host svn.freebsd.org
        User jarjar

In svn+ssh://svn.freebsd.org/base, base refers to the source tree. Similarly, ports refers to the ports tree, and so on. These are separate repositories with their own change number sequences, access controls and commit mail.

For the ports repository, head refers to the current ports tree. For example, head/www/firefox is what would go into /usr/ports/www/firefox in the ports tree. Some other key locations are:

/tags/n which corresponds to a specific tag taken for a release for example. Naming convention for n is: RELENG_x_y_z for release branches, RELEASE_x_y_z for release tags and RELEASE_x_EOL for branch EOL tags. e.g:

/branches/ is used during the release process to merge security updates until the tree is tagged.

There will be no /projects and /user space in the official ports repository. Use one of the public available repositories like marcuscom (marcus@), redports (decke@) or chruetertee (beat@).

Daily Use

Help

SVN has built in help documentation. It can be accessed by typing the following command:

% svn help

Checkout

As seen earlier, to check out the FreeBSD ports head branch:

% svn checkout svn+ssh://svn.freebsd.org/ports/head /usr/ports

or

% svn co --depth=empty svn+ssh://svn.freebsd.org/ports /usr/ports
% cd /usr/ports
% svn up head

This way you can checkout svnadmin, a tag or branch later in the same working copy.

Anonymous Checkout

It is possible to anonymously check out the FreeBSD repository with Subversion. This will give access to a read-only tree that can be updated, but not committed to. To do this, use one of the following commands:

% svn co svn://svn.freebsd.org/ports/head /usr/ports
% svn co http://svn.freebsd.org/ports/head /usr/ports

Partial Checkout

It is possible to check out only part of the FreeBSD ports tree:

% svn checkout --depth empty svn+ssh://svn.freebsd.org/ports/head /usr/ports
% cd /usr/ports
% svn update --set-depth files
% svn update Mk
% svn update Templates
% svn update Tools
% svn update --set-depth files $category
% cd $category
% svn update $port

Updating the Tree

To update a working copy to either the latest revision, or a specific revision:

% svn update
% svn update -r12345

Status

To view the local changes that have been made to the working copy:

% svn status

CVS has no direct equivalent of this command. The nearest would be cvs up -N which shows local changes and files that are out-of-date. Doing this in SVN is possible too, however:

% svn status --show-updates

Editing and Committing

Like CVS but unlike Perforce, SVN does not need to be told in advance about file editing.

svn commit works like the equivalent CVS command. To commit all changes in the current directory and all subdirectories:

% svn commit

To commit all changes in, for example, Mk/ and www/firefox/ in a single operation:

% svn commit Mk/ www/firefox/ 

Always commit all corresponding changes in one commit as SVN will have a single revision number for the whole commit.

If you don't want to deal with Subversion properties (see below) use the psvn wrapper to commit:

% /usr/ports/Tools/scripts/psvn commit

Adding and Removing Files

Note: Before adding files, get a copy of auto-props.txt and add it to ~/.subversion/config according to the instructions in the file. If you added something before you've read this, you may use svn rm --keep-local for just added files, fix your config file and re-add them again. The initial config file is created when you first run a svn command, even something as simple as svn help.

As with CVS, files are added to a SVN repository with svn add. To add a file named foo, edit it, then:

% svn add foo

Files can be removed with svn remove:

% svn remove foo

Subversion does not require rming the file before svn rming it, and indeed complains if that happens.

It is possible to add directories with svn add:

% mkdir bar
% svn add bar

Although svn mkdir makes this easier by combining the creation of the directory and the adding of it:

% svn mkdir bar

In CVS, the directory is immediately created in the repository when you cvs add it; this is not the case in Subversion. Furthermore, unlike CVS, Subversion allows directories to be removed using svn rm, however there is no svn rmdir:

% svn rm bar

Subversion config

Make sure you have a ~/.subversion/config. Simply using it (svn --help) will cause a default one to be created if you don't already have one.

Make the following changes to ~/.subversion/config:

Set:

enable-auto-props = yes

Append/edit auto-props section (if you already use the auto-props for FreeBSD src the entry for Makefile is already done):

[auto-props]
bsd.*.mk     = svn:eol-style=native; svn:keywords=FreeBSD=%H; svn:mime-type=text/plain
distinfo*    = svn:eol-style=native; fbsd:nokeywords=yes; svn:mime-type=text/plain
extrapatch-* = svn:eol-style=native; fbsd:nokeywords=yes; svn:mime-type=text/plain
extra-patch-*= svn:eol-style=native; fbsd:nokeywords=yes; svn:mime-type=text/plain
patch-*      = svn:eol-style=native; fbsd:nokeywords=yes; svn:mime-type=text/plain
pkg-*        = svn:eol-style=native; fbsd:nokeywords=yes; svn:mime-type=text/plain
Makefile*    = svn:eol-style=native; svn:keywords=FreeBSD=%H; svn:mime-type=text/plain

To ignore certain files you can add something like this in the miscellany section:

[miscellany]
global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
 *.bak *.orig *.rej *~ #*# .#* .*.swp .DS_Store

Properties

To list the current properties use svn proplist:

% svn proplist Makefile
Eigenschaften zu »Makefile«:
  svn:mime-type
  svn:keywords
  svn:eol-style

Properties can be showed, deleted and added with svn propget, svn propdel or svn propset:

% svn propdel svn:mime-type Makefile
property 'svn:mime-type' deleted from 'Makefile'.
% svn proplist Makefile
Properties on 'Makefile':
  svn:keywords
  svn:eol-style
% svn propset svn:mime-type text/plain Makefile
property 'svn:mime-type' set on 'Makefile'
% svn proplist Makefile
Properties on 'Makefile':
  svn:mime-type
  svn:keywords
  svn:eol-style
% svn propget svn:mime-type Makefile
text/plain

Use those properties:

The Tools/scripts/psvn wrapper makes sure all properties are set the right way.

Copying and Moving Files

The following (obviously) creates a copy of foo.c, named bar.c:

% svn copy foo.c bar.c

To move and rename a file:

% svn move foo.c bar.c

The above command is the exact equivalent of:

% svn copy foo.c bar.c
% svn remove foo.c

Neither of these operations have equivalents in CVS.

Readding a removed port

If readding a port that has been in the tree in the past, do not simply use svn add-- this will obscure the history of the port's previous existence.

Instead you should copy the last living revision of the port (use http://people.freebsd.org/~crees/removed_ports/index.xml):

% svn co svn+ssh://svn.freebsd.org/ports/head/category
% svn cp 'svn+ssh://svn.freebsd.org/ports/head/category/port@{YYYY-MM-DD}' port

Make your changes and ensure that you add or remove any appropriate files. Some revived files may have the cvs2svn:cvs-rev property set which is blocked by a pre-commit hook so you need to remove the cvs2svn:cvs-rev property first:

% svn propdel -R cvs2svn:cvs-rev port

Don't forget to add the revived port to the category Makefile and remove the appropriate entry from the MOVED file. Then commit the changes in a single commit.

Repo-Copy

/!\ First make sure that you were using an up to date portstree and the target directory does not exist.

Copy or move the ports directory by using svn cp or svn mv. Edit the files in the new directory (like adding LATEST_LINK for example) and then commit all changes together (like the removed port, the newly added, the change to category/Makefile and the MOVED entry). There is no longer a need to do a force commit after a repocopy.

Example (Repo-copy www/firefox to www/firefox-esr):

% cd /usr/ports && svn up
% ls www/firefox-esr
ls: www/firefox-esr: No such file or directory
% svn cp www/firefox www/firefox-esr
A         www/firefox-esr
% vi www/Makefile www/firefox-esr/Makefile
% svn ci www/Makefile www/firefox-esr

Log and Annotate

svn log will show all the revisions that affect a directory and files within that directory in reverse chronological order, if run on a directory. This contrasts with cvs log in that CVS shows the complete log for each file in the directory, including duplicate entries for revisions that affect multiple files.

svn annotate, or equally svn praise or svn blame, is equivalent to cvs annotate in everything but output format.

Diffs

svn diff displays changes to the working copy of the repository. Diffs generated by SVN are unified by default, unlike CVS, and include new files by default in the diff output.

As with CVS, svn diff can show the changes between two revisions of the same file:

% svn diff -r179453:179454 UPDATING

It can also show all changes for a specific changeset. The following will show what changes were made to the current directory and all subdirectories in changeset 179454:

% svn diff -c179454 .

Reverting

Local changes (including additions and deletions) can be reverted using svn revert. Unlike cvs up -C, it does not update out-of-date files--it just replaces them with pristine copies of the original version.

Conflicts

If a svn update resulted in a merge conflict, Subversion will remember which files have conflicts and refuse to commit any changes to those files until explicitly told that the conflicts have been resolved. The simple, not yet deprecated procedure is the following:

% svn resolved foo

However, the preferred procedure is:

% svn resolve --accept=working foo

The two examples are equivalent. Possible values for --accept are:

working: use the version in your working directory (which one presumes has been edited to resolve the conflicts).

base: use a pristine copy of the version you had before svn update, discarding your own changes, the conflicting changes, and possibly other intervening changes as well.

mine-full: use what you had before svn update, including your own changes, but discarding the conflicting changes, and possibly other intervening changes as well.

theirs-full: use the version that was retrieved when you did svn update, discarding your own changes.

Reverting a Commit

Reverting a commit to a previous version is fairly easy:

% svn merge -r179454:179453 UPDATING
% svn commit

Change number syntax, with negative meaning a reverse change, can also be used:

% svn merge -c -179454 UPDATING
% svn commit

This can also be done directly in the repository:

% svn merge -r179454:179453 svn+ssh://svn.freebsd.org/ports/head/UPDATING

Reverting the deletion of a file is slightly different. Copying the version of the file that predates the deletion is required. For example, to restore a file that was deleted in revision N, restore version N-1:

% svn copy svn+ssh://svn.freebsd.org/ports/head/UPDATING@179454
% svn commit

or, equally:

% svn copy svn+ssh://svn.freebsd.org/ports/head/UPDATING@179454 svn+ssh://svn.freebsd.org/ports/head/UPDATING

Do not simply recreate the file manually and svn add it--this will cause history to be lost.

Fixing Mistakes

While we can do surgery in an emergency, do not plan on having mistakes fixed behind the scenes. Plan on mistakes remaining in the logs forever. Be sure to check the output of svn status and svn diff before committing.

Mistakes will happen, but, unlike with CVS, they can generally be fixed without disruption.

Take a case of adding a file in the wrong location. The right thing to do is to svn move the file to the correct location and commit. This causes just a couple of lines of metadata in the repository journal, and the logs are all linked up correctly.

The wrong thing to do is to delete the file and then svn add an independent copy in the correct location: the history of the file is lost.

Forced Commit

Forced commits aren't available with SVN. Nevertheless there is a workaround how to do forced commits with SVN. For example if you like to do a forced commit on a Makefile create a copy of this file first:

% cp Makefile Makefile.orig

Then do a whitespace change or something similar in the file:

% vi Makefile

Now do a svn commit, enter your commit message but don't close the editor:

% svn ci Makefile

Open a second terminal and overwrite the modified file with the original one:

% mv Makefile.orig Makefile

Now close the first editor and finish the commit.

Some Tips

Lock the ports tree

Only portmgr should lock the portstree. To lock the ports tree uncommend the ^head/ portmgr line in ports/svnadmin/conf/approvers. To unlock the tree comment out the same line. Don't use svn lock to lock down the tree. This is prohibited by a hook.

Create a tag

Only create a tag directly from head if there is no need to merge changes (like the CVS tag slips)!

Commit directly in the repository with the revision number you like to tag (stick to the tag naming convention). You don't need to lock the tree for this:

( <!> Just specify the revision you like to tag and make sure you have a trailing slash between head and @! )

% svn copy svn+ssh://svn.FreeBSD.org/ports/head/@295095 svn+ssh://svn.FreeBSD.org/ports/tags/RELEASE_x_y_z

Create release branch and release tagging

Release branching and tagging is done by portmgr. Create a release branch once the ports tree is ready for an upcoming release:

( <!> Specify the revision you like to branch/tag and make sure you have a trailing slash between head and @! )

% svn copy svn+ssh://svn.FreeBSD.org/ports/head/@297395 svn+ssh://svn.FreeBSD.org/ports/branches/RELENG_8_4_1

Now security and build/run fixes can be merged to the release branch (see the "Merging" section)

Once the release is done create a tag out of the release branch:

% svn copy svn+ssh://svn.FreeBSD.org/ports/branches/RELENG_8_4_1 svn+ssh://svn.FreeBSD.org/ports/tags/RELEASE_8_4_1

Merging

Use a full checkout of the release branch (no partial checkouts), do the merge in the top directory (equivalent of /usr/ports) and don't edit mergeinfo manually.

If a commit needs merging to the release branch (like security updates, build fixes) switch to the release branch (make sure your working copy has no uncommitted changes):

% cd /usr/ports
% svn switch svn+ssh://svn.FreeBSD.org/ports/branches/RELENG_8_4_1

You can check which commits are available for merging:

% svn mergeinfo ^/head --show-revs eligible
r297398

Merge the commit to the release branch now (In this example r297398 is merged to RELENG_8_4_1. Use MFH r297398: at the beginning of the commit message ). The mergeinfo is recorded in the top directory of a release branch only (Make sure you do the merge in the top directory!):

% cd /usr/ports
% svn merge -c 297398 ^/head
% svn ci

Fixing mergeinfo

If a mergeinfo wasn't recorded properly (for example if the merge wasn't done using "svn merge") it can be added afterwards.

Go to the top directory of the release checkout:

% cd /usr/ports

Now record the missing mergeinfo:

% svn merge --record-only -c 307190 ^/head

The merged revisions should be visible now (Please double check if they are there):

% svn mergeinfo ^/head
% svn propget svn:mergeinfo .

If you see your merged revisions in both outputs commit it (Commit message could be something like "Fix mergeinfo")

% svn ci

Enable feature freeze

To enable a feature freeze change the FEATURE_FREEZE variable in svn.FreeBSD.org/ports/svnadmin/conf/featurefreeze.conf to 1.

PortsSubversionPrimer (last edited 2012-11-13 16:44:36 by BeatGaetzi)