TicGit-ng 1.0.2.14 → 1.0.2.15
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ticgit-ng/attachment.rb +56 -0
- data/lib/ticgit-ng/base.rb +65 -26
- data/lib/ticgit-ng/cli.rb +59 -37
- data/lib/ticgit-ng/command/attach.rb +48 -0
- data/lib/ticgit-ng/command/help.rb +27 -0
- data/lib/ticgit-ng/command/list.rb +1 -1
- data/lib/ticgit-ng/command/tag.rb +5 -8
- data/lib/ticgit-ng/command.rb +2 -1
- data/lib/ticgit-ng/comment.rb +10 -8
- data/lib/ticgit-ng/ticket.rb +136 -15
- data/lib/ticgit-ng/version.rb +1 -1
- data/lib/ticgit-ng.rb +4 -2
- metadata +19 -16
@@ -0,0 +1,56 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
class Attachment
|
3
|
+
attr_reader :user, :added, :filename, :sha, :attachment_name
|
4
|
+
attr_reader :original_filename
|
5
|
+
alias :read :initialize
|
6
|
+
|
7
|
+
def initialize( fname )
|
8
|
+
#FIXME expect fname to be a raw filename that needs to be converted
|
9
|
+
# into a properly formatted ticket name
|
10
|
+
@filename=fname
|
11
|
+
trailing_underscore= File.basename(fname)[/_$/] ? true : false
|
12
|
+
trailing_underscore=false
|
13
|
+
temp=File.basename(fname).split('_').reverse
|
14
|
+
@added=Time.at(temp.pop.to_i)
|
15
|
+
@user= temp.pop
|
16
|
+
@attachment_name= temp.reverse.join('_')
|
17
|
+
if trailing_underscore
|
18
|
+
@attachment_name << '_'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#Called when attaching a new attachment and when reading/opening attachments
|
23
|
+
#FIXME Make a 'read' and 'create' function to differentiate between
|
24
|
+
# between creation of a ticket and reading an existing ticket.
|
25
|
+
def self.create raw_fname, ticket, time
|
26
|
+
#Attachment naming format:
|
27
|
+
#ticket_name/ATTACHMENTS/123456_jeff.welling@gmail.com_fubar.jpg
|
28
|
+
#raw_fname "/home/guy/Desktop/fubar.jpg"
|
29
|
+
|
30
|
+
#create attachment dir if first run
|
31
|
+
a_name= File.expand_path( File.join(
|
32
|
+
File.join( ticket.ticket_name, 'ATTACHMENTS' ),
|
33
|
+
ticket.create_attachment_name(raw_fname, time)
|
34
|
+
))
|
35
|
+
#create new filename from ticket
|
36
|
+
if File.exist?( File.dirname( a_name ) ) &&
|
37
|
+
!File.directory?( File.dirname(a_name) )
|
38
|
+
|
39
|
+
puts "Could not create ATTACHMENTS directory"
|
40
|
+
exit 1
|
41
|
+
elsif !File.exist?( File.dirname( a_name ) )
|
42
|
+
Dir.mkdir File.dirname( a_name )
|
43
|
+
end
|
44
|
+
#copy/link the raw_filename
|
45
|
+
Dir.chdir( File.dirname(a_name) ) do
|
46
|
+
FileUtils.cp( raw_fname, a_name )
|
47
|
+
end
|
48
|
+
#call init on the new file to properly populate the variables
|
49
|
+
a_name= File.join(
|
50
|
+
File.basename( File.dirname( a_name ) ),
|
51
|
+
File.basename( a_name )
|
52
|
+
)
|
53
|
+
return Attachment.new( a_name )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ticgit-ng/base.rb
CHANGED
@@ -10,6 +10,14 @@ module TicGitNG
|
|
10
10
|
|
11
11
|
def initialize(git_dir, opts = {})
|
12
12
|
@git = Git.open(find_repo(git_dir))
|
13
|
+
|
14
|
+
begin
|
15
|
+
git.lib.full_log_commits 1
|
16
|
+
rescue
|
17
|
+
puts "Git error: please check your git repository, you must have at least one commit in git"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
13
21
|
@logger = opts[:logger] || Logger.new(STDOUT)
|
14
22
|
|
15
23
|
#This is to accomodate for edge-cases where @logger.puts
|
@@ -65,6 +73,22 @@ module TicGitNG
|
|
65
73
|
|
66
74
|
@state = File.expand_path(File.join(@tic_dir, proj, 'state'))
|
67
75
|
|
76
|
+
branches=git.lib.branches_all.map{|b|b.first}
|
77
|
+
unless branches.include?(which_branch?) && File.directory?(@tic_working)
|
78
|
+
if branches.include?(which_branch?) and !File.exist?(@tic_working)
|
79
|
+
#branch exists but tic_working doesn't
|
80
|
+
#this could be because the dir was deleted or the repo itself
|
81
|
+
#was moved, so recreate the dir.
|
82
|
+
reset_ticgitng
|
83
|
+
elsif @init
|
84
|
+
puts "Initializing TicGit-ng"
|
85
|
+
init_ticgitng_branch( branches.include?(which_branch?) )
|
86
|
+
else
|
87
|
+
puts "Please run `ti init` to initialize TicGit-ng for this repository before running other ti commands."
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
68
92
|
if File.file?(@state)
|
69
93
|
load_state
|
70
94
|
else
|
@@ -84,6 +108,9 @@ module TicGitNG
|
|
84
108
|
# save config file
|
85
109
|
def save_state
|
86
110
|
state_list = [@last_tickets, @current_ticket]
|
111
|
+
unless File.exist? @state
|
112
|
+
FileUtils.mkdir_p( File.dirname(@state) )
|
113
|
+
end
|
87
114
|
File.open(@state, 'w+'){|io| Marshal.dump(state_list, io) }
|
88
115
|
File.open(@config_file, 'w+'){|io| io.write(config.to_yaml) }
|
89
116
|
end
|
@@ -103,8 +130,8 @@ module TicGitNG
|
|
103
130
|
end
|
104
131
|
|
105
132
|
# returns new Ticket
|
106
|
-
def ticket_new(title, options = {})
|
107
|
-
t = TicGitNG::Ticket.create(self, title, options)
|
133
|
+
def ticket_new(title, options = {}, time=nil)
|
134
|
+
t = TicGitNG::Ticket.create(self, title, options, time)
|
108
135
|
reset_ticgitng
|
109
136
|
TicGitNG::Ticket.open(self, t.ticket_name, tickets[t.ticket_name])
|
110
137
|
end
|
@@ -122,6 +149,7 @@ module TicGitNG
|
|
122
149
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
123
150
|
ticket.add_comment(comment)
|
124
151
|
reset_ticgitng
|
152
|
+
ticket
|
125
153
|
end
|
126
154
|
end
|
127
155
|
|
@@ -264,6 +292,7 @@ module TicGitNG
|
|
264
292
|
ticket.add_tag(tag)
|
265
293
|
end
|
266
294
|
reset_ticgitng
|
295
|
+
ticket
|
267
296
|
end
|
268
297
|
end
|
269
298
|
|
@@ -273,6 +302,7 @@ module TicGitNG
|
|
273
302
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
274
303
|
ticket.change_state(new_state)
|
275
304
|
reset_ticgitng
|
305
|
+
ticket
|
276
306
|
end
|
277
307
|
end
|
278
308
|
end
|
@@ -282,6 +312,7 @@ module TicGitNG
|
|
282
312
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
283
313
|
ticket.change_assigned(new_assigned)
|
284
314
|
reset_ticgitng
|
315
|
+
ticket
|
285
316
|
end
|
286
317
|
end
|
287
318
|
|
@@ -290,6 +321,7 @@ module TicGitNG
|
|
290
321
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
291
322
|
ticket.change_points(new_points)
|
292
323
|
reset_ticgitng
|
324
|
+
ticket
|
293
325
|
end
|
294
326
|
end
|
295
327
|
|
@@ -298,15 +330,10 @@ module TicGitNG
|
|
298
330
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
299
331
|
@current_ticket = ticket.ticket_name
|
300
332
|
save_state
|
333
|
+
ticket
|
301
334
|
end
|
302
335
|
end
|
303
336
|
|
304
|
-
def comment_add(ticket_id, comment, options = {})
|
305
|
-
end
|
306
|
-
|
307
|
-
def comment_list(ticket_id)
|
308
|
-
end
|
309
|
-
|
310
337
|
def tic_states
|
311
338
|
['open', 'resolved', 'invalid', 'hold']
|
312
339
|
end
|
@@ -334,30 +361,22 @@ module TicGitNG
|
|
334
361
|
def read_tickets
|
335
362
|
tickets = {}
|
336
363
|
|
337
|
-
bs = git.lib.branches_all.map{|b| b.first }
|
338
|
-
|
339
|
-
unless (bs.include?(which_branch?) || bs.include?(which_branch?)) &&
|
340
|
-
File.directory?(@tic_working)
|
341
|
-
unless @init
|
342
|
-
puts "Please run `ti init` to initialize TicGit-ng for this repository before running other ti commands."
|
343
|
-
exit
|
344
|
-
else
|
345
|
-
puts "Initializing TicGit-ng"
|
346
|
-
init_ticgitng_branch(
|
347
|
-
git.lib.branches_all.map{|b| b.first }.include?(which_branch?)
|
348
|
-
)
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
364
|
tree = git.lib.full_tree(which_branch?)
|
353
365
|
tree.each do |t|
|
354
366
|
data, file = t.split("\t")
|
355
367
|
mode, type, sha = data.split(" ")
|
356
368
|
tic = file.split('/')
|
357
369
|
if tic.size == 2 # directory depth
|
358
|
-
|
359
|
-
|
360
|
-
|
370
|
+
ticket, info = tic
|
371
|
+
tickets[ticket] ||= { 'files' => [] }
|
372
|
+
tickets[ticket]['files'] << [info, sha]
|
373
|
+
elsif tic.size == 3
|
374
|
+
ticket, attch_dir, filename = tic
|
375
|
+
if filename
|
376
|
+
filename = 'ATTACHMENTS_' + filename
|
377
|
+
tickets[ticket] ||= { 'files' => [] }
|
378
|
+
tickets[ticket]['files'] << [filename, sha]
|
379
|
+
end
|
361
380
|
end
|
362
381
|
end
|
363
382
|
tickets
|
@@ -410,7 +429,9 @@ module TicGitNG
|
|
410
429
|
def new_file(name, contents)
|
411
430
|
File.open(name, 'w+'){|f| f.puts(contents) }
|
412
431
|
end
|
432
|
+
|
413
433
|
def which_branch?
|
434
|
+
#At present use the 'ticgit' branch for backwards compatibility
|
414
435
|
branches=@git.branches.local.map {|b| b.name}
|
415
436
|
if branches.include? 'ticgit-ng'
|
416
437
|
return 'ticgit-ng'
|
@@ -436,6 +457,24 @@ module TicGitNG
|
|
436
457
|
def needs_reset? cache_mtime, gitlog_mtime
|
437
458
|
((cache_mtime.to_i - gitlog_mtime.to_i) > 120) or ((gitlog_mtime.to_i - cache_mtime.to_i) > 120)
|
438
459
|
end
|
460
|
+
|
461
|
+
def ticket_attach filename, ticket_id=nil, time=nil
|
462
|
+
t = ticket_revparse( ticket_id )
|
463
|
+
ticket= TicGitNG::Ticket.open( self, t, tickets[t] )
|
464
|
+
ticket.add_attach( self, filename, time )
|
465
|
+
reset_ticgitng
|
466
|
+
ticket
|
467
|
+
end
|
439
468
|
|
469
|
+
#ticket_get_attachment()
|
470
|
+
#file_id - return the attachment identified by file_id
|
471
|
+
#new_filename - save the attachment as new_filename
|
472
|
+
#ticket_id - get the attachment from ticket_id instead of current
|
473
|
+
def ticket_get_attachment file_id=nil, new_filename=nil, ticket_id=nil
|
474
|
+
if t = ticket_revparse(ticket_id)
|
475
|
+
ticket = Ticket.open( self, t, tickets[t] )
|
476
|
+
ticket.get_attach( file_id, new_filename )
|
477
|
+
end
|
478
|
+
end
|
440
479
|
end
|
441
480
|
end
|
data/lib/ticgit-ng/cli.rb
CHANGED
@@ -110,44 +110,62 @@ module TicGitNG
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def ticket_show(t, more=nil)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
unless t.comments.empty?
|
132
|
-
puts "Comments (#{t.comments.size}):"
|
133
|
-
t.comments.reverse_each do |c|
|
134
|
-
puts " * Added #{c.added.strftime('%m/%d %H:%M')} by #{c.user}"
|
135
|
-
|
136
|
-
wrapped = c.comment.split("\n").map{|line|
|
137
|
-
line.length > 80 ? line.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip : line
|
138
|
-
}.join("\n")
|
139
|
-
|
140
|
-
wrapped = wrapped.split("\n").map{|line| "\t#{line}" }
|
113
|
+
days_ago = ((Time.now - t.opened) / (60 * 60 * 24)).round
|
114
|
+
|
115
|
+
data = [
|
116
|
+
['Title', t.title],
|
117
|
+
['TicId', t.ticket_id],
|
118
|
+
'',
|
119
|
+
['Assigned', t.assigned],
|
120
|
+
['Opened', "#{t.opened} (#{days_ago} days)"],
|
121
|
+
['State', t.state.upcase],
|
122
|
+
['Points', t.points || 'no estimate'],
|
123
|
+
['Tags', t.tags.join(', ')],
|
124
|
+
''
|
125
|
+
]
|
126
|
+
|
127
|
+
data.each do |(key, value)|
|
128
|
+
puts(value ? "#{key}: #{value}" : key)
|
129
|
+
end
|
141
130
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
131
|
+
#FIXME display attachments inline chronologically with comments
|
132
|
+
unless t.comments.empty? and t.attachments.empty?
|
133
|
+
comments_and_attachments= Hash.new
|
134
|
+
puts "Comments and attachments (#{t.comments.size + t.attachments.size}):"
|
135
|
+
t.comments.each do |c|
|
136
|
+
comments_and_attachments[c.added]=c
|
137
|
+
end
|
138
|
+
|
139
|
+
t.attachments.each do |a|
|
140
|
+
comments_and_attachments[a.added]=a
|
141
|
+
end
|
142
|
+
comments_and_attachments.sort.each {|item|
|
143
|
+
if item[1].class==TicGitNG::Comment
|
144
|
+
#print comment
|
145
|
+
puts " * Added #{item[1].added.strftime('%m/%d %H:%M')} by #{item[1].user}"
|
146
|
+
|
147
|
+
wrapped = item[1].comment.split("\n").map{|line|
|
148
|
+
line.length > 80 ? line.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip : line
|
149
|
+
}.join("\n")
|
150
|
+
|
151
|
+
wrapped = wrapped.split("\n").map{|line| "\t#{line}" }
|
152
|
+
|
153
|
+
if wrapped.size > 6 and more.nil?
|
154
|
+
puts wrapped[0, 6].join("\n")
|
155
|
+
puts "\t** more... **"
|
156
|
+
else
|
157
|
+
puts wrapped.join("\n")
|
158
|
+
end
|
159
|
+
puts
|
160
|
+
else
|
161
|
+
#print attachment
|
162
|
+
puts " * Added #{item[1].added.strftime('%m/%d %H:%M')} by #{item[1].user}"
|
163
|
+
puts " Attachment: #{t.attachments.index(item[1]) }"
|
164
|
+
puts " Filename: #{item[1].attachment_name}"
|
165
|
+
puts
|
166
|
+
end
|
167
|
+
}
|
149
168
|
end
|
150
|
-
end
|
151
169
|
end
|
152
170
|
|
153
171
|
class << self
|
@@ -254,4 +272,8 @@ module TicGitNG
|
|
254
272
|
end
|
255
273
|
|
256
274
|
TicGitNG::CLI.reset_window_width
|
257
|
-
|
275
|
+
begin
|
276
|
+
Signal.trap("SIGWINCH") { TicGitNG::CLI.reset_window_width }
|
277
|
+
rescue
|
278
|
+
TicGitNG::CLI.use_fallback
|
279
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Attach a file to a ticket
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti attach
|
7
|
+
# (print help for 'ti attach')
|
8
|
+
#
|
9
|
+
# ti attach {filename}
|
10
|
+
# (attach {filename} to current ticket)
|
11
|
+
#
|
12
|
+
# ti attach -i {ID} {filename}
|
13
|
+
# (attach file {filename} to ticket with ID {ID})
|
14
|
+
#
|
15
|
+
# ti attach -g {f_id}
|
16
|
+
# (retrieve attached file {f_id}, place in current dir}
|
17
|
+
#
|
18
|
+
# ti attach -g {f_id} -n {new_filename}
|
19
|
+
# (retrieve attached file {f_id}, place as {new_filename})
|
20
|
+
module Attach
|
21
|
+
def parser(opts)
|
22
|
+
opts.banner = "Usage: ti attach [options] [filename]"
|
23
|
+
|
24
|
+
opts.on_head(
|
25
|
+
"-i TICKET_ID", "--id TICKET_ID", "Attach the file to this ticket"){|v|
|
26
|
+
options.ticket_id = v
|
27
|
+
}
|
28
|
+
opts.on_head(
|
29
|
+
"-g FILE_ID", "--get FILE_ID", "Retrieve the file FILE_ID"){|v|
|
30
|
+
puts "Warning: ticket ID argument is not valid with the retrieve attachment argument" if options.ticket_id
|
31
|
+
options.get_file = v
|
32
|
+
}
|
33
|
+
opts.on_head(
|
34
|
+
"-n N_FILENAME", "--new-filename", "Use this filename for the retrieved attachment"){|v|
|
35
|
+
raise ArgumentError, "Error: New filename argument is only valid with the retrieve arrachment argument" unless options.get_file
|
36
|
+
options.new_filename = v
|
37
|
+
}
|
38
|
+
end
|
39
|
+
def execute
|
40
|
+
if options.get_file
|
41
|
+
tic.ticket_get_attachment( options.get_file, options.new_filename )
|
42
|
+
else
|
43
|
+
tic.ticket_attach( args[0], options.ticket_id )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Shows help for ticgit or a particular command
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti help (show help for ticgit)
|
7
|
+
# ti help {command} (show help for specified command)
|
8
|
+
module Help
|
9
|
+
def parser(opts)
|
10
|
+
opts.banner = "Usage: ti help [command]\n"
|
11
|
+
end
|
12
|
+
def execute
|
13
|
+
cli = CLI.new([])
|
14
|
+
if ARGV.length >= 2 # ti help {command}
|
15
|
+
action = ARGV[1]
|
16
|
+
if command = Command.get(action)
|
17
|
+
cli.extend(command)
|
18
|
+
else
|
19
|
+
puts "Unknown command #{action}\n\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
puts cli.usage
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -6,7 +6,7 @@ module TicGitNG
|
|
6
6
|
o.banner = "Usage: ti list [options]"
|
7
7
|
o.on_head(
|
8
8
|
"-o ORDER", "--order ORDER",
|
9
|
-
"Field to order by - one of : assigned,state,date,title"){|v|
|
9
|
+
"Field to order by - one of : assigned,state,date,title [.desc for reverse order]"){|v|
|
10
10
|
options.order = v
|
11
11
|
}
|
12
12
|
|
@@ -13,17 +13,14 @@ module TicGitNG
|
|
13
13
|
puts 'remove'
|
14
14
|
end
|
15
15
|
|
16
|
-
if ARGV.size >
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
tic.ticket_tag(ARGV[2], nil, options)
|
21
|
-
elsif ARGV.size == 2 #tag add 'tag_foobar'
|
22
|
-
tic.ticket_tag(ARGV[1], nil, options)
|
16
|
+
if ARGV.size > 2 # `ti tag 1234abc tagname1`
|
17
|
+
tic.ticket_tag(ARGV[2], ARGV[1].chomp, options)
|
18
|
+
elsif ARGV.size == 2 # `ti tag tagname1`
|
19
|
+
tic.ticket_tag(ARGV[1], nil, options)
|
23
20
|
else
|
24
21
|
puts 'You need to at least specify one tag to add'
|
25
22
|
puts
|
26
|
-
puts
|
23
|
+
puts usage
|
27
24
|
end
|
28
25
|
end
|
29
26
|
end
|
data/lib/ticgit-ng/command.rb
CHANGED
@@ -13,8 +13,9 @@ module TicGitNG
|
|
13
13
|
register 'Attach', 'Attach file to ticket', 'attach'
|
14
14
|
register 'Checkout', 'Checkout a ticket', 'checkout', 'co'
|
15
15
|
register 'Comment', 'Comment on a ticket', 'comment'
|
16
|
+
register 'Help', 'Show help for a ticgit command', 'help'
|
16
17
|
register 'List', 'List tickets', 'list'
|
17
|
-
register 'Milestone', 'List and modify milestones', 'milestone'
|
18
|
+
# register 'Milestone', 'List and modify milestones', 'milestone'
|
18
19
|
register 'New', 'Create a new ticket', 'new'
|
19
20
|
register 'Points', 'Assign points to a ticket', 'points'
|
20
21
|
register 'Recent', 'List recent activities', 'recent'
|
data/lib/ticgit-ng/comment.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
module TicGitNG
|
2
2
|
class Comment
|
3
|
+
attr_accessor :base, :user, :added, :comment
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
@
|
5
|
+
def initialize( c, user, time=nil )
|
6
|
+
raise unless c
|
7
|
+
@comment= c
|
8
|
+
@user=user
|
9
|
+
@added= time.nil? ? Time.now : time
|
10
|
+
self
|
11
|
+
end
|
9
12
|
|
13
|
+
def self.read( base, file_name, sha )
|
10
14
|
type, date, user = file_name.split('_')
|
11
15
|
|
12
|
-
|
13
|
-
@user = user
|
16
|
+
new( (base.git.gblob(sha).contents rescue nil), user, Time.at(date.to_i) )
|
14
17
|
end
|
15
|
-
|
16
18
|
end
|
17
19
|
end
|
data/lib/ticgit-ng/ticket.rb
CHANGED
@@ -25,10 +25,10 @@ module TicGitNG
|
|
25
25
|
@attachments = []
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.create(base, title, options = {})
|
28
|
+
def self.create(base, title, options = {}, time=nil)
|
29
29
|
t = Ticket.new(base, options)
|
30
30
|
t.title = title
|
31
|
-
t.ticket_name = self.create_ticket_name(title)
|
31
|
+
t.ticket_name = self.create_ticket_name(title, time)
|
32
32
|
t.save_new
|
33
33
|
t
|
34
34
|
end
|
@@ -42,39 +42,46 @@ module TicGitNG
|
|
42
42
|
title, date = self.parse_ticket_name(ticket_name)
|
43
43
|
t.opened = date
|
44
44
|
|
45
|
-
ticket_hash['files'].each do |fname,
|
45
|
+
ticket_hash['files'].each do |fname, sha|
|
46
46
|
if fname == 'TICKET_ID'
|
47
|
-
tid =
|
47
|
+
tid = sha
|
48
48
|
elsif fname == 'TICKET_TITLE'
|
49
|
-
t.title = base.git.gblob(
|
49
|
+
t.title = base.git.gblob(sha).contents
|
50
50
|
else
|
51
51
|
# matching
|
52
52
|
data = fname.split('_')
|
53
53
|
|
54
54
|
case data[0]
|
55
55
|
when 'ASSIGNED'
|
56
|
-
t.assigned = data[1]
|
57
|
-
when '
|
58
|
-
|
56
|
+
t.assigned = data[1..-1].join('_')
|
57
|
+
when 'ATTACHMENTS'
|
58
|
+
#Attachments dir naming format:
|
59
|
+
#ticket_name/ATTACHMENTS/123456_jeff.welling@gmail.com_fubar.jpg
|
60
|
+
#data[] format:
|
61
|
+
#"ATTACHMENTS_1342116799_jeff.welling@gmail.com_Rakefile".split('_')
|
62
|
+
filename=File.join( 'ATTACHMENTS', fname.gsub(/^ATTACHMENTS_/,'') )
|
63
|
+
t.attachments << TicGitNG::Attachment.new( filename )
|
59
64
|
when 'COMMENT'
|
60
|
-
t.comments << TicGitNG::Comment.
|
65
|
+
t.comments << TicGitNG::Comment.read(base, fname, sha)
|
61
66
|
when 'POINTS'
|
62
|
-
t.points = base.git.gblob(
|
67
|
+
t.points = base.git.gblob(sha).contents.to_i
|
63
68
|
when 'STATE'
|
64
69
|
t.state = data[1]
|
65
70
|
when 'TAG'
|
66
71
|
t.tags << data[1]
|
67
72
|
when 'TITLE'
|
68
|
-
t.title = base.git.gblob(
|
73
|
+
t.title = base.git.gblob(sha).contents
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
72
77
|
|
78
|
+
if !t.attachments.class==NilClass and t.attachments.size > 1
|
79
|
+
t.attachments= t.attachments.sort {|a1, a2| a1.added <=> a2.added }
|
80
|
+
end
|
73
81
|
t.ticket_id = tid
|
74
82
|
t
|
75
83
|
end
|
76
84
|
|
77
|
-
|
78
85
|
def self.parse_ticket_name(name)
|
79
86
|
epoch, title, rand = name.split('_')
|
80
87
|
title = title.gsub('-', ' ')
|
@@ -144,6 +151,89 @@ module TicGitNG
|
|
144
151
|
base.git.add File.join(ticket_name, t)
|
145
152
|
base.git.commit("added comment to ticket #{ticket_name}")
|
146
153
|
end
|
154
|
+
@comments << TicGitNG::Comment.new( comment, email )
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_attach( base, filename, time=nil )
|
159
|
+
filename=File.expand_path(filename)
|
160
|
+
#FIXME Refactor -- Attachment.new should be called from Ticket.rb
|
161
|
+
# -- Attachment filename creation should be handled
|
162
|
+
# by the Attachment.rb code
|
163
|
+
base.in_branch do |wd|
|
164
|
+
attachments << (a=TicGitNG::Attachment.create( filename, self, time))
|
165
|
+
base.git.add File.join( ticket_name, a.filename )
|
166
|
+
base.git.commit("added attachment #{File.basename(a.filename)} to ticket #{ticket_name}")
|
167
|
+
end
|
168
|
+
if attachments.class!=NilClass and attachments.size > 1
|
169
|
+
@attachments=attachments.sort {|a1,a2| a1.added <=> a2.added }
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
#file_id can be one of:
|
175
|
+
# - An index number of the attachment (1,2,3,...)
|
176
|
+
# - A filename (fubar.jpg)
|
177
|
+
# - nil (nil) means use the last attachment
|
178
|
+
#
|
179
|
+
#if new_filename is nil, use existing filename
|
180
|
+
def get_attach file_id=nil, new_filename=nil
|
181
|
+
attachment=nil
|
182
|
+
pwd=Dir.pwd
|
183
|
+
base.in_branch do |wd|
|
184
|
+
if file_id.to_i==0 and (file_id=="0" or file_id.class==Fixnum)
|
185
|
+
if !attachments[file_id.to_i].nil?
|
186
|
+
attachment= attachments[0]
|
187
|
+
else
|
188
|
+
puts "No attachments match file id #{file_id}"
|
189
|
+
exit 1
|
190
|
+
end
|
191
|
+
elsif file_id.to_i > 0
|
192
|
+
if !attachments[file_id.to_i].nil?
|
193
|
+
attachment= attachments[file_id.to_i]
|
194
|
+
else
|
195
|
+
puts "No attachments match file id #{file_id}"
|
196
|
+
exit 1
|
197
|
+
end
|
198
|
+
else
|
199
|
+
#find attachment by filename
|
200
|
+
attachments.each {|a|
|
201
|
+
attachment=a if a.attachment_name==file_id
|
202
|
+
}
|
203
|
+
if attachment.nil?
|
204
|
+
puts "No attachments match filename #{file_id}"
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
if !new_filename
|
210
|
+
#if no filename is specified...
|
211
|
+
filename= attachment.attachment_name
|
212
|
+
else
|
213
|
+
#if there is a new_filename given
|
214
|
+
if File.exist?( new_filename ) and File.directory?( new_filename )
|
215
|
+
#if it is a directory, not a filename
|
216
|
+
filename= File.join(
|
217
|
+
new_filename,
|
218
|
+
File.basename(attachment.attachment_name)
|
219
|
+
)
|
220
|
+
else
|
221
|
+
#if it is a filename, not a dir
|
222
|
+
filename= new_filename
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
unless File.exist?( File.dirname(filename) )
|
227
|
+
FileUtils.mkdir_p( File.dirname(filename) )
|
228
|
+
end
|
229
|
+
#save attachment [as new_filename]
|
230
|
+
t=File.join( ticket_name, attachment.filename )
|
231
|
+
unless filename[/^\//]
|
232
|
+
filename=File.join( pwd, filename )
|
233
|
+
end
|
234
|
+
FileUtils.cp( t, filename )
|
235
|
+
end
|
236
|
+
self
|
147
237
|
end
|
148
238
|
|
149
239
|
def change_state(new_state)
|
@@ -159,6 +249,8 @@ module TicGitNG
|
|
159
249
|
base.git.add File.join(ticket_name, t)
|
160
250
|
base.git.commit("added state (#{new_state}) to ticket #{ticket_name}")
|
161
251
|
end
|
252
|
+
@state=new_state
|
253
|
+
self
|
162
254
|
end
|
163
255
|
|
164
256
|
def change_assigned(new_assigned)
|
@@ -175,6 +267,8 @@ module TicGitNG
|
|
175
267
|
base.git.add File.join(ticket_name,t)
|
176
268
|
base.git.commit("assigned #{new_assigned} to ticket #{ticket_name}")
|
177
269
|
end
|
270
|
+
@assigned=new_assigned
|
271
|
+
self
|
178
272
|
end
|
179
273
|
|
180
274
|
def change_points(new_points)
|
@@ -187,6 +281,8 @@ module TicGitNG
|
|
187
281
|
base.git.add File.join(ticket_name, 'POINTS')
|
188
282
|
base.git.commit("set points to #{new_points} for ticket #{ticket_name}")
|
189
283
|
end
|
284
|
+
@points=new_points
|
285
|
+
self
|
190
286
|
end
|
191
287
|
|
192
288
|
def add_tag(tag)
|
@@ -214,6 +310,10 @@ module TicGitNG
|
|
214
310
|
base.git.commit("added tags (#{tag}) to ticket #{ticket_name}")
|
215
311
|
end
|
216
312
|
end
|
313
|
+
tags.each {|tag|
|
314
|
+
@tags << tag
|
315
|
+
}
|
316
|
+
self
|
217
317
|
end
|
218
318
|
|
219
319
|
def remove_tag(tag)
|
@@ -232,6 +332,8 @@ module TicGitNG
|
|
232
332
|
base.git.commit("removed tags (#{tag}) from ticket #{ticket_name}")
|
233
333
|
end
|
234
334
|
end
|
335
|
+
@tags.delete_if {|t| t==tag }
|
336
|
+
self
|
235
337
|
end
|
236
338
|
|
237
339
|
def path
|
@@ -250,10 +352,29 @@ module TicGitNG
|
|
250
352
|
assigned.split('@').first rescue ''
|
251
353
|
end
|
252
354
|
|
253
|
-
def self.create_ticket_name(title)
|
254
|
-
[Time.now.to_i.to_s, Ticket.clean_string(title), rand(999).to_i.to_s].join('_')
|
355
|
+
def self.create_ticket_name(title, time=nil)
|
356
|
+
[time.nil? ? Time.now.to_i.to_s : time, Ticket.clean_string(title), rand(999).to_i.to_s].join('_')
|
255
357
|
end
|
256
358
|
|
257
|
-
|
359
|
+
def create_attachment_name( attachment_name, time=nil )
|
360
|
+
raise ArgumentError, "create_attachment_name( ) only takes a string" unless attachment_name.class==String
|
361
|
+
if time
|
362
|
+
if time.to_i == 0
|
363
|
+
raise ArgumentError, "argument 'time' is not valid" unless time.class==Fixnum
|
364
|
+
else
|
365
|
+
time=time.to_i
|
366
|
+
end
|
367
|
+
end
|
368
|
+
time or time=Time.now.to_i
|
369
|
+
time.to_s+'_'+email+'_'+File.basename( attachment_name )
|
370
|
+
end
|
371
|
+
def ==(ticket2)
|
372
|
+
self.instance_variables.each {|instance_var|
|
373
|
+
unless send( instance_var.gsub('@','').to_sym ) == ticket2.instance_eval {send(instance_var.gsub('@','').to_sym)}
|
374
|
+
return false
|
375
|
+
end
|
376
|
+
}
|
377
|
+
return true
|
378
|
+
end
|
258
379
|
end
|
259
380
|
end
|
data/lib/ticgit-ng/version.rb
CHANGED
data/lib/ticgit-ng.rb
CHANGED
@@ -4,6 +4,7 @@ require 'optparse'
|
|
4
4
|
require 'ostruct'
|
5
5
|
require 'set'
|
6
6
|
require 'yaml'
|
7
|
+
require 'pp'
|
7
8
|
|
8
9
|
# Add the directory containing this file to the start of the load path if it
|
9
10
|
# isn't there already.
|
@@ -15,12 +16,12 @@ require 'rubygems'
|
|
15
16
|
require 'git'
|
16
17
|
|
17
18
|
#Only redefine if we are not using 1.9
|
18
|
-
unless Config::CONFIG["ruby_version"][/^\d\.9/]
|
19
|
+
unless (defined?(RbConfig) ? RbConfig : Config)::CONFIG["ruby_version"][/^\d\.9/]
|
19
20
|
# FIXME: Monkeypatch git until fixed upstream
|
20
21
|
module Git
|
21
22
|
class Lib
|
22
23
|
def config_get(name)
|
23
|
-
do_get = lambda do |
|
24
|
+
do_get = lambda do |x|
|
24
25
|
command('config', ['--get', name])
|
25
26
|
end
|
26
27
|
if @git_dir
|
@@ -35,6 +36,7 @@ end
|
|
35
36
|
|
36
37
|
require 'ticgit-ng/base'
|
37
38
|
require 'ticgit-ng/cli'
|
39
|
+
require 'ticgit-ng/attachment'
|
38
40
|
module TicGitNG
|
39
41
|
autoload :VERSION, 'ticgit-ng/version'
|
40
42
|
autoload :Comment, 'ticgit-ng/comment'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: TicGit-ng
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 73
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 2
|
10
|
-
-
|
11
|
-
version: 1.0.2.
|
10
|
+
- 15
|
11
|
+
version: 1.0.2.15
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Scott Chacon
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2012-
|
20
|
+
date: 2012-08-12 00:00:00 Z
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
23
|
name: git
|
@@ -58,28 +58,31 @@ extensions: []
|
|
58
58
|
extra_rdoc_files: []
|
59
59
|
|
60
60
|
files:
|
61
|
-
- bin/ti
|
62
61
|
- bin/ticgitweb
|
62
|
+
- bin/ti
|
63
63
|
- lib/ticgit-ng.rb
|
64
|
-
- lib/ticgit-ng/comment.rb
|
65
64
|
- lib/ticgit-ng/cli.rb
|
66
|
-
- lib/ticgit-ng/
|
67
|
-
- lib/ticgit-ng/command/
|
68
|
-
- lib/ticgit-ng/command/
|
69
|
-
- lib/ticgit-ng/command/init.rb
|
65
|
+
- lib/ticgit-ng/attachment.rb
|
66
|
+
- lib/ticgit-ng/command/milestone.rb
|
67
|
+
- lib/ticgit-ng/command/checkout.rb
|
70
68
|
- lib/ticgit-ng/command/state.rb
|
71
69
|
- lib/ticgit-ng/command/list.rb
|
70
|
+
- lib/ticgit-ng/command/assign.rb
|
72
71
|
- lib/ticgit-ng/command/sync.rb
|
73
|
-
- lib/ticgit-ng/command/
|
72
|
+
- lib/ticgit-ng/command/comment.rb
|
74
73
|
- lib/ticgit-ng/command/points.rb
|
75
|
-
- lib/ticgit-ng/command/assign.rb
|
76
|
-
- lib/ticgit-ng/command/milestone.rb
|
77
74
|
- lib/ticgit-ng/command/tag.rb
|
78
|
-
- lib/ticgit-ng/command/
|
75
|
+
- lib/ticgit-ng/command/show.rb
|
76
|
+
- lib/ticgit-ng/command/help.rb
|
77
|
+
- lib/ticgit-ng/command/recent.rb
|
78
|
+
- lib/ticgit-ng/command/attach.rb
|
79
|
+
- lib/ticgit-ng/command/init.rb
|
80
|
+
- lib/ticgit-ng/command/new.rb
|
81
|
+
- lib/ticgit-ng/comment.rb
|
79
82
|
- lib/ticgit-ng/version.rb
|
83
|
+
- lib/ticgit-ng/ticket.rb
|
80
84
|
- lib/ticgit-ng/command.rb
|
81
85
|
- lib/ticgit-ng/base.rb
|
82
|
-
- lib/ticgit-ng/ticket.rb
|
83
86
|
- LICENSE_MIT
|
84
87
|
- LICENSE_GPL
|
85
88
|
- README.mkd
|
@@ -112,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
115
|
requirements: []
|
113
116
|
|
114
117
|
rubyforge_project: ticgit-ng
|
115
|
-
rubygems_version: 1.
|
118
|
+
rubygems_version: 1.8.15
|
116
119
|
signing_key:
|
117
120
|
specification_version: 3
|
118
121
|
summary: Git based distributed ticketing system
|