An upgrade of gitflow

Last week, I silently tagged gitflow 0.2. The most important changes since 0.1 are:

  • Order of arguments changed to have a more “gitish” subcommand structure. For example, you now say:
    git flow feature start myfeature
  • Better initializer. git flow init now prompts interactively to set up a gitflow enabled repo.
  • Added a command to list all feature/release/hotfix/support branches, e.g.:
    git flow feature list
  • Made all merge/rebase operations failsafe, providing a non-destructive workflow in case of merge conflicts.
  • Easy diff’ing of all changes on a specific (or the current) feature branch:
    git flow feature diff [feature]
  • Add support for feature branch rebasing:
    git flow feature rebase
  • Some subactions now take name prefixes as their arguments, for convenience. For example, if you have feature branches called “experimental”, “refactoring” and “feature-X”, you could say:
    git flow feature finish ref

    And gitflow will know you mean the “refactoring” feature branch.

    These actions are: finish, diff and rebase.

  • Much better overall sanity checking.
  • Better portability (POSIX compliant code)
  • Better (more portable) flag parsing using Kate Ward’s shFlags.
  • Improved installer. To install git flow as a first-class Git subcommand, simply type:
    sudo make install
  • Major and minor bug fixes.

That’s all for now.

Unexpected side effects in Python classes

Today, I lost several hours while debugging a language implementation detail in Python that I did not know of and that really feels counterintuitive and dangerous to me.

I was writing unit tests for a Python class that I was implementing, when one of the tests that had repeatedly been passing suddenly failed. Moreover, the failing test case was really for testing some completely unrelated piece of functionaly. This simply could not be broken!

After at least an hour of scrutinizing the code, I was able to distill the real problem, which I think is summarized here in the most compact way:

Creating a simple Foo instance twice exposes the ugly side effect: the second Foo instance has an already initialized x instance variable when the constructor enters! Yuck! Moreover, now, too:

Apparently, the x “instance variable” is a shared object, much like a global or class variable.

To be even more confusing, this doesn’t seem to hold for basic data types. For example, change the dictionary to an integer, and the example behaves as expected:

The behaviour demystified

The real confusion here is that I was thinking that I was creating “instance variables”, like you would in C++ or Java. As the Python documentation mentions:

“data attributes correspond to [...] to data members in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to.”

Yes, I knew that, but nonetheless my real-world class is much bigger than Foo and I wanted an explicit overview on which instance variables are in this class. Hence the data member.

However, this is not how the Python interpreter processes Python code. In fact, upon class definition, the statement x = {} is executed within the scope of the newly defined class. To prove this:

Even without a constructor or instance variable, we can access the data member x. Of course. Now this suddenly seems obvious.

But what about our instance variables? Apparently, when we create a new instance of Bar, the instance data member x is initially pointing to the same object as the class data member x. The following example proves this:

This example also demonstrates the subtlety of the accidentally discovered side-effect. Remember how we were changing the dictionary in our initial example? self.x[id] = id
The instance data member was pointing to the same object as the class data member. By updating the dictionary, the single dictionary object was changed, causing unwanted side effects in other class instances.

In the gist above, we force x to point to a new dictionary by the assignment self.x = { id:id }. In other words, x points to a new object! This also perfectly explains why the integer example worked—it’s the same kind of assignment.

Conclusion

To summarize, I learned some important lessons today:

  • All the time, I have been creating class data members in all my classes, without knowing this.
  • I initialized those members to default values, effectively creating useless objects that are never accessed and just claiming memory.
  • Although it can be explained, a seemingly innocent statement like x = {} can have very ugly side effects. Be warned!
  • Never underestimate the power of unit tests. It is absolutely worth the investment.

gitflow 0.1 released

After the overwhelming attention and feedback on the Git branching model post, a general consensus was that this workflow would benefit from some form of proper scriptability. The workflow works seamlessly if you perform the steps involved manually, but hey… manually is manually, really.

UPDATE 2/4/2010:
Anyone reading this: I recommend NOT USING this very early release, but to jump on the current develop tip, which is much more mature. Release 0.2 is coming very soon.

An assisting tool (dubbed gitflow) was therefore created to provide simple, high-level commands to adopt the workflow into your own software development process. It’s free and it’s open source. Feel free to contribute to it if you like.

Fork me on Github:
http://github.com/nvie/gitflow

Since this morning, the first working release 0.1 was tagged, albeit very basic.

A quick walkthrough

The gitflow script essentially features six subcommands: paired start/finish commands for managing the different types of branches from the originating article:

  • Feature branches
    • gitflow start feature myfeature
    • gitflow finish feature myfeature
  • Release branches
    • gitflow start release version-id
    • gitflow finish release version-id
  • Hotfix branches
    • gitflow start hotfix version-id
    • gitflow finish hotfix version-id

Each of these scripts exactly reports what actions were taken and what follow-up actions are required by the user. This output will be polished in future versions to improve the UX. An example output:

$ gitflow finish feature foo
Branches 'develop' and 'origin/develop' have diverged.
And local branch 'develop' is ahead of 'origin/develop'.
Switched to branch "develop"
Your branch is ahead of 'origin/develop' by 12 commits.
Merge made by recursive.
 README |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
Deleted branch foo (cd3effb).

Summary of actions:
- The feature branch 'foo' was merged into 'develop'
- Feature branch 'foo' has been removed
- You are now on branch 'develop'

Limitations

The script is very limited at the moment yet, but future versions will fix that, too. Some of the main limitations:

  • Branch names (master, develop) and the remote repo name (origin) are currently fixed.
  • There is no support for dealing with merge conflicts yet.
  • There is no support for support-* branches (see the original comment that proposed this extension)
  • There is no documentation.
  • There is no installer.

However, as this post is written, some of the limitations are already taken care of by community members. Power to the open source!

sudo make sandwich

This one is just too funny not to link.

Image from xkcd.com.

Building Git from scratch on Snow Leopard

When you try to build Git from scratch on a Snow Leopard machine, you may have ran into the following problem:

$ git clone git://github.com/git/git.git
Initialized empty Git repository in /Users/nvie/git/.git/
remote: Counting objects: 111619, done.
remote: Compressing objects: 100% (28007/28007), done.
remote: Total 111619 (delta 82192), reused 111264 (delta 81852)
Receiving objects: 100% (111619/111619), 27.60 MiB | 531 KiB/s, done.
Resolving deltas: 100% (82192/82192), done.
$ cd git
$ make prefix=/usr/local/bin/git
GIT_VERSION = 1.6.2.rc0.90.g0753
    * new build flags or prefix
    CC fast-import.o
    CC abspath.o
    :
    :
ld: warning: in /opt/local/lib/libexpat.dylib, file is not of required architecture
    ...
ld: symbol(s) not found
collect2: ld returned 1 exit status

Then, you have a pretty big change you are having an old Darwin ports (macports) collection in use which has not yet been upgraded to Snow Leopard’s new x64 architecture.

There is, however, a simple solution to this, namely to have the git build ignore the Darwin ports, simply by adding the following parameter to the build:

$ make NO_DARWIN_PORTS=1 prefix=/usr/local/bin/git

A successful Git branching model

In this post I present the development model that I’ve introduced for all of my projects (both at work and private) about a year ago, and which has turned out to be very successful. I’ve been meaning to write about it for a while now, but I’ve never really found the time to do so thoroughly, until now. I won’t talk about any of the projects’ details, merely about the branching strategy and release management.

Read the rest of this entry »

Avoiding fast-forward merges in Git

Just a pointer to an excellent post I wanted to share with you:

In the section “Why we shouldn’t fast-forward” at http://robey.lag.net/2009/11/29/more-git.html, Robey explains very clearly how fast forwards effectively make you loose historical information that may be crucial to ever reconstruct or understand your own project’s history.

Auto-generate classes for your Core Data data model, revisited

A few months ago, I wrote about automatically generating classes for your Core Data entities and how to automate Xcode using users scripts, such that, when your model changed, you only needed to run your custom script again and your intermediate model files would reflect the new situation.

Well, the guys from the mogenerator project have come up with a far superior solution in the mean time. The newest version of mogenerator comes with an Xcode plugin named Xmo’d, which monitors your *.xcdatamodel file for changes and, as soon as it changes, regenerates all of the neccessary files.

This means that there is officially no more reason not to use mogenerator.

To set it up, download the installer package from their (improved) project website and install it. (Before installing, please read the important release note about the renamed method +newInManagedObjectContext:.)

When installed, all you need to do is Command-click your *.xcdatamodel file, click Get Info, switch to the Comments tab and add the string “xmod” to the comment field. This is the trigger for Xmo’d to start (re)generating your machine-classes (the underscored class files) when the data model changes. Brilliant!

Adding the trigger to the comment field.

Oh, the default location at which the generated files will be emitted, is in a folder named after your project, right next to where your *.xcdatamodel already sits:

Location where output files are generated

Enjoy it and spread the word!

Automatically generate classes for your Core Data data model

When designing a Core Data data model for your Xcode projects, you can choose to create Objective-C object wrappers for your entities, so that you can profit from type-safe code. The normal, tedious, workflow for this is that you select each entity from the model designer, select all of its attributes and relationships, Ctrl-click it and from the contextual menu first select “Copy Obj-C 2.0 Method Declarations To Clipboard”, paste it into the appropriate class header file, then do the same thing for the method implementations in the class implementation file. Waaaaaay too much work. Not to mention the manual copy-pastes are really hard to keep in sync once you start adding functionality to these class files, since you don’t want to overwrite those additions, but you want to keep replacing everything else.

Meet mogenerator

Fortunately, there is a great way for automating this process, using mogenerator. The tool can be downloaded as a DMG installer (Aral Balkan’s blog mentions a workaround for older Xcode versions, but for Xcode 3.1.3 it worked out of the box for me), or you can checkout the sources from github and build it yourself.

The mogenerator command line tool eases this generation process by reading the *.xcdatamodel file and generating both class files and intermediate class files for each entity. The intermediate classes (called machine classes) are continuously overwritten by subsequent regenerations, so you should never edit the contents of these files. The actual model object classes (called human classes) inherit from those intermediate classes with a default empty implementation, allowing for all manual extensions.

For example, when you design a model with two entities Foo and Bar, mogenerator can be invokes as follows:

mogenerator -m MyDocument.xcdatamodel -M Entities -H Model

The flag -m sets the input model file, while -M and -H specify the output directories where the machine and human classes should be generated respectively.

This does a few things:

  • In the Entities subdirectory, there will be generated header and implementation files for NSManagedObject subclasses called _Foo and _Bar;
  • In the Model subdirectory, there will be generated classes called Foo and Bar—respective subclasses of _Foo and _Bar. These are only created if not available yet. Otherwise, they are left as is.

Wrapping it up

The trick of how mogenerator works is that you can run the script as often as you want. After every change in your model, you’ll want to re-run the generation again to update the machine classes. You could easily leave Xcode, switch over to Terminal and issue the command above. But you’ll get quite tired of that after a few times.

Therefore, I’ve written a custom user script that can be added to Xcode (see figure), which does the following:

  • You can configure the output directories in the first lines of the script. There is no per-project configuration, so choose them as you would like to use them with all your projects;
  • Mind that these generated files are not automatically included in your Xcode project. Drag them there once and ideally put the machine generated classes into a group under “Other resource”, so you never have to see them again. Whenever you add a new class to your model, new files will be generated, so again you must drag the new files to reference those, of course!
  • The script can be run with any file in the project opened. It starts out with that file and walks up the directory tree to search for your Xcode project. If found, it executes all the rest from your project directory. (Suggestions are welcome, I could not find a better implementation since a variable like %%%{PBXProjectPath}%%% does not seem to exist.)
  • It invokes mogenerator to generate all model classes for the project. It is smart enough to detect whether you are using Brian Webster’s BWOrderedManagedObject in your project. If so, your generated machine classes will inherit from BWOrderedManagedObject instead of NSManagedObject.

Edit user script window

To add this script to Xcode, open the menu Scripts (the icon) > Edit User Scripts… Click the “+”-button on the bottom-left and select “New shell script”. Set the values for Input, Directory, Output and Errors as in the screenshot above, then copy-paste the script below into the code window. Add a nice keyboard shortcut to this action to top it off :-) I’ve chosen ^⌥⌘G for this.

Please feel free to leave any comments if this helped you.

#!/bin/sh
#
# Automatic (re)generation of model classes for all *.xcdatamodel files.
# Written by Vincent Driessen
#
# You are free to use this script in any way.
# The original blog post is http://nvie.com/archives/263
#
 
# Define output directories
MACHINE_DIR="Entities"
MODEL_DIR="Model"
 
# Look for the Xcode project directory for this file
cd `dirname "%%%{PBXFilePath}%%%"`
while [ `ls -d *.xcodeproj 2>/dev/null | wc -l` -eq 0 ]; do
    cd ..
    if [ "`pwd`" = "/" ]; then
        echo "No Xcode project found."
        exit 1
    fi
done
 
echo "Project directory is `pwd`"
 
#
# Check to see whether the base class is just a default (NSManagedObject) or maybe
# Brian Webster's excellent BWOrderedManagedObject.
# http://www.fatcatsoftware.com/blog/2008/per-object-ordered-relationships-using-core-data
#
# NOTE:
# The check really is quite arbitrary: if there exists a file called BWOrderedManagedObject.h
# somewhere below the project root directory, we assume that we want to use this as the base
# class for all generated classes.
#
EXTRA_FLAGS=
if [ `find . -name BWOrderedManagedObject.h | wc -l` -gt 0 ]; then
	EXTRA_FLAGS+="--base-class BWOrderedManagedObject"
fi
 
# Generate the model classes using mogenerator
for model in `find . -name '*.xcdatamodel'`; do
	# The output directories have to exist, so create them
    mkdir -p "${MACHINE_DIR}" "${MODEL_DIR}"
    mogenerator ${EXTRA_FLAGS} -m "${model}" -M "${MACHINE_DIR}" -H "${MODEL_DIR}"
done

NSManagedObjectContext extensions

The Core Data framework rules, and its API is really really powerful. But really, why does the Core Data API require us to write so much boilerplate code? Simple things need to be simple.

Why is the deletion of a managed object from the NSManagedObjectContext so easy:

[context deleteObject:someObject];

Compared to its creation:

[NSEntityDescription insertNewObjectForEntityForName:@"someObjectClassName"
                              inManagedObjectContext:context];

Extending NSManagedObjectContext

Add the following category on NSManagedObjectContext to all of your Core Data projects and your pains will be history.

@implementation NSManagedObjectContext (NSManagedObjectContextConvenienceMethods)
 
- (id)newObject:(Class)entity {
    return [NSEntityDescription insertNewObjectForEntityForName:[entity description]
                                         inManagedObjectContext:self];
}
 
@end

Now, a call to create a new object is as easy as deleting it.

[context newObject:[someEntity class]];

Further enhancements of NSManagedObject

Matt Gallagher has written an excellent article about how to further enhance NSManagedObject for adding simple, one-line fetch support. Be sure to check it out.

←Older