Team Development Using Subversion

An Oracle JDeveloper How To Document
Written by Susan Duncan, Oracle Corporation
May, 2008

Overview


Developing applications as a team introduces several considerations, including source and version control for files, and utilizing shared resources. This paper details the use of integrated source control via Subversion in JDeveloper 11g, and how your team can package and share resources. It looks at best practices in team development, and shows how to coordinate check-in procedures and shared files across a development team to leverage these features for maximum productivity

Even the smallest of development teams needs to maintain control of its source code. There are many ways to do this: using a manual system, a database, a commercially available or in-house Source Code Management (SCM) system. But whatever system you use, your system should fulfill certain basic functions:

  • Integrate code changes
  • Maintain code history
  • Revert to previous code version
  • Compare versions and changes
  • Merge changes

Supported Version Control Systems

JDeveloper supports a number of version control systems. This means you can use JDeveloper as a client to connect to the specific version control repository and manipulate your versioned code from within JDeveloper. This gives developers greater productivity, as they don’t have to open another tool or move outside of their IDE to carry out SCM operations.

In JDeveloper 11g Subversion (SVN) support directly into the IDE. JDeveloper 11g focuses source control support on SVN whilst continuing to enhance support for other systems, whether these are integrated into JDeveloper (CVS) or downloadable extensions for products such as Clear Case, Perforce etc.

Why Subversion?

SVN is a powerful, open-source version control system that has fast become the de-facto standard for application version control. It was developed to update and overcome some of the limitations of Concurrent Versions System (CVS), an older open-source system that was widely used before SVN was introduced and continues to be used. There is a free online book that details not only how SVN works but also details its improvements over CVS. It can be used to check and understand concepts and ensure that JDeveloper's SVN support is the best it can be.

One of the major changes between CVS and SVN is SVN’s usage of atomic commits. When you commit files either all or none of the committed changes are done. If there is a problem – perhaps a network failure or conflicts between some of the changed code – then you do not finish up with some of your files committed whilst others are rejected. This was a drawback with CVS and is particularly important when you are doing application development that has dependencies between files – such as a Java file and a corresponding XML metadata file.

Another advantage of SVN is its support for full versioning of both files and folders. A full history of them is held on the SVN server and this allows you to add, delete, copy and rename then. CVS only versions files and so there is not full ‘true’ version history as the folders containing these files are not versioned. In addition, replacing a file with a new one (completely unrelated) with the same name does not give a clean history to that new file, the old history from the redundant file is maintained. In addition, if a file is renamed in CVS then the version history of those files is lost, while Subversion copies the history to the new element.

Where more than one developer is going to be working on the same file, there has to be a system to prevent one developer's work unintentionally overwriting another's. The preferred solution to this with Subversion is the copy-modify-merge model. In this model, each developer working on a file takes a copy of it (called a Working Copy), modifies it, then merges it with the latest code in the repository. This may sound as if it would cause frequent conflicts in the content, but in practice this is rarely the case. When conflicts do occur, there is an efficient process for resolving them (see Resolving Code Conflicts).

As with any development project, time spent setting up your application and the SVN repository structure for that application is time well spent.

Versioning Level

A question that comes up regularly in development teams is ‘At what level should I version my code?’ The answer should always be - at the top level. If you are using JDeveloper this means at the Application Level. We adopt this internally for JDeveloper’s development and it's a top tip for best practice.

Initial Import

If you are working on a new development you may have to carry out the initial import of your application to the SVN repository. It makes sense to set up the repository at an early stage of development. Perhaps once the initial application has been created. The recommended standard repository layout is to provide an <Application Name> directory containing three folders: Trunk, Branches, Tags (see Branching).

Using JDeveloper you can use the Subversion Navigator to add a new folder to your repository ready to house your new application directly (see Figure 1) or you can create the folder whilst using JDeveloper’s Import Wizard

Figure 1. JDeveloper's Subversion Repository Navigator

The wizard takes you through the steps to import your initial application into the repository. The easiest way to open the wizard is to select Versioning -> Import Files from the application dropdown menu. Now you can create the folder to hold your source code, comment your import, exclude any files or file patterns from the initial import (see Excluding Files) and automatically checkout the code to your local working copy ready for your development work.

Working Copy

Using JDeveloper’s integrated SVN Navigator and the Application Navigator it is easy to make best use of the SVN concept Working Copy. To work on an application, you check it out from the SVN repository. This copy becomes your working copy. In addition to the standard Commit and Update SVN commands, JDeveloper provides additional commands Commit Working Copy and Update Working Copy. These commands ensure that all the files in your working copy are committed/updated whatever node in the Application Navigator you invoked the command from. Figure 2 shows the Commit Working Copy menu being invoked from the home.jspx node in the Application Navigator.

Figure 2. Invoking Commit Working Copy From File Menu

What if my development team uses tools other than JDeveloper?

If you are using JDeveloper, you should version at the application level. This assures that your .jws and .jpr configuration files are included in source control, especially important if you have dependencies between projects. But what if not all your development team are using JDeveloper? Perhaps you have some team members that use Eclipse or another IDE as well as JDeveloper? In this case it is important to ensure that you still place both your 'jws' and your 'jpr' files under source control.

Why? Because JDeveloper looks at the state of those files to decide what versioning menu options it makes available. For instance, say the projects making up an application had been imported into SVN but the root level application folder and the .jws file had not been included. Perusing the versioned files through the SVN Navigator repository you would notice that there was no .jws in the application root folder. To overcome this you would create a new empty application and check out the JDeveloper projects into the new application. But if you tried to access the Versioning menu at the application level you would see 'Import Files' as the .jws file is not part of the versioned code and so JDeveloper offers you the option to place the application under version control. If this happens the workaround is to go back to the repository and add the .jws file to version control. This brings the IDE back into kilter and allows you to continue using JDeveloper's integrated SVN support.

What if my development process mandates commits at the project level?

The above scenario, checking out projects into an empty application, does offer you an alternative to versioning at the application level (best practice). By checking out individual projects you are creating a working copy of each project as you check it out. This means that you can commit and update each project as a separate entity. But beware, if you do this then be sure that you understand any dependencies between the projects and are careful to update all the projects if others are also working on them.

A better alternative is to stick to the best practice of versioning at the application level and use JDeveloper’s Pending Changes windows to commit by project. Pending Changes consists of three tabs and provides information on individual applications and projects open in your IDE that are under version control.

  • The Outgoing Changes tab shows you all files that have been modified and need committing to the repository. Figure 3 shows 1 file with an outgoing changes in the StoreFrontUI project.
  • The Candidates tab shows you all files that you have added to the working copy of your versioned applications and projects (open in the IDE) since you last committed. These files need to be added to your SVN repository using the Add command – either from the Add button in the Candidates tab or through the Application Navigator (see Excluding Files)
  • The Incoming Changes tab shows you all the files that have been committed to the SVN repository by another member of the development team since you last committed your changes. (See Comparing Versions)

Figure 3. Outgoing Changes Tab

Whether you use Update or manually review and add the changes might depend on how many and how complex the changes are and also how easy the source is to read. Another very important consideration is the nature of the change. In many cases, especially if you are using a framework such as Oracle’s Application Development Framework (ADF) making the changes manually could be error prone as there are dependencies between Java and their corresponding XML metadata files. For this reason best practice is to use the Update Working Copy mechanism to update code from the repository, whether you have used the History to manually browse changes in advance or not. It may be that you use the History tab to find changes and the author (see Figure 6) and then communicate with the author if you are unsure of how the changes affect your code.

What if I need to check out other code not in my application?

This might occur if your development organization has read only ‘standard’ code or libraries. Rather than each developer checking this code out and adding it to their application by hand you can use the svn:externals property. The advantage of this is that once you set the property on your application everyone who checks out the application from SVN will also get exactly the same set of code and associated libraries. SVN Properties are exhaustively covered in the SVN book detailed earlier.

Figure 4. Add Subversion Property

Figure 4 shows the JDeveloper Add Subversion Property dialog. In this example the Value String field show a value pair entered to indicate that the library adflibStoreFrontService.jar is to be checked out of its URL in an SVN repository to a folder called external_jars. The folder external_jars will be created in the Resource File indicated in the dialog. Invoking the dialog on this node in the Application Navigator set this. The correct URL of the library was obtained by using the Copy URL command on the node in the Subversion Navigator (see Figure 1)

Excluding Files From Version Control

There are a couple of ways that files are excluded from the SVN Repository. You can exclude files during the Initial Import, ensure that certain file name patterns are never added to the repository or mark specific files for exclusion. JDeveloper recognizes certain file types as not needing to be controlled and additionally provides functionality for you to use the Subversion property svn:ignore.

During Initial Import

As mentioned earlier, it is possible to add filters to exclude files during the Initial Import of an application. JDeveloper uses its Global Ignore List to provide a pre-populated list of those files/file types that you are not likely to need versioned such as compiled Java classes (.class files). The Global Ignore List is displayed in Step 5 of the Import wizard. You can add filters to this list. Note that these additional filters are not added to the Global Ignore List and are not persisted between JDeveloper sessions. If you find that there is a specific pattern that your development teams always filter then you should document this in your development process to ensure that any team member doing an Initial Import of code is aware of it.

Imagine this scenario: You create a new application containing some compiled Java classes. The Global Ignore List is also used by the Application Navigator to restrict the items displayed there. Hence, you do not see the compiled classes in the navigator but, of course, they exist on the file system. Now you Import the application into SVN and the same filter is applied to the application. As part of the Import wizard you choose to Check Out the imported application. Your original application location on the file system will by default now contain your checked out application – minus the excluded files. In fact, your original files have been backed up by JDeveloper so it is possible to return to them. But from this point on you should be working with your repository to maintain all your required code.

Excluding New Files

During application development it might be that you want to exclude certain file types or specific files from the repository, whilst still maintaining them in your working copy. To do this you use the svn:ignore property. There are a number of places to do this:

  • Candidates Tab, Pending Changes
    • This tab shows all the files you have not yet added to version control. You can select files here and use the context menu to invoke the Add Subversion Property dialog. The dialog is populated with the files you selected and you can either choose to specifically ignore those files or edit the dialog to add file patterns.
  • Specific file in the Application Navigator
    • By invoking the Add Subversion Property dialog on a specific file you can again add that file or a file pattern to svn:ignore.
  • Folder node in the Application Navigator
    • svn:ignore is always set on a folder so you can invoke the Add Subversion Propery dialog from any folder node. Even when following the first two options above, the property is set on the owning folder of the selected files

Resolving Code Conflicts

As with all development projects there are a multitude of ways to resolve code conflicts between developers. No tool can ensure that conflicts do not occur. Much of the onus is on the development process of the team, its methodology and, as always, communication between developers. Some development teams where the developer who has the ‘lock’ on the main code wears a ‘lock’ hat to indicate to other team members that no one else should be updating the code (doesn’t work so well with distributed teams).

JDeveloper's development process and methodology minimize conflicts but developers talk regularly to each other and are pro-active in informing and discussing potential conflicts that might arise through new feature development, refactoring or any of the other myriad of reasons that conflicts arise. Having said that, SVN and JDeveloper can help to minimize these and provide strategies and answers to resolve them

Comparing Versions

In a team environment it is inevitable that other developers change code. These changes may or may not conflict with code that you are changing. Using the Pending Changes Incoming tab it is possible to see what files have being changed by other developers. Figure 5 shows that another team member has checked in a new version of home.jspx. At this stage it is not known if the changes are likely to conflict with your working copy changes.

Figure 5. Incoming Changes Tab

Using this example, having seen that there are Incoming changes you have a number of options:

  • Update your code to this new version using Update or Update Working Copy.
    • If you choose this option then, provided there are no conflicts, your code will be updated with these new changes. If you are not working on home.jspx then this is the obvious course of action.
  • Manually check the differences between your working copy and the newer version.
    • To do this you can look at the History tab of your working copy of home.jspx. Figure 6 shows that there are 2 differences. In the right hand pane is your editable working copy. Choose any of the listed versions to display in the left hand pane. In this example the latest repository version – 2766 has been chosen. You can move the changes into your editable working copy from this History tab.
  • Select the Version History menu item from the Versioning menu to review the different versions of the file.
    • This is very similar to the History tab of the file, but Version History is a read-only compare tool. Having perused the differences, use Update Working Copy to get the changes into your working copy. This can be used to compare the history of files that do not have editors – such as the .jpr project and .jws application files.

Figure 6. History tab of a file

Whether you use Update or manually review and add the changes might depend on how many and how complex the changes are and also how easy the source is to read. Another very important consideration is the nature of the change. In many cases, especially if you are using a framework such as Oracle’s Application Development Framework (ADF) making the changes manually could be error prone as there are dependencies between Java and their corresponding XML metadata files. For this reason best practice is to use the Update Working Copy mechanism to update code from the repository, whether you have used the History to manually browse changes in advance or not. It may be that you use the History tab to find changes and the author (see Figure 6) and then communicate with the author if you are unsure of how the changes affect your code.

An Alternative Approach

An alternative to the above is the lock-modify-unlock model, in which a developer locks a file, modifies it, adds it back to the repository, then unlocks it. Locking a file does not prevent another developer from working on a copy of the file: it prevents the content of that copy from being returned to the repository until the lock is removed. So there is still potential for wasted effort in this model.

The lock-modify-unlock model is more suitable for working with files in binary formats, where it is impossible to merge conflicting changes. It ensures that developers take it in strict turns to submit changes to a file. JDeveloper provides support for the Subversion property svn:needs-lock on a file and for retaining locks on files that you are about to commit.

Resolving Conflicts

Conflicts arise when two files have identical entries containing different content. JDeveloper’s Compare and Merge tools are XML aware. This means that XML elements, properties and white spaces are read and analysed when working to resolve avoid conflicts. If the file is not XML then if two files have identical line positions containing different content a conflict will arise. Through its merge algorithms JDeveloper is able to resolve very many potential conflicts but it is up to you to make final decisions on those conflicts that are not resolveable.

If you don’t use the Compare tools prior to doing a Commit of your working copy to the repository then you will probably learn that one or more of your files has conflicts at Commit. In this case, the commit is prohibited and, after you have Updated your working copy or specific files from the repository, any files in conflict will have duplicate copies in the application navigator (the files represent the different versions of the file that are in conflict. Select the Versioning -> Resolve Conflicts menu from the file version with an exclamation point icon next to it to open the Resolve Conflicts window. Figure 7 shows an example of this window. It consists of three panes: The left pane shows your version of the file, the right shows the repository version and the central pane shows the resulting file once you resolve the conflicts. In this example the right and left panes have been extended to show the content of those panes. Notice that the file is an XML file and JDeveloper has recognized that the same element, available in both versions of the file, contains differences. In this case, the label property differs.

Figure 7. Merge Conflict Resolution Editor

Once you have resolved all the conflicts shown in the file you can save and merge the resulting file using the Save and Merge button at the top of the window. Now you are ready to Commit your updated and conflict free files to the repository.

Branching

Branching is something that is seen as a requirement of a source control system although in practice it might not be used as extensively as you might think. The mechanics of branching are ‘simple’ but it is only as good as the process that the development team uses. It requires developers to be vigilant in what code they checkout, update and merge and to where. In Subversion a branch is a copy of code that is maintained and versioned independently but always remembers its ancestry. Your development process should document exactly when and how branches are to be used. It may be that a team is asked to work on a prototype that is not wanted on the main development arm. Branching allows them to maintain this development independently whilst also being able to update their code from the main development as required and, at the end of the prototype, either discard their code or merge it back into that main code line.

It may be that your branch code is a short term or a long-term project. Whatever the case, it is always advisable to Update from the main development line often if at some stage your branch will be merged back into the main code line.

The steps to set up and work on a branch are simple but care should be taken to understand what is happening at each stage of the process. JDeveloper provides functionality to help with the mechanics of following Subversion’s branching solution:

Create A Branch

In Subversion a branch is a copy of a code revision or a working copy taken at a point in time. Figure 8 shows the Branch/Tag dialog. You can create a branch from a Working Copy or from any revision in the repository. In this example the HEAD Revision in the repository is to be used as a branch. The working copy will be copied to the repository location specified in the To URL . All the history of the working copy (held in the From location in the repository) will remain associated with the new branch.

Figure 8. Branch/Tag dialog

As well as the capability to Branch, Subversion also provides a Tag option. These two options are identical. A Tag is generally used to give a snapshot of code at a given point in time whilst a branch is used for independent development. To preserve the difference between a Tag and a Branch the Subversion repository administrator tends to severely restrict the access and editing capabilities given to any Tag directory

Working With A Branch

Once you have created a branch, whether from a working copy or from a specific revision, you are likely to want to work on your branch code and continue to make changes and commit that code. To ensure that your working copy commits to the correct place in the repository you use the Switch command. Figure 9 diagrams the steps of creating and working with a branch from your working copy:

  1. Check out, edit and commit your working copy code to the main development line
  2. Create myBranch from your working copy and switch to myBranch (this ensures any subsequent commit/updates to your working copy are done with the myBranch code line)
  3. Edit and commit your working copy that is now switched to commit to myBranch
  4. As dictated by your development process, update your branch code from the main code and resolve any conflicts
  5. Merge your working copy with all the changes from myBranch. Then your working copy will reflect the merging of the latest repository code and the myBranch code. Do this by specifying the first and last revisions from myBranch that you want to merge.
  6. Resolve any irresolvable conflicts between main and myBranch in the normal way
  7. At this point your working copy reflects the latest merged code from the repository and your branch. The next step depends on where you are in your development. You might:
    a. Switch your branch back to myBranch and continue working and committing changes to myBranch
    b. Commit your working copy back into the main code so that all developers now have the latest myBranch and main code available to them
    c. Discard myBranch as it was a prototype and has served its purpose and is never going to be added to the main development line

Figure 9. Typical branching scenario

Summary

JDeveloper and Subversion provide developers with an integrated team development environment. This paper has demonstrated the client side integration and best practices for using Subversion with JDeveloper. It is outside the scope of this paper to discuss the administration of the SVN Repository and such issues as connection protocols.

 
false ,,,,,,,,,,,,,,,