Module: GitFlow

Extended by:
GitFlow
Included in:
GitFlow
Defined in:
lib/git_bpf/lib/gitflow.rb

Defined Under Namespace

Modules: Mixin Classes: HelpCommand, NoSuchCommand

Constant Summary collapse

HELP =
<<-HELP

GitFlow is a tool to create custom git commands implemented in ruby.
It is generic enougth to be used on any git based project besides Apache Buildr.

OVERVIEW:

gitflow is intended to help developers with their daily git workflow,
performing repetitive git commands for them. It is implemented in
ruby so you can do anything, from invoking rake tasks to telling
people on twitter you are having trouble with their code :P.

To get help for a specific command use:
    gitflow.rb help command
    gitflow.rb command --help

For convenience you can create an alias to be execute using git.
The following example registers buildr-git.rb, which provides apache
svn and git synchronization commands:

    git config alias.apache '!'"ruby $PWD/doc/scripts/buildr-git.rb"

After that you can use
    git apache command --help

EXTENDING YOUR WORKFLOW:

You can create your own gitflow commands, to adapt your development
workflow.

Simply create a ruby script somewhere say ~/.buildr/gitflow.rb
And alias it in your local repo:

    git config alias.flow '!'"ruby ~/.buildr/gitflow.rb"
    git config alias.work '!'"ruby ~/.buildr/gitflow.rb my-flow sub-work"

A sample command would look like this.. (you may want to look at buildr-git.rb)

    #!/usr/bin/env ruby
    require /path/to/gitflow.rb

    class MyCommand < GitFlow/'my-flow'

      @help = "Summary to be displayed when listing commands"
      @documentation = "Very long help that will be paged if necessary. (for --help)"

      # takes an openstruct to place default values and option values.
      # returns an array of arguments given to optparse.on
      def options(opts)
        opts.something = 'default'
        [
         ['--name NAME', lambda { |n| opts.name = n }],
         ['--yes', lambda { |n| opts.yes = true }]
        ]
      end

      # takes the opts openstruct after options have been parsed and
      # an argv array with non-option arguments.
      def execute(opts, argv)
        # you can run another command using
        run('other-command', '--using-this', 'arg')
        some = git('config', '--get', 'some.property').chomp rescue nil
        page { puts "This will be paged on terminal if needed" }
      end

      class SubCommand < MyCommand/'sub-work'
        ... # implement a subcommand
      end

    end

You would then get help for your command with

    git flow my-flow --help
    git work --help

Using gitflow you can customize per-project git interface.

HELP

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#programObject

Returns the value of attribute program.



24
25
26
# File 'lib/git_bpf/lib/gitflow.rb', line 24

def program
  @program
end

#should_runObject

Returns the value of attribute should_run.



24
25
26
# File 'lib/git_bpf/lib/gitflow.rb', line 24

def should_run
  @should_run
end

#traceObject

Returns the value of attribute trace.



24
25
26
# File 'lib/git_bpf/lib/gitflow.rb', line 24

def trace
  @trace
end

Instance Method Details

#/(command_name) ⇒ Object

Return a class to be extended in order to register a GitFlow command if command name is nil, it will be registered as the top level command. Classes implementing commands also provide this method, allowing for sub-command creation.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/git_bpf/lib/gitflow.rb', line 140

def /(command_name)
  command_name = command_name.to_s unless command_name.nil?
  cls = Class.new { include GitFlow::Mixin }
  (class << cls; self; end).module_eval do
    attr_accessor :help, :documentation, :command
    define_method(:/) do |subcommand|
      raise "Subcommand cannot be nil" unless subcommand
      GitFlow/([command_name, subcommand].compact.join(' '))
    end
    define_method(:inherited) do |subclass|
      subclass.command = command_name
      GitFlow.commands[command_name] = subclass
    end
  end
  cls
end

#command(argv) ⇒ Object



173
174
175
176
177
178
179
180
# File 'lib/git_bpf/lib/gitflow.rb', line 173

def command(argv)
  cmds = []
  argv.each_with_index do |arg, i|
    arg = argv[0..i].join(' ')
    cmds << commands[arg] if commands.key?(arg)
  end
  cmds.last || commands[nil]
end

#commandsObject



157
158
159
# File 'lib/git_bpf/lib/gitflow.rb', line 157

def commands
  @commands ||= Hash.new
end

#optparseObject



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/git_bpf/lib/gitflow.rb', line 161

def optparse
  optparse = opt = OptionParser.new
  opt.separator ' '
  opt.separator 'OPTIONS'
  opt.separator ' '
  opt.on('-h', '--help', 'Display this help') do
    GitFlow.pager; puts opt; throw :exit
  end
  opt.on('--trace', 'Display traces') { GitFlow.trace = true }
  optparse
end

#pagerObject



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/git_bpf/lib/gitflow.rb', line 110

def pager
  return if RUBY_PLATFORM =~ /win32/
  return unless STDOUT.tty?

  read, write = IO.pipe

  unless Kernel.fork # Child process
    STDOUT.reopen(write)
    STDERR.reopen(write) if STDERR.tty?
    read.close
    write.close
    return
  end

  # Parent process, become pager
  STDIN.reopen(read)
  read.close
  write.close

  ENV['LESS'] = 'FSRX' # Don't page if the input is short enough

  Kernel.select [STDIN] # Wait until we have input before we start the pager
  pager = ENV['PAGER'] || 'less'
  exec pager rescue exec '/bin/sh', '-c', pager
end

#run(*argv) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/git_bpf/lib/gitflow.rb', line 182

def run(*argv)
  catch :exit do
    command = self.command(argv).new
    argv = argv[command.class.command.split.length..-1] if command.class.command
    parser = optparse
    parser.banner = "Usage: #{GitFlow.program} #{command.class.command} [options]"
    options = OpenStruct.new
    if command.respond_to?(:options)
      command.options(options).each { |args| parser.on(*args) }
    end
    if command.class.documentation && command.class.documentation != ''
      parser.separator ' '
      parser.separator command.class.documentation.split(/\n/)
    end
    parser.parse!(argv)
    command.execute(options, argv)
  end
end