public
Fork of buildr/buildr
Description: Apache Buildr
Homepage: http://buildr.apache.org
Clone URL: git://github.com/vic/buildr.git
assaf (author)
Thu Mar 26 18:58:25 -0700 2009
commit  5c3bb4772339677c1d7b85f7f04bfbd8107597b5
tree    66a6fae08d88ddab8e0037b38d46d0cfc9c8459f
parent  8f3c6074839d9ba9e5411ef6e72835b924c9e872
buildr / doc / scripts / buildr-git.rb
100755 513 lines (416 sloc) 18.987 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
#!/usr/bin/env ruby
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with this
# work for additional information regarding copyright ownership. The ASF
# licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
 
 
# This script helps buildr developers to obtain their own git clone from
# github, and also provides GitFlow commands to keep the git mirror in sync
# with Apache SVN.
#
# If you already have a buildr clone, just do the following:
#
# git config alias.apache '!'"ruby $PWD/doc/scripts/buildr-git.rb"
#
# After this, you have a 'git apache' command and you can try (be sure to read the help)
#
# git apache help
# git apache setup svn --help
# git apache sync --help
#
# To configure your local repo for svn synchronization,
#
# git apache update-authors
# git remote add upstream git@github.com:buildr/buildr.git
# git apache setup svn --username apacheLogin --apache-git upstream
# git apache sync
#
# This script can also be run without having a local buildr clone:
#
# ruby -ropen-uri -e 'eval(open("http://svn.apache.org/viewvc/buildr/trunk/doc/scripts/buildr-git.rb?view=co").read)' help
 
 
 
require 'yaml'
require 'open-uri'
 
if $0 == '-e' # invoked from open-uri
  gitflow = "http://svn.apache.org/viewvc/buildr/trunk/doc/scripts/gitflow.rb?view=co"
  eval(open(gitflow).read)
else
  require File.expand_path('gitflow', File.dirname(__FILE__))
end
 
GitFlow.program = 'buildr-git'
 
module BuildrGit
 
  class UpdateUsersCommand < GitFlow/'update-users'
 
    @help = "Update list of Apache SVN committers from Jukka's list."
    @@url = 'http://people.apache.org/~jukka/authors.txt'
 
    def self.authors_file
      File.expand_path('.git/authors.txt', Dir.pwd)
    end
 
    def self.user_email(apache_login, authors_file = nil)
      authors_file ||= self.authors_file
      authors = YAML.load(File.read(authors_file).gsub!(/\s+=\s+/, ': '))
      contact = authors[apache_login]
      fail "You are not listed as apache commiter on #{authors_file}" unless contact
      fail "Not a valid contact line: #{contact}" unless contact =~ /\s+<(.*)>/
      [$`, $1]
    end
 
    def options(opts)
      opts.url = @@url
      opts.file = self.class.authors_file
      [['-u', '--url URL',
        "From URL. defaults to: #{opts.url}", lambda { |url|
          opts.url = url
       }],
       ['-f', '--file FILE',
        "Write to FILE, defaults to #{opts.file}", lambda { |path|
          opts.file = path
        }]
      ]
    end
 
    def execute(opts, argv)
      FileUtils.mkdir_p(File.dirname(opts.file))
      content = open(opts.url).read
      File.open(opts.file, "w") { |f| f.print content }
    end
  end
 
  class CloneCommand < GitFlow/:clone
    @help = "Create a clone from github.com/buildr repository."
    
    def options(opts)
      opts.origin = 'git://github.com/buildr/buildr.git'
      opts.svn_prefix = 'apache'
      opts.project = 'buildr'
      opts.local = expand_path(opts.project)
      [['--prefix SVN_PREFIX', opts.svn_prefix, lambda { |p|
          opts.svn_prefix = p }],
       ['--origin GIT_ORIGIN', opts.origin, lambda { |o|
          opts.origin = o }],
       ['-d', '--dir DIR', opts.local, lambda { |d| opts.local = d }]
      ]
    end
 
    def execute(opts, argv)
      git 'clone', opts.origin, opts.local
      Dir.chdir(opts.local) do
        run 'update-users'
        run 'setup'
      end
    end
  end
 
  class SetupCommand < GitFlow/:setup
    @help = "Setup your buildr clone to be used with git mirror."
    def options(opt)
      []
    end
    
    def execute(opt, argv)
      run 'setup', 'alias'
      run 'setup', 'svn'
    end
  end
 
  class SetupAliasCommand < SetupCommand/:alias
    def execute(opt, argv)
      me = expand_path('doc/scripts/buildr-git.rb')
      git 'config', 'alias.apache', "!ruby #{me}"
    end
  end
 
  class SetupSvnCommand < SetupCommand/:svn
    @help = "Setup for getting changes from Apache SVN."
 
    def options(opt)
      opt.svn_prefix = 'apache'
      opt.svn_path = 'buildr'
      opt.townhall = 'origin'
      [['--username SVN_USER', 'Use Apache svn username for this svn remote',
        lambda { |e| opt.apache_login = e }],
       ['--svn-prefix PREFIX', 'The name of svn remote to use for project.',
        "Defaults to #{opt.svn_prefix}",
        lambda{|p| opt.svn_prefix = p }],
       ['--svn-uri URI', lambda {|p| opt.svn_uri = p }],
       ['--svn-rev REVISION', lambda {|p| opt.svn_rev = p }],
       ['--svn-path PATH', 'The path to append to svn-uri.',
        "Defaults to #{opt.svn_path}", lambda {|p| opt.svn_path = p }],
       ['--apache-git REMOTE', 'The name of remote you are using as town-hall git repo.',
        "Defaults to #{opt.townhall}",
        lambda {|p| opt.townhall = p }]
      ]
    end
 
    def execute(opt, argv)
      authors_file = UpdateUsersCommand.authors_file
      git 'config', 'svn.authorsfile', authors_file
      git 'config', 'apache.svn', opt.svn_prefix
      git 'config', 'apache.git', opt.townhall
      
      if opt.apache_login
        user, email = UpdateUsersCommand.user_email(opt.apache_login, authors_file)
        puts "You claim to be #{user} <#{email}> with apache login: #{opt.apache_login}"
        git('config', 'user.name', user)
        git('config', 'user.email', email)
      end
      
      if opt.svn_rev
        revision = opt.svn_rev
      else
        location, revision = svn_loc_rev
        revision = opt.svn_rev || revision
      end
      
      if opt.svn_uri
        repo = opt.svn_uri
      else
        fail "No #{opt.svn_path} directory on #{location}" unless
          location =~ /\/#{opt.svn_path}/
        repo = $`
      end
 
      # Tell git where the svn repository is
      git('config', "svn-remote.#{opt.svn_prefix}.url", repo)
      git('config', "svn-remote.#{opt.svn_prefix}.fetch",
          "#{opt.svn_path}/trunk:refs/remotes/#{opt.svn_prefix}/trunk")
      git('config', "svn-remote.#{opt.svn_prefix}.branches",
          "#{opt.svn_path}/branches/*:refs/remotes/#{opt.svn_prefix}/*")
      git('config', "svn-remote.#{opt.svn_prefix}.tags",
          "#{opt.svn_path}/tags/*:refs/remotes/#{opt.svn_prefix}/tags/*")
 
      # Store the user for svn dcommit
      if opt.apache_login
        git('config', "svn-remote.#{opt.svn_prefix}.username", opt.apache_login)
      end
 
      # Create the svn branch, do this instead of pulling the full svn history
      git('update-ref', "refs/remotes/#{opt.svn_prefix}/trunk",
          'refs/remotes/origin/master')
      # create tags from git
      git('tag').split.each do |tag|
        git('update-ref', "refs/remotes/#{opt.svn_prefix}/tags/#{tag}",
            "refs/tags/#{tag}")
      end
      # update svn metadata
      mkdir_p(expand_path('.git/svn'))
      svn_meta = expand_path('.git/svn/.metadata')
      git('config', '--file', svn_meta,
          "svn-remote.#{opt.svn_prefix}.branches-maxRev", revision)
      git('config', '--file', svn_meta,
          "svn-remote.#{opt.svn_prefix}.tags-maxRev", revision)
    end
 
    def svn_loc_rev
      meta = sh('git log -n 10 | grep git-svn-id | head -n 1').chomp
      fail "No svn metadata on last 10 commits" if meta.empty?
      meta.split[1].split('@')
    end
  end
 
  class FetchCommand < GitFlow/:fetch
    @help = "Get changes from svn, creating tags, branches on townhall"
    @documentation = <<-DOC
This command can be used to fetch changes from Apache\'s SVN repo.
 
GIT CONFIG VALUES:
 
apache.svn - The svn remote using to get changes from Apache SVN.
Set by setup-svn --svn-prefix.
DOC
 
    def options(opt)
      opt.apache_svn = git('config', '--get', 'apache.svn').chomp rescue nil
      [['--apache-svn SVN_REMOTE', 'The SVN remote used to get changes from Apache',
        "Current value: #{opt.apache_svn}",
        lambda { |r| opt.apache_svn = r }]
       ]
    end
 
    def execute(opt, argv)
      fail "Missing apache.svn config value" unless opt.apache_svn
      git('svn', 'fetch', opt.apache_svn)
    end
  end
 
  class SyncCommand < GitFlow/:sync
    @help = "Synchronizes between Apache svn and git townhall."
    @documentation = <<-DOC
This command will perform the following actions:
* fetch changes from apache svn.
* rebase them on the current branch or on the one specified with --onto
* dcommit (this will push your changes to Apache trunk)
 
GIT CONFIG VALUES:
 
apache.svn
The svn remote using to get changes from Apache SVN.
Set by setup-svn --svn-prefix.
 
apache.git
The git remote used as townhall repository.
Set by setup-svn --townhall.
 
svn-remote.APACHE_GIT.username
If configured, sync will use this svn username while dcommiting.
DOC
 
    def options(opt)
      git('branch').split.tap { |n| opt.current = n[n.index('*')+1] }
      opt.branch = opt.current
      opt.svn_branch = 'trunk'
      opt.git_branch = 'master'
      opt.apache_git = git('config', '--get', 'apache.git').chomp rescue nil
      opt.apache_svn = git('config', '--get', 'apache.svn').chomp rescue nil
      opt.svn_username = git('config', '--get',
                             "svn-remote.#{opt.apache_svn}.username").chomp rescue nil
      [['--apache-svn SVN_REMOTE', 'The SVN remote used to get changes from Apache',
        "Current value: #{opt.apache_svn}",
        lambda { |r| opt.apache_svn = r }],
       ['--apache-git REMOTE', 'The git remote used as town-hall repository.',
        "Current value: #{opt.apache_git}",
        lambda { |r| opt.apache_git = r }],
       ['--username SVN_USER',
        'Specify the SVN username for dcommit',
        "Defaults to: #{opt.svn_username}",
        lambda { |b| opt.svn_username = b }],
       ['--svn-branch SVN_BRANCH',
        'Specify the SVN branch to rebase changes from, and where to dcommit',
        "Defaults to: #{opt.svn_branch}",
        lambda { |b| opt.svn_branch = b }],
       ['--git-branch REMOTE_BRANCH',
        'Specify the remote town-hall branch (on apache.git) to update',
        "Defaults to: #{opt.git_branch}",
        lambda { |b| opt.git_branch = b }],
       ['--branch BRANCH', 'Specify the local branch to take changes from',
        "Current branch: #{opt.branch}",
        lambda { |b| opt.branch = b }]
      ]
    end
 
    def execute(opt, argv)
      # obtain the svn url
      url = git('config', '--get', "svn-remote.#{opt.apache_svn}.url").chomp
      # obtain the path for project
      path = git('config', '--get', "svn-remote.#{opt.apache_svn}.branches").
        chomp.split('/branches').first
      commit_url = "#{url}/#{path}/#{opt.svn_branch}"
 
      # obtain latest changes from svn
      git('svn', 'fetch', '--svn-remote', opt.apache_svn)
      # obtain latest changes from git
      git('fetch', opt.apache_git,
          "#{opt.git_branch}:refs/remotes/#{opt.apache_git}/#{opt.git_branch}")
 
      # rebase svn changes in the desired branch
      git('rebase', "#{opt.apache_svn}/#{opt.svn_branch}", opt.branch)
      git('rebase', "#{opt.apache_git}/#{opt.git_branch}", opt.branch)
      
      # dcommit to the specific svn branch
      ['svn', 'dcommit',
       '--svn-remote', opt.apache_svn, '--commit-url', commit_url].tap do |cmd|
        if opt.svn_username
          cmd << '--username' << opt.svn_username
        end
        git(*cmd)
      end
      
      # update townhall remote ref
      git('update-ref',
          "refs/remotes/#{opt.apache_git}/#{opt.git_branch}",
          "refs/remotes/#{opt.apache_svn}/#{opt.svn_branch}")
 
      # forward the remote townhall/master to apache/trunk
      git('push', opt.apache_git,
          "refs/remotes/#{opt.apache_git}/#{opt.git_branch}:#{opt.git_branch}")
 
      # get back to the original branch
      git('checkout', opt.current)
    end
  end
 
 
  # This one is displayed when the user executes this script using
  # open-uri -e
  HEADER = <<HEADER
 
Buildr official commit channel is Apache's svn repository, however some
developers may prefer to use git while working on several features and
merging other's changes.
 
This script will configure a gitflow copy on so you can commit to svn.
 
Enter <-h> to see options, <-H> to see notes about configured aliases
and recommended workflow, or any other option.
 
Ctrl+D or an invalid option to abort
HEADER
  
  # When fork is completed, we display the following notice on a
  # pager, giving the user a brief overview of git aliases used
  # to keep the mirror in sync.
  NOTICE = <<NOTICE
ALIASES:
 
Some git aliases have been created for developer convenience:
 
git apache fetch # get changes from apache/trunk without merging them
# you can inspect what's happening on trunk without
# having to worry about merging conflicts.
# Inspect the remote branch with `git log apache/trunk`
# Or if you have a git-ui like `tig` you can use that.
 
git apache merge # Merge already fetched changes on the current branch
# Use this command to get up to date with trunk changes
# you can always cherry-pick from the apache/trunk
# branch.
 
git apache pull # get apache-fetch && git apache-merge
git apache push # Push to Apache's SVN. Only staged changes (those
# recorded using `git commit`) will be sent to SVN.
# You need not to be on the master branch.
# Actually you can work on a tiny-feature branch and
# commit directly from it.
#
# VERY IMPORTANT:
#
# Missing commits on Apache's SVN will be sent using
# your apache svn account. This means that you can
# make some commits on behalf of others (like patches
# comming from JIRA issues or casual contributors)
# Review the apache-push alias on .git/config if you
# want to change login-name used for commit to SVN.
#
# See the recommended workflow to avoid commiting
# other developers' changes and the following section.
 
THE GITHUB MIRROR:
 
Buildr has an unofficial git mirror on github, maintained by Apache committers:
 
http://github.com/buildr/buildr
 
This mirror DOES NOT replace Apache's SVN repository. We really care about
using Apache infrastructure and following Apache project guidelines for
contributions. This git mirror is provided only for developers convenience,
allowing them to easily create experimental branches or review code from
other committers.
 
All code that wants to make it to the official Apache Buildr repository needs
to be committed to the Apache SVN repository by using the command:
 
git synchronize
 
This command will synchronize both ways svn<->git to keep trunk upto date.
You need to be an Apache committer and have permissions on the SVN repo.
It's VERY IMPORTANT for Buildr committers to remember that contributions from
external entities wanting to be accepted will require them to sign the Apache ICLA.
We provide the git mirror to make it easier for people to experiment and
contribute back to Buildr, before merging their code in, please remember they
have to create create a JIRA issue granting ASF permission to include their code,
just like any other contribution following Apache's guidelines.
 
So, it's very important - if you care about meritocracy - to follow or at
least that you get an idea of the recommended workflow.
 
RECOMMENDED WORKFLOW:
So now that you have your local buildr copy you can create topic branches
to work on independent features, and still merge easily with head changes.
 
They may seem lots of things to consider, but it's all for Buildr's healt.
As all things git, you can always follow your own workflow and even create
aliases on you .git/config file to avoid typing much. So, here they are:
 
1) get your gitflow configured
(you have already do so, this was the most difficult part)
 
2) create a topic branch to work on, say.. you want to add cool-feature:
 
git checkout -b cool-feature master
# now on branch cool-feature
 
3) hack hack hack.. use the source luke.
every time you feel you have something important like added failing
spec, added part of feature, or resolved some conflict from merges,
you can commit your current progress. If you want to be selective, use:
git commit --interactive
 
3) review your changes, get ALL specs passing, repeat step 3 as needed
 
4) let's see what are they doing on trunk
 
git apache-fetch
# You can inspect the upstream changes without having to merge them
git log apache/trunk # what are they doing!!
5) integrate mainstream changes to your cool-feature branch, you can always
use `git cherry-pick` to select only some commits.
 
git merge apache/trunk cool-feature
6) Go to 3 unless ALL specs are passing.
 
7.a) (Skip to 7.b you have commit bit on Apache's SVN)
Create a patch using `git format-patch`
Promote your changes, create a JIRA issue and upload it granting Apache
license to include your code:
 
https://issues.apache.org/jira/browse/BUILDR
dev@buildr.apache.org
 
7.b) Now you have everyhing on staging area and merged important changes
from apache/trunk, it's time to commit them to Apache's SVN.
 
git apache-push
 
8) Optional. If you are a buildr committer you may want to synchronize
the github mirror for helping others to get changes without having to
wait on Victor's cronjob to run every hour (useful for urgent changes).
 
git synchronize
 
9) Pull changes from origin frequently.
git fetch origin
git rebase --onto origin/master master master
 
10) Unconditionally, Go to step 2 ;)
Share your gitflow workflow, git tips, etc.
 
RESOURCES:
 
http://github.com/buildr/buildr/tree/master
http://git.or.cz/gitwiki/GitCheatSheet
http://groups.google.com/group/git-users/web/git-references
 
NOTICE
  #' for emacs
  
end