Using REAL Studio with a Version Control System

Every once in a while, people seem to be seeking other's experience on using version control with REAL Studio. Probably for good reason, too. There are many ways it can be done, and the experimentation required to find the best approach requires a ton of time.

This article describes how to track a REALbasic project that contains a shared library with a version control system. It assumes you already know how to use REAL Studio, and that you have used version control at least a couple times.

So here it goes. I won't go as far as to say that my idea is the best, but it works nicely in my case.

Environment

The experimentation that generated the results here was done with REAL Studio 2010r1, and Git 1.7.

At of the time that this article was last updated, I am using REAL Studio 2010r2 and Git 1.7.1, and it seems as if this article still applies.

Prerequisite Knowledge

Since this page is public, I'll try to accommodate people who don't know how to use a version control program. I'll assume you know something about REAL Studio, though.

There are two basic models of version control (VC) systems these days: centralized, and distributed. Centralized systems require a central repository, and developers checkout the latest version directly from that server. Distributed systems do not require a central server although there is no reason why you can't have one. Every developer has his or her own clone of the whole repository, and when someone commits new data, then everyone else pulls from that person, either directly or transitively. There are many implications and arguments regarding these two models, but that is for a different discussion. All you need to know for this article is that the VC system used is Git, which follows the distributed model.

The proper way to use a distributed version control system is to break each logical package into its own repository. So for me, my common code is in a single repository, and each one of my main projects has its own repository.

Next comes the hierarchal structure.

Almost every VC system has the ability to fetch another repository and use it for a particular folder. In Subversion, they're called svn:externals. In Git, they're called submodules. This functionality allows for common code to be tracked independently of the main project. After thinking about it for a while, it makes sense, but at first glance it might not be so obviously a good idea. All I do is tell the main project repository to add my library as a submodule.

Experimentation Results

Next comes the hard part - tracking data. REAL Studio can save as three different project formats - rbp, xml, and rbvcp.

Tracking any data in a versioning repository can be tricky. In VC systems designed for programming, data is usually stored on a per-line basis. So, if you change a single character, or even leading whitespace of a line, then the whole line is considered replaced with the new version. Because of this, you want to save your data in a format that is both reproducible and is based on plain text. I mean, there is nothing wrong with tracking binary files, but you're not going to be able to diff or merge them if two people modify the latest version independently.

So that rules out the rbp format.

Another general idea in tracking code in a repository is that you only track the project. In other words, the absolute minimum it would take for someone else to pick up where you left off. Granted, that's almost everything, but there are a few things that REAL Studio likes to store (for your convenience) that are not actually part of the programming or building process, such as the location and size of the main window, and the history of what code you have viewed. If this information is committed to the repository, then you will end up with what is best described as "noise" - When you diff what your coworker did, you will have to filter out the data that's not actually part of the code. Over time, this gets time consuming and annoying.

So that rules out the xml format.

At this point, I crossed my fingers and hoped that there would be no problems with the rbvcp format.

REAL Studio's Version Control Project format (rbvcp) is designed to play nicely with VC systems. All non-critical project information (window location, view history, etc) is located in a hidden file that you can tell your VC system to ignore. All logical units of information in the project (a window, a class, a menubar, etc) are stored in separate independent files. This may seem complicated, but it becomes very useful when you want to know what you broke a week ago in Window1. You can diff the entire file for Window1, and since that file is only Window1, you won't have the noise of that massive new framework you just finished yesterday obscuring the small changes you are looking for.

Anyways, a few weeks of testing later, I only found one bug (Case #12367), which Feedback says has been fixed for the next version of REAL Studio. The bug itself wasn't that critical to begin with, and since it's been fixed, I'd say it's not much to sweat over anymore.

So basically, the rbvcp format is really the only format that works with a VC system. Now for figuring out how to set up that library.

When implementing a shared library, it is logical to use external items in REAL Studio. That way, you can update the external item using the VC system, and your project will magically get the changes. When making an external item in REAL Studio, you have two choices for the file format: Object (similar to rbp), and xml. Both of those formats were excluded earlier.

Even if you're willing to add some superfluous data with the xml format (which I was at first), it has another interesting problem. Every once in a while, blocks of attributes written in the xml will get written in a reversed order. As far as any VC system is concerned, that's a whole bunch of lines changed for no good reason. Unnecessary noise.

Ouch.

Okay, well it's time to think outside of the box. My next idea came from observing the folder structure created by the rbvcp format. It was vaguely familiar... It turns out that REAL Studio mirrors the folder structure in the Project Editor to the file system. So, if you have a class called Foo in a folder called Bar in the Project Editor, then the class will be saved in its own file, called Foo.rbbas, inside a folder called Bar, which is next to the main project file. This, combined with the fact that submodules are located in subfolders, provides a potential solution.

So, I ditched using actual external project items. Let's see how this goes.

I started with creating the shared library. I created a new GUI project, and saved it in a new repository using the rbvcp format. Then, I went to my parent project (which is already in rbvcp), and added the library as a submodule. So at this point, the repositories are set up, but the parent project does not know about the classes in the library yet.

Then came a surprisingly easy part. To add, or link up, the library with the main project, I simply dragged the folder that contained the shared library from Finder into the Project Editor of the main project. This causes REAL Studio to scan the folder I dragged in, creating folders in the Project Editor to match, and adding the classes, windows, and modules and other files that happened to be in that folder. Since the main project is in the rbvcp format, the library classes are saved in corresponding files in the filesystem... which are the same files I just dragged in! So, linking up the library automatically creates items that behave as external, and if I modify something in the library from the main project, the changes are saved go to where the library is saved, inside the library's repository.

The only little detail is that dragging a folder into the Project Editor adds all the files in the folder to the Project Editor, so I had to go into the folder it created in the Project Editor, and delete the library's App class, project file, menu bar, etc. But deleting is a heck of a lot easier than adding. I think it took like four mouse clicks total.

Pitfalls

There is a very important shortcoming I found which does not affect me that much, but from what I hear from other users, will drive some other people insane. If two REAL Studio projects share classes using actual external objects, and they are open at the same time, then REAL Studio will automatically update the other project when one of them changes something that is shared between the two. This appears to only work for external items. The rbvcp format saves things in independent folders, but they're not technically external. If two rbvcp projects overlap, and they're open simultaneously, REAL Studio will NOT update the other project when you modify some code that is shared between the two.

There was also a rather interesting behavior when I started playing with the rbvcp format. It seemed as if REAL Studio saved the files of the rbvcp format using Mac Classic line endings rather than Unix line endings. Since Git only accepts Unix line endings, this caused a problem. It treated entire files as a single line. Luckily, REAL Studio seemed to be rather intelligent; it would save files with the same line endings as they were read with. So, I just quit REAL Studio, used a plain text editor to change all the files to Unix line endings, fired up REAL Studio again, and everything worked after that.

Occasionally, a brand new file might be saved with Mac Classic line endings. The example (below) describes how to detect when this happens. Basically, you just watch for Git thinking that massive hunks of a file are on one line. Do a diff before you commit - it is usually obvious. git diff --cached tells what data is staged. git diff tells changes that are not committed nor staged.

Results

I use an approach that might or might not be the intended method of implementing a project with a library in REAL Studio, as suggested by REAL Studio not handling updating shared code in two projects open simultaneously (see pitfalls above). But if you don't open your main project and your library at the same time, and if you stay away from inner classes until the next version of REAL Studio comes out, then I think you will find that the overhead of using a VC system in this way becomes minimal, increasing the amount of brain power left for being productive.

To recap, the following is a typical folder structure of my projects. A '.git' folder denotes the presence of a repository. If a repository is inside another, then it is assumed that it is a submodule:

  • .git
  • My Project.rbres
  • My Project.rbvcp
  • App.rbbas
  • Build Automation.rbbas
  • MenuBar1.rbmnu
  • Window1.rbfrm
  • My Common Library
    • .git
    • My Common Library.rbres
    • My Common Library.rbvcp
    • App.rbbas
    • Build Automation.rbbas
    • MenuBar1.rbmnu
    • Window1.rbfrm
    • Common
      • Lots
      • And
      • Lots
      • Of
      • Classes

Upon dragging the folder "My Common Library" into the Project Editor of the main project, the main project then contains an exact duplicate of the entire folder. I then delete everything inside My Common Library in the Project Editor except the Common folder. So in the end, the Project Editor contains the following items:

  • My Project.rbres
  • My Project.rbvcp
  • App.rbbas
  • Build Automation.rbbas
  • MenuBar1.rbmnu
  • Window1.rbfrm
  • My Common Library
    • Common
      • Lots
      • And
      • Lots
      • Of
      • Classes

Example

My common code for REALbasic is available online, so I can provide a very real example. Here I will show how to create a new parent project, and add an existing shared library as a submodule. Since parent projects and shared libraries are created the same way (it's just how you use them), once you're done with this example, you should be very close to knowing just about all the basics.

Just in case you skipped this whole page up until now, keep in mind that this example was written while using REAL Studio 2010r2, and Git 1.7.1. Granted, there might be nothing wrong with using alternate versions, but things might not work as easily.

I'll start out with the basic structure. This example involves a single parent project, and my public common library - that's a total of two repositories. My common library will become a submodule of the parent repository.

First, let's make sure things work. You will require Git and a command line to follow this example word-for-word. So, does Git work?

$ git --version
git version 1.7.1

Okay, so Git works. Let's make ourselves a folder for this example so that we don't mess up anything else:

$ cd ~/Desktop
$ mkdir submodule_test
$ cd submodule_test

Now, does the URL to the common library work?

$ git clone git://github.com/andrewkeller/REALbasic-Common-KFS-BSD.git
Initialized empty Git repository in /Users/andrew/Desktop/submodule_test/My Great Project/REALbasic-Common-KFS-BSD/.git/
remote: Counting objects: 782, done.
remote: Compressing objects: 100% (352/352), done.
remote: Total 782 (delta 473), reused 694 (delta 423)
Receiving objects: 100% (782/782), 248.33 KiB | 165 KiB/s, done.
Resolving deltas: 100% (473/473), done.

Looks like it does. You can poke around in it if you would like, but we don't actually want it there. We'll add it as a submodule later. Let's delete it for now (either move it to the trash, or use rm at the command line).

$ rm -rf REALbasic-Common-KFS-BSD

Now that we know that everything seems to be working, we need to create a project that will use the common library.

$ mkdir "My Great Project"
$ cd "My Great Project"
$ git init

At this point, we should have an empty Git repository. You can verify that it is an empty Git repository by seeing if the following command returns the following result:

$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)

Let us now save a brand new REAL Studio project in this folder. The only big requirement is that you save the project as the version control format. The option to do this is in the Save As dialog:

You should now have a ton of files inside your "My Great Project" folder:

$ ls -al
total 80
drwxr-xr-x  12 andrew  staff   408 Jun 14 16:18 .
drwxr-xr-x   3 andrew  staff   102 Jun 14 14:48 ..
-rw-r--r--@  1 andrew  staff  6148 Jun 14 16:05 .DS_Store
-rw-r--r--@  1 andrew  staff   260 Jun 14 15:04 .My Great Project.rbuistate
drwxr-xr-x  10 andrew  staff   340 Jun 14 16:18 .git
-rw-r--r--   1 andrew  staff    12 Jun 14 15:41 .gitignore
-rw-r--r--@  1 andrew  staff   843 Jun 14 15:04 App.rbbas
-rw-r--r--@  1 andrew  staff   281 Jun 14 15:04 Build Automation.rbbas
-rw-r--r--@  1 andrew  staff  2130 Jun 14 15:04 MenuBar1.rbmnu
-rw-r--r--@  1 andrew  staff    12 Jun 14 15:04 My Great Project.rbres
-rw-r--r--@  1 andrew  staff   798 Jun 14 15:04 My Great Project.rbvcp
-rw-r--r--@  1 andrew  staff   766 Jun 14 15:04 Window1.rbfrm

Let me really quick go over the files here. The '.git' folder is the Git repository itself. In general, I don't mess with it. The '.My Great Project.rbuistate' file is one of the files that REAL Studio creates. It is the file that contains the state of the GUI when you last saved, that way, if you re-open your project, you get your tabs back and the window is in the same place, and so on. The rest of the files are all parts of the project itself.

If we ask Git for the current status now, we should get the following result:

$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .My Great Project.rbuistate
#   App.rbbas
#   Build Automation.rbbas
#   MenuBar1.rbmnu
#   My Great Project.rbres
#   My Great Project.rbvcp
#   Window1.rbfrm
nothing added to commit but untracked files present (use "git add" to track)

So, according to what Git told us, we have an empty repository, and we now have a few untracked files. This is good. This is expected.

Now, there's a couple things we're going to want to modify before we move on. First, we do not want to track the file that REAL Studio uses to store the state of the user interface, because it is not critical to the project. Let's tell Git to ignore all files ending with '.rbuistate':

$ echo "*.rbuistate" > .gitignore

While we're at it, let's also exclude obsolete source files, the Builds folder, and debug builds, all created by REAL Studio:

$ echo "*.obsolete" >> .gitignore
$ echo "Builds*" >> .gitignore
$ echo "*.debug" >> .gitignore

We can make sure that the .gitignore file contains what we want, just to make sure:

$ cat .gitignore
*.rbuistate
*.obsolete
Builds*
*.debug

Good. So now, Git will not track the state of REAL Studio's graphical interfaces. Less noise for the repository. If we ask Git for the status of the repository again, it should no longer show that file as untracked:

$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .gitignore
#   App.rbbas
#   Build Automation.rbbas
#   MenuBar1.rbmnu
#   My Great Project.rbres
#   My Great Project.rbvcp
#   Window1.rbfrm
nothing added to commit but untracked files present (use "git add" to track)

There is one more thing to fix, however it is easier to see after we stage the REAL Studio files. Let's go ahead and commit the .gitignore file, just to get it out of the way:

$ git add .gitignore
$ git commit -m "Instructed Git to ignore .rbuistate files."
[master (root-commit) 28daf30] Instructed Git to ignore .rbuistate files.
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore

Now, let's stage everything else we've added:

$ git add -A

Git should now report the current status as such:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   App.rbbas
#   new file:   Build Automation.rbbas
#   new file:   MenuBar1.rbmnu
#   new file:   My Great Project.rbres
#   new file:   My Great Project.rbvcp
#   new file:   Window1.rbfrm
#

Notice how the files are no longer untracked, and are instead staged for the next commit. This is where we find the next (and last) thing to fix. Some of those files use Mac Classic line endings (CR, which is an ASCII 13), and Git only accepts Unix line endings (LF, which is an ASCII 10). We can see the CR line endings by asking Git for all the data that is currently staged:


$ git diff --cached
diff --git a/App.rbbas b/App.rbbas
new file mode 100644
index 0000000..44621da
--- /dev/null
+++ b/App.rbbas
@@ -0,0 +1 @@
+#tag Class^MProtected Class App^MInherits Application^M    #tag Constant, Name = kEditClear, Type = String, Dynamic = False, Default = \"&Delete", Scope = Public^M      #Tag Instance, Platform = Windows, Language = Default, Definition  = \"&Delete"^M     #Tag Instance, Platform = Linux, Language = Default, Definition  = \"&Delete"^M   #tag EndConstant^M^M    #tag Constant, Name = kFileQuit, Type = String, Dynamic = False, Default = \"&Quit", Scope = Public^M     #Tag Instance, Platform = Windows, Language = Default, Definition  = \"E&xit"^M   #tag EndConstant^M^M    #tag Constant, Name = kFileQuitShortcut, Type = String, Dynamic = False, Default = \"", Scope = Public^M      #Tag Instance, Platform = Mac OS, Language = Default, Definition  = \"Cmd+Q"^M        #Tag Instance, Platform = Linux, Language = Default, Definition  = \"Ctrl+Q"^M    #tag EndConstant^M^M^MEnd Class^M#tag EndClass^M
\ No newline at end of file
diff --git a/Build Automation.rbbas b/Build Automation.rbbas
new file mode 100644
index 0000000..f75999a
--- /dev/null
+++ b/Build Automation.rbbas
@@ -0,0 +1 @@
+#tag BuildAutomation^M         Begin BuildStepList Linux^M             Begin BuildProjectStep Build^M              End^M           End^M           Begin BuildStepList Mac OS X^M              Begin BuildProjectStep Build^M              End^M           End^M           Begin BuildStepList Windows^M               Begin BuildProjectStep Build^M              End^M           End^M#tag EndBuildAutomation^M
\ No newline at end of file
diff --git a/MenuBar1.rbmnu b/MenuBar1.rbmnu
new file mode 100644
index 0000000..b6ba99a
--- /dev/null
+++ b/MenuBar1.rbmnu
@@ -0,0 +1 @@
+#tag Menu^MBegin Menu MenuBar1^M   Begin MenuItem FileMenu^M      SpecialMenu = 0^M      Text = "&File"^M      Index = -2147483648^M      AutoEnable = True^M      Begin QuitMenuItem FileQuit^M         SpecialMenu = 0^M         Text = "#App.kFileQuit"^M         Index = -2147483648^M         ShortcutKey = "#App.kFileQuitShortcut"^M         Shortcut = "#App.kFileQuitShortcut"^M         AutoEnable = True^M      End^M   End^M   Begin MenuItem EditMenu^M      SpecialMenu = 0^M      Text = "&Edit"^M      Index = -2147483648^M      AutoEnable = True^M      Begin MenuItem EditUndo^M         SpecialMenu = 0^M         Text = "&Undo"^M         Index = -2147483648^M         ShortcutKey = "Z"^M         Shortcut = "Cmd+Z"^M         MenuModifier = True^M         AutoEnable = True^M      End^M      Begin MenuItem ^M         SpecialMenu = 0^M         Text = "-"^M         Index = -2147483648^M         AutoEnable = True^M      End^M      Begin MenuItem EditCut^M         SpecialMenu = 0^M         Text = "Cu&t"^M         Index = -2147483648^M         ShortcutKey = "X"^M         Shortcut = "Cmd+X"^M         MenuModifier = True^M         AutoEnable = True^M      End^M      Begin MenuItem EditCopy^M         SpecialMenu = 0^M         Text = "&Copy"^M         Index = -2147483648^M         ShortcutKey = "C"^M         Shortcut = "Cmd+C"^M         MenuModifier = True^M         AutoEnable = True^M      End^M      Begin MenuItem EditPaste^M         SpecialMenu = 0^M         Text = "&Paste"^M         Index = -2147483648^M         ShortcutKey = "V"^M         Shortcut = "Cmd+V"^M         MenuModifier = True^M         AutoEnable = True^M      End^M      Begin MenuItem EditClear^M         SpecialMenu = 0^M         Text = "#App.kEditClear"^M         Index = -2147483648^M         AutoEnable = True^M      End^M      Begin MenuItem ^M         SpecialMenu = 0^M         Text = "-"^M         Index = -2147483648^M         AutoEnable = True^M      End^M      Begin MenuItem EditSelectAll^M         SpecialMenu = 0^M         Text = "Select &All"^M         Index = -2147483648^M         ShortcutKey = "A"^M         Shortcut = "Cmd+A"^M         MenuModifier = True^M         AutoEnable = True^M      End^M   End^MEnd^M#tag EndMenu^M
\ No newline at end of file
diff --git a/My Great Project.rbres b/My Great Project.rbres
new file mode 100644
index 0000000..a6d4df6
Binary files /dev/null and b/My Great Project.rbres differ
diff --git a/My Great Project.rbvcp b/My Great Project.rbvcp
new file mode 100644
index 0000000..3f46d58
--- /dev/null
+++ b/My Great Project.rbvcp
@@ -0,0 +1 @@
+Type=Desktop^MRBProjectVersion=2010.02^MClass=App;App.rbbas;&h11191BE4;&h0;false^MWindow=Window1;Window1.rbfrm;&h7766027;&h0;false^MMenuBar=MenuBar1;MenuBar1.rbmnu;&h6CA2E5EA;&h0;false^MBuildSteps=Build Automation;Build Automation.rbbas;&h3AA4DF04;&h0;false^MDefaultWindow=Window1^MAppMenuBar=MenuBar1^MMajorVersion=1^MMinorVersion=0^MSubVersion=0^MNonRelease=0^MRelease=0^MInfoVersion=^MLongVersion=^MShortVersion=^MWinCompanyName=^MWinInternalName=^MWinProductName=^MAutoIncrementVersionInformation=False^MBuildFlags=&h4000^MBuildLanguage=&h0^MDebugLanguage=&h0^MRegion=^MWindowsName=My Application.exe^MMacCarbonMachName=My Application^MLinuxX86Name=MyApplication^MMacCreator=^MMDI=0^MMDICaption=^MDefaultEncoding=&h0^MAppIcon=My Great Project.rbres;&h0^MOSXBundleID=^MDebuggerCommandLine=^MUseGDIPlus=False^MUseBuildsFolder=True^M
\ No newline at end of file
diff --git a/Window1.rbfrm b/Window1.rbfrm
new file mode 100644
index 0000000..a526f2d
--- /dev/null
+++ b/Window1.rbfrm
@@ -0,0 +1 @@
+#tag Window^MBegin Window Window1^M   BackColor       =   &hFFFFFF^M   Backdrop        =   ""^M   CloseButton     =   True^M   Composite       =   False^M   Frame           =   0^M   FullScreen      =   False^M   HasBackColor    =   False^M   Height          =   400^M   ImplicitInstance=   True^M   LiveResize      =   True^M   MacProcID       =   0^M   MaxHeight       =   32000^M   MaximizeButton  =   False^M   MaxWidth        =   32000^M   MenuBar         =   1822615018^M   MenuBarVisible  =   True^M   MinHeight       =   64^M   MinimizeButton  =   True^M   MinWidth        =   64^M   Placement       =   0^M   Resizeable      =   True^M   Title           =   "Untitled"^M   Visible         =   True^M   Width           =   600^MEnd^M#tag EndWindow^M^M#tag WindowCode^M#tag EndWindowCode^M^M
\ No newline at end of file

See the '^M' instances everywhere? Those are all CR line endings. Those must be converted to LF line endings in order for you to not go insane later. Lucky for us, REAL Studio is relatively smart - all we have to do is change the line endings once, and REAL Studio will re-save the file later using the same line endings it read it with.

So, for each of REAL Studio's files, open them in a program that can convert line endings, and tell it to convert the file to Unix line endings. You can use any program, but I'll use Xcode just as an example. First, I'm going to go into Xcode preferences, and tell it to always save existing files using Unix line endings:

Next, I just select REAL Studio's files in Finder, drag them all into Xcode so that I now have 5 windows open (6 minus 1, because one of them is binary and won't open in Xcode), and for each window, tell it to Save, and then close the window. In other words, Command-S, Command-W, until all the windows are gone. Congratulations - all the files now have Unix line endings.

Now, let's add our new changes (since we changed line endings):

$ git add -A

And now, let's ask Git for the data on the stage, like we did before:


diff --git a/App.rbbas b/App.rbbas
new file mode 100644
index 0000000..b6e1662
--- /dev/null
+++ b/App.rbbas
@@ -0,0 +1,20 @@
+#tag Class
+Protected Class App
+Inherits Application
+   #tag Constant, Name = kEditClear, Type = String, Dynamic = False, Default = \"&Delete", Scope = Public
+       #Tag Instance, Platform = Windows, Language = Default, Definition  = \"&Delete"
+       #Tag Instance, Platform = Linux, Language = Default, Definition  = \"&Delete"
+   #tag EndConstant
+
+   #tag Constant, Name = kFileQuit, Type = String, Dynamic = False, Default = \"&Quit", Scope = Public
+       #Tag Instance, Platform = Windows, Language = Default, Definition  = \"E&xit"
+   #tag EndConstant
+
+   #tag Constant, Name = kFileQuitShortcut, Type = String, Dynamic = False, Default = \"", Scope = Public
+       #Tag Instance, Platform = Mac OS, Language = Default, Definition  = \"Cmd+Q"
+       #Tag Instance, Platform = Linux, Language = Default, Definition  = \"Ctrl+Q"
+   #tag EndConstant
+
+
+End Class
+#tag EndClass
diff --git a/Build Automation.rbbas b/Build Automation.rbbas
new file mode 100644
index 0000000..f9b6aee
--- /dev/null
+++ b/Build Automation.rbbas
@@ -0,0 +1,14 @@
+#tag BuildAutomation
+           Begin BuildStepList Linux
+               Begin BuildProjectStep Build
+               End
+           End
+           Begin BuildStepList Mac OS X
+               Begin BuildProjectStep Build
+               End
+           End
+           Begin BuildStepList Windows
+               Begin BuildProjectStep Build
+               End
+           End
+#tag EndBuildAutomation
diff --git a/MenuBar1.rbmnu b/MenuBar1.rbmnu
new file mode 100644
index 0000000..913069c
--- /dev/null
+++ b/MenuBar1.rbmnu
@@ -0,0 +1,87 @@
+#tag Menu
+Begin Menu MenuBar1
+   Begin MenuItem FileMenu
+      SpecialMenu = 0
+      Text = "&File"
+      Index = -2147483648
+      AutoEnable = True
+      Begin QuitMenuItem FileQuit
+         SpecialMenu = 0
+         Text = "#App.kFileQuit"
+         Index = -2147483648
+         ShortcutKey = "#App.kFileQuitShortcut"
+         Shortcut = "#App.kFileQuitShortcut"
+         AutoEnable = True
+      End
+   End
+   Begin MenuItem EditMenu
+      SpecialMenu = 0
+      Text = "&Edit"
+      Index = -2147483648
+      AutoEnable = True
+      Begin MenuItem EditUndo
+         SpecialMenu = 0
+         Text = "&Undo"
+         Index = -2147483648
+         ShortcutKey = "Z"
+         Shortcut = "Cmd+Z"
+         MenuModifier = True
+         AutoEnable = True
+      End
+      Begin MenuItem
+         SpecialMenu = 0
+         Text = "-"
+         Index = -2147483648
+         AutoEnable = True
+      End
+      Begin MenuItem EditCut
+         SpecialMenu = 0
+         Text = "Cu&t"
+         Index = -2147483648
+         ShortcutKey = "X"
+         Shortcut = "Cmd+X"
+         MenuModifier = True
+         AutoEnable = True
+      End
+      Begin MenuItem EditCopy
+         SpecialMenu = 0
+         Text = "&Copy"
+         Index = -2147483648
+         ShortcutKey = "C"
+         Shortcut = "Cmd+C"
+         MenuModifier = True
+         AutoEnable = True
+      End
+      Begin MenuItem EditPaste
+         SpecialMenu = 0
+         Text = "&Paste"
+         Index = -2147483648
+         ShortcutKey = "V"
+         Shortcut = "Cmd+V"
+         MenuModifier = True
+         AutoEnable = True
+      End
+      Begin MenuItem EditClear
+         SpecialMenu = 0
+         Text = "#App.kEditClear"
+         Index = -2147483648
+         AutoEnable = True
+      End
+      Begin MenuItem
+         SpecialMenu = 0
+         Text = "-"
+         Index = -2147483648
+         AutoEnable = True
+      End
+      Begin MenuItem EditSelectAll
+         SpecialMenu = 0
+         Text = "Select &All"
+         Index = -2147483648
+         ShortcutKey = "A"
+         Shortcut = "Cmd+A"
+         MenuModifier = True
+         AutoEnable = True
+      End
+   End
+End
+#tag EndMenu
diff --git a/My Great Project.rbres b/My Great Project.rbres
new file mode 100644
index 0000000..a6d4df6
Binary files /dev/null and b/My Great Project.rbres differ
diff --git a/My Great Project.rbvcp b/My Great Project.rbvcp
new file mode 100644
index 0000000..b372c21
--- /dev/null
+++ b/My Great Project.rbvcp
@@ -0,0 +1,36 @@
+Type=Desktop
+RBProjectVersion=2010.02
+Class=App;App.rbbas;&h11191BE4;&h0;false
+Window=Window1;Window1.rbfrm;&h7766027;&h0;false
+MenuBar=MenuBar1;MenuBar1.rbmnu;&h6CA2E5EA;&h0;false
+BuildSteps=Build Automation;Build Automation.rbbas;&h3AA4DF04;&h0;false
+DefaultWindow=Window1
+AppMenuBar=MenuBar1
+MajorVersion=1
+MinorVersion=0
+SubVersion=0
+NonRelease=0
+Release=0
+InfoVersion=
+LongVersion=
+ShortVersion=
+WinCompanyName=
+WinInternalName=
+WinProductName=
+AutoIncrementVersionInformation=False
+BuildFlags=&h4000
+BuildLanguage=&h0
+DebugLanguage=&h0
+Region=
+WindowsName=My Application.exe
+MacCarbonMachName=My Application
+LinuxX86Name=MyApplication
+MacCreator=
+MDI=0
+MDICaption=
+DefaultEncoding=&h0
+AppIcon=My Great Project.rbres;&h0
+OSXBundleID=
+DebuggerCommandLine=
+UseGDIPlus=False
+UseBuildsFolder=True
diff --git a/Window1.rbfrm b/Window1.rbfrm
new file mode 100644
index 0000000..bf2b44e
--- /dev/null
+++ b/Window1.rbfrm
@@ -0,0 +1,32 @@
+#tag Window
+Begin Window Window1
+   BackColor       =   &hFFFFFF
+   Backdrop        =   ""
+   CloseButton     =   True
+   Composite       =   False
+   Frame           =   0
+   FullScreen      =   False
+   HasBackColor    =   False
+   Height          =   400
+   ImplicitInstance=   True
+   LiveResize      =   True
+   MacProcID       =   0
+   MaxHeight       =   32000
+   MaximizeButton  =   False
+   MaxWidth        =   32000
+   MenuBar         =   1822615018
+   MenuBarVisible  =   True
+   MinHeight       =   64
+   MinimizeButton  =   True
+   MinWidth        =   64
+   Placement       =   0
+   Resizeable      =   True
+   Title           =   "Untitled"
+   Visible         =   True
+   Width           =   600
+End
+#tag EndWindow
+
+#tag WindowCode
+#tag EndWindowCode
+

Since we have no '^M' instances anywhere (not to mention that our lines of code are actually separate lines now), we are good to go. Everything uses Unix line endings now.

Let's finally go ahead and commit the REAL Studio files:

$ git commit -m "Added a blank REAL Studio project to this repository."
[master af87cb6] Added a blank REAL Studio project to this repository.
 6 files changed, 189 insertions(+), 0 deletions(-)
 create mode 100644 App.rbbas
 create mode 100644 Build Automation.rbbas
 create mode 100644 MenuBar1.rbmnu
 create mode 100644 My Great Project.rbres
 create mode 100644 My Great Project.rbvcp
 create mode 100644 Window1.rbfrm

So now we have our parent repository done - finally... I'm going to go get some tea. Be back in 30.

 

Okay, I'm back. Our next step is to link the parent project with the common library. It's basically two steps and we're done. Yes, the hard part is over.

We will now add the common library to our parent project (note that the cwd is still ~/Desktop/submodule_test/My Great Project):

$ git submodule add git://github.com/andrewkeller/REALbasic-Common-KFS-BSD.git "RB Common KFS BSD"
Initialized empty Git repository in /Users/andrew/Desktop/submodule_test/My Great Project/RB Common KFS BSD/.git/
remote: Counting objects: 782, done.
remote: Compressing objects: 100% (352/352), done.
remote: Total 782 (delta 473), reused 694 (delta 423)
Receiving objects: 100% (782/782), 248.33 KiB | 79 KiB/s, done.
Resolving deltas: 100% (473/473), done.

So, what did that command do?

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   .gitmodules
#   new file:   RB Common KFS BSD
#

It created a file called '.gitmodules' that tells Git which submodules exist. Let's add and commit this change:

$ git add -A
$ git commit -m "Added the RB Common KFS BSD library as a submodule."
[master 0c5770f] Added the RB Common KFS BSD library as a submodule.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 RB Common KFS BSD

There we go. The library has now been added as a submodule. Now for linking it up with the main project.

Take the RB Common KFS BSD folder that was just created (located next to the main project's project file), and drag the whole thing right into the Project Editor in REAL Studio, like this:

REAL Studio should then import the whole folder, and the Project Editor should end up like this:

Only one step to go! We must now delete some extraneous files that REAL Studio imported. Delete everything inside the RB Common KFS BSD folder except for the Common folder, like this:

Delete everything except the Common folder, so that your window looks like this -->

Done! You now have full access to the whole repository. Any changes you make to the library are saved in the library's repository. Any library updates you receive via the repository itself will be instantly available to the main project.

There is one more little detail I would like to discuss before I tear myself away from the computer here. Although the arrangement described here works very nicely and creates a very productive environment, the structure is, in one specific way, not very versatile, and not forgiving at all if you stray too far from the arrangement.

REAL Studio's version control format stores all of the items in your projects in the same manner as they display in the Project Editor. For example, in the above picture, App, Window1, MenuBar1, and Build Automation are stored in separate files next to the project file. RB Common KFS BSD becomes a folder in the same place, and Common is a folder inside of that. The pattern continues for the whole source tree.

The coincidence I'm relying on is the subrepository is called RB Common KFS BSD, and the folder in the Project Editor is labeled RB Common KFS BSD. Because the folder structure of the Project Editor exactly overlaps with the folder structure in the subrepository, we get the illusion of a linked external library.

I say illusion because the idea of the RB Common KFS BSD being an "external" "linked" library is actually quite false. The action of dragging the RB Common KFS BSD folder into the Project Editor window does NOT create links for code objects. It creates links for non-code files, but not for the files we actually care about. It instead takes the code in every code file it finds, and imports it directly into the project, storing it internally. The trick is that in the process of storing the imported code locally in the project, it follows the folder structure in the Project Editor (because this is the rbvcp format), right into where the subrepository happens to be located. It is a complete coincidence.

In other words, if your parent project is in your Documents folder, and your common library is on your Desktop, and you drag the common library into the Project Editor of your parent project, REAL Studio will import the classes, just like it always does, except this time, REAL Studio will save the classes where it wants to - next to the project file - which is not where the original library is located.

So, to clarify, if you have ten projects that all use the RB Common KFS BSD library, then you should have ten separate repositories, each of which have their own private clone of the RB Common KFS BSD library (aka a submodule). None of the projects can share a clone of the library.

Unless, of course, if you have two projects in the same folder, which would be the same repository. In that case, the two projects can share the same library.

But I think you get the point by now.

Happy Coding.

Project Download

A repository generated using these instructions is available here.


0 Comments

If you have any comments, feel free to email them to me and I'll post them here.