I disagree that we need to check the state on every setter method.

If the user modifies the command after they've invoked call(), they get whatever they get.  In the case of CommitCommand, nothing happens.  In the case of LogCommand, their RevWalk internally might get screwed up so the active Iterator returned from their Iterable might get screwed up.  But if they've completed the iteration, it might not be a problem.

Most people won't make the mistake of using a command twice, because they will invoke the setters in a single statement followed by call() and be done.  Since call() returns a type that isn't the command, its somewhat more difficult to invoke a setter and change the command after the call() was made.

But, if you think its better to fail-fast on the setters, I can go along with that.

If we fail-fast on the setters, yes, a RuntimeException like IllegalStateException is going to be better choice than a checked exception that extends from our GitAPIException type.

If we are going to fail-fast in the setters, the easier that is to program, the better, because authors will forget to put in the checkState() invocations.  So some sort of dynamic proxy might be a good idea.  But it means we have to write a public interface for the command, and then a private implementation class that implements the interface.  And Git just acts as a factory for the dynamic proxy.  This pattern may be a bit more work for the author than always calling checkState() in each setter method, but it is easier to ensure its working correctly.

Using Java's native Proxy class is portable without adding additional dependencies.  So I would start with that.  It may not be as fast or as efficient as doing compile-type byte-code modifications via AspectJ, or runtime code generation via ObjectWeb ASM.  But the Git API stuff isn't going to be performance critical for setting up a command object, so I think we can eat the reflection overhead right now.  Besides, if we ever were to change to a different implementation method, having the interface and implementation class would probably also be required there too, so switching would be easy.