git-thin 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +11 -0
- data/Rakefile +13 -0
- data/bin/git-thin +10 -0
- data/bin/thin +10 -0
- data/git-thin.gemspec +26 -0
- data/lib/git-thin.rb +5 -0
- data/lib/git-thin/command.rb +40 -0
- data/lib/git-thin/command/action.rb +117 -0
- data/lib/git-thin/command/checkout.rb +17 -0
- data/lib/git-thin/command/clone.rb +30 -0
- data/lib/git-thin/command/detect.rb +192 -0
- data/lib/git-thin/command/expire.rb +125 -0
- data/lib/git-thin/command/export.rb +256 -0
- data/lib/git-thin/command/fetch.rb +17 -0
- data/lib/git-thin/command/hook.rb +357 -0
- data/lib/git-thin/command/pull.rb +18 -0
- data/lib/git-thin/command/thin_config.rb +326 -0
- data/lib/git-thin/gem_version.rb +3 -0
- data/lib/git-thin/utils/utils.rb +133 -0
- data/spec/command/thin_spec.rb +12 -0
- data/spec/spec_helper.rb +50 -0
- metadata +111 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'claide'
|
2
|
+
require 'json'
|
3
|
+
require 'git-thin/command/action'
|
4
|
+
|
5
|
+
module GitThin
|
6
|
+
|
7
|
+
class Pull < Thin
|
8
|
+
include Action
|
9
|
+
self.arguments = Action.arguments
|
10
|
+
self.summary = Action.summary 'pull'
|
11
|
+
self.description = Action.description 'pull'
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
super
|
15
|
+
setup(argv,'pull')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'claide'
|
2
|
+
require 'json'
|
3
|
+
# require 'git-thin/utils/utils'
|
4
|
+
|
5
|
+
module GitThin
|
6
|
+
class ThinConfig
|
7
|
+
include GitThinUtils
|
8
|
+
DEFAULT = 'default'.freeze
|
9
|
+
def initialize(name,keys)
|
10
|
+
@name = name
|
11
|
+
@keys = keys
|
12
|
+
@config = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
return @name
|
17
|
+
end
|
18
|
+
def keys
|
19
|
+
return @keys
|
20
|
+
end
|
21
|
+
def setValue(value,key = nil )
|
22
|
+
if key
|
23
|
+
if @keys.include? key
|
24
|
+
@config[key] = value
|
25
|
+
end
|
26
|
+
else
|
27
|
+
for key in @keys
|
28
|
+
@config[key] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def getValue(key = nil )
|
34
|
+
if key
|
35
|
+
value = @config[key]
|
36
|
+
if value.nil? && key == 'path'
|
37
|
+
value = @config['lfs']
|
38
|
+
end
|
39
|
+
return value
|
40
|
+
else
|
41
|
+
return @config.values
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.prase_config(configs)
|
46
|
+
ret_configs = {}
|
47
|
+
for config in configs
|
48
|
+
items = config.split('=')
|
49
|
+
if items.length != 2
|
50
|
+
logE 'error thin config:'+config
|
51
|
+
next
|
52
|
+
end
|
53
|
+
name = items[0].strip.downcase
|
54
|
+
value = items[1].strip.split(',')
|
55
|
+
name_items = name.split('.')
|
56
|
+
if name_items.length <= 1
|
57
|
+
logE 'error thin config:'+config
|
58
|
+
next
|
59
|
+
end
|
60
|
+
if name_items[0] != 'thin'
|
61
|
+
# logE 'error thin config:'+config
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
config_name = name_items[1]
|
66
|
+
config = ret_configs[config_name]
|
67
|
+
if not config
|
68
|
+
config = ThinConfig.new config_name,['lfs','path']
|
69
|
+
ret_configs[config_name] = config
|
70
|
+
end
|
71
|
+
|
72
|
+
if name_items.length > 2
|
73
|
+
config_key = name_items[2]
|
74
|
+
config.setValue(value,config_key)
|
75
|
+
else
|
76
|
+
config.setValue value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
return ret_configs
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.prase_pwd_config(configs,source_root,pwd)
|
84
|
+
types = []
|
85
|
+
if pwd.index(source_root) == 0
|
86
|
+
# 在git 目录中,判断是否命中path路径
|
87
|
+
length = source_root.length
|
88
|
+
sub = pwd[length+1..-1]
|
89
|
+
if sub
|
90
|
+
for key in configs.keys
|
91
|
+
paths = configs[key].getValue('path')
|
92
|
+
for path in paths
|
93
|
+
if sub.index(path) == 0
|
94
|
+
types.push key.downcase
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
100
|
+
logW 'You are out of the repo'
|
101
|
+
end
|
102
|
+
return types
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.prase_type_config(argv,configs,source_root=nil ,pwd=nil )
|
106
|
+
types = []
|
107
|
+
if argv.arguments.length > 0 && argv.arguments[0].index('-')!=0
|
108
|
+
first_args = argv.arguments[0].split '|'
|
109
|
+
need_shift = false
|
110
|
+
for arg in first_args
|
111
|
+
if configs.keys.include? arg.downcase
|
112
|
+
types.push arg.downcase
|
113
|
+
need_shift = true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
if need_shift
|
117
|
+
argv.shift_argument
|
118
|
+
end
|
119
|
+
elsif pwd && source_root
|
120
|
+
types = types.concat prase_pwd_config(configs,source_root,pwd)
|
121
|
+
end
|
122
|
+
if types.length == 0
|
123
|
+
types.push(DEFAULT)
|
124
|
+
end
|
125
|
+
return types
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
class Config < Thin
|
130
|
+
include GitThinUtils
|
131
|
+
self.summary = 'Config lfs with .thinconfig'
|
132
|
+
|
133
|
+
self.arguments = [
|
134
|
+
CLAide::Argument.new('TYPE', false )
|
135
|
+
]
|
136
|
+
self.description = <<-DESC
|
137
|
+
According to .thinconfig, configure the platform directory to LFS
|
138
|
+
DESC
|
139
|
+
def self.options
|
140
|
+
[
|
141
|
+
['--source_root', 'Specify the warehouse address manually if necessary.'],
|
142
|
+
['--store', 'Store config to lfs'],
|
143
|
+
['--list', 'List the configs supported by the locally repository, (configured by.thinconfig) '],
|
144
|
+
['--current', 'Displays the current configuration'],
|
145
|
+
['--reset', 'Reset config to thin and lfs'],
|
146
|
+
['--check', 'Check the integrity of the config'],
|
147
|
+
].concat(super)
|
148
|
+
end
|
149
|
+
def initialize(argv)
|
150
|
+
super
|
151
|
+
@pwd = Dir.pwd
|
152
|
+
@types = []
|
153
|
+
@configs = {}
|
154
|
+
@source_root = argv.option('source_root')
|
155
|
+
@store = argv.flag?('store')
|
156
|
+
@list = argv.flag?('list')
|
157
|
+
@current = argv.flag?('current')
|
158
|
+
@reset = argv.flag?('reset')
|
159
|
+
@check = argv.flag?('check')
|
160
|
+
|
161
|
+
if not @source_root
|
162
|
+
run_shell 'git rev-parse --show-toplevel',false ,LOGNone do |out,err,status|
|
163
|
+
if status == 0
|
164
|
+
@source_root = out[0].strip
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
if not File.exist? @source_root+'/.git'
|
169
|
+
UI.puts "git repository not found"
|
170
|
+
exit 1
|
171
|
+
end
|
172
|
+
else
|
173
|
+
if @source_root[-1] == '/'
|
174
|
+
@source_root = @source_root[0..-2 ]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
if not Dir.exist? @source_root
|
178
|
+
@source_root = nil
|
179
|
+
return
|
180
|
+
end
|
181
|
+
if FileTest.exist? @source_root+'/.thinconfig'
|
182
|
+
run_shell "git config -lf #{@source_root}/.thinconfig",true ,true do |out,err,status|
|
183
|
+
@configs = ThinConfig.prase_config out
|
184
|
+
end
|
185
|
+
end
|
186
|
+
@types =ThinConfig.prase_type_config argv,@configs
|
187
|
+
end
|
188
|
+
|
189
|
+
def validate!
|
190
|
+
super
|
191
|
+
help! 'validate SOURCE_ROOT is required' unless @source_root
|
192
|
+
end
|
193
|
+
|
194
|
+
def git_config
|
195
|
+
if not @git_configs
|
196
|
+
run_shell 'git config -l',true ,true do |outs,errs,status|
|
197
|
+
@git_configs = ThinConfig.prase_config outs
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
def run_config
|
202
|
+
values=[]
|
203
|
+
for type in @types
|
204
|
+
config = @configs[type]
|
205
|
+
if type == ThinConfig::DEFAULT
|
206
|
+
git_config
|
207
|
+
config = @git_configs[ThinConfig::DEFAULT]
|
208
|
+
end
|
209
|
+
if config
|
210
|
+
values.push config.getValue('lfs')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
if values.length > 0
|
214
|
+
run_shell "git config thin.#{ThinConfig::DEFAULT} "+values.join(','),true ,true
|
215
|
+
if @store
|
216
|
+
run_shell "git config lfs.fetchinclude "+values.join(',')
|
217
|
+
end
|
218
|
+
end
|
219
|
+
return values
|
220
|
+
end
|
221
|
+
|
222
|
+
def list
|
223
|
+
logN 'List the configs supported by the locally repository: '
|
224
|
+
git_config
|
225
|
+
config = @git_configs[ThinConfig::DEFAULT]
|
226
|
+
if config
|
227
|
+
log = "name:#{config.name}"
|
228
|
+
for key in config.keys
|
229
|
+
log += " #{key}:#{config.getValue key}"
|
230
|
+
end
|
231
|
+
logN log
|
232
|
+
end
|
233
|
+
for config in @configs.values
|
234
|
+
log = "name:#{config.name}"
|
235
|
+
for key in config.keys
|
236
|
+
log += " #{key}:#{config.getValue key}"
|
237
|
+
end
|
238
|
+
logN log
|
239
|
+
end
|
240
|
+
end
|
241
|
+
def reset
|
242
|
+
run_shell "git config --unset thin.#{ThinConfig::DEFAULT} ",true ,true
|
243
|
+
run_shell "git config --unset lfs.fetchinclude ",true ,true
|
244
|
+
end
|
245
|
+
def current
|
246
|
+
|
247
|
+
types = ThinConfig::prase_pwd_config(@configs,@source_root,@pwd)
|
248
|
+
if types.length == 0
|
249
|
+
types.push ThinConfig::DEFAULT
|
250
|
+
end
|
251
|
+
for type in types
|
252
|
+
config = @configs[type]
|
253
|
+
if type == ThinConfig::DEFAULT
|
254
|
+
git_config
|
255
|
+
config = @git_configs[ThinConfig::DEFAULT]
|
256
|
+
end
|
257
|
+
if config
|
258
|
+
logN "name:#{type} lfs:#{config.getValue('lfs')}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def match_config(match_file)
|
264
|
+
for config in @configs.values
|
265
|
+
lfs = config.getValue 'lfs'
|
266
|
+
if lfs
|
267
|
+
lfs = lfs.map do|file|
|
268
|
+
file.strip
|
269
|
+
end
|
270
|
+
for lf in lfs
|
271
|
+
if match_file.index(lf) == 0
|
272
|
+
return config
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
return nil
|
278
|
+
end
|
279
|
+
def check
|
280
|
+
lfs_files = []
|
281
|
+
run_shell "git lfs ls-files",false ,LOGNone do |outs,errs,status|
|
282
|
+
if status
|
283
|
+
for line in outs
|
284
|
+
line[13..-2]
|
285
|
+
lfs_files.push line[13..-2]
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
unmatch_files = []
|
290
|
+
for lfs_file in lfs_files
|
291
|
+
|
292
|
+
if match_config(lfs_file).nil?
|
293
|
+
unmatch_files.push lfs_file
|
294
|
+
end
|
295
|
+
end
|
296
|
+
if unmatch_files.length > 0
|
297
|
+
logN 'The following LFS files are not configured:'
|
298
|
+
for unmatch_file in unmatch_files
|
299
|
+
logW unmatch_file
|
300
|
+
end
|
301
|
+
else
|
302
|
+
logN 'All LFS files are configured'
|
303
|
+
end
|
304
|
+
end
|
305
|
+
def run
|
306
|
+
Dir.chdir @source_root
|
307
|
+
if @list
|
308
|
+
list
|
309
|
+
elsif @reset
|
310
|
+
reset
|
311
|
+
elsif @current
|
312
|
+
current
|
313
|
+
elsif @check
|
314
|
+
check
|
315
|
+
else
|
316
|
+
values = run_config
|
317
|
+
if values.length > 0
|
318
|
+
logN "git config thin.#{ThinConfig::DEFAULT} "+values.join(",")
|
319
|
+
else
|
320
|
+
logW "not match analyze config"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
Dir.chdir @pwd
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'io/console'
|
3
|
+
module GitThinUtils
|
4
|
+
|
5
|
+
LOGC= 1<<0
|
6
|
+
LOGN= 1<<1
|
7
|
+
LOGPRUNE = 1<<2
|
8
|
+
LOGNone= 0
|
9
|
+
LOGA = LOGC|LOGN
|
10
|
+
|
11
|
+
|
12
|
+
def set_progress(index, char = '*')
|
13
|
+
print (char * (index / 2.5).floor).ljust(40, " "), " #{index}%\r"
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_shell(command,force = 0,log_type = LOGA,retryCount = 0 )
|
17
|
+
if log_type === false
|
18
|
+
log_type = LOGPRUNE
|
19
|
+
elsif log_type === true
|
20
|
+
log_type = LOGNone
|
21
|
+
end
|
22
|
+
if (log_type & LOGC ) == LOGC
|
23
|
+
logC command+"\n",(log_type & LOGPRUNE )!=LOGPRUNE
|
24
|
+
end
|
25
|
+
stdin, stdout, stderr,wait_thr = Open3.popen3(command)
|
26
|
+
pid = wait_thr[:pid]
|
27
|
+
out_lines = []
|
28
|
+
error_lines = []
|
29
|
+
stdout.sync = true
|
30
|
+
stderr.sync = true
|
31
|
+
out = Thread.new do
|
32
|
+
# while !stdout.eof?
|
33
|
+
# c = stdout.getc
|
34
|
+
# putc c
|
35
|
+
# stdout.flush
|
36
|
+
# end
|
37
|
+
stdout.each do |line|
|
38
|
+
out_lines.push line
|
39
|
+
if log_type && log_type > LOGC
|
40
|
+
logN line,(log_type & LOGPRUNE )!=LOGPRUNE
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
err = Thread.new do
|
45
|
+
# while !stderr.eof?
|
46
|
+
# c = stderr.getc
|
47
|
+
# putc c
|
48
|
+
# stderr.flush
|
49
|
+
# end
|
50
|
+
stderr.each do |line|
|
51
|
+
error_lines.push line
|
52
|
+
|
53
|
+
if log_type
|
54
|
+
logW line,(log_type & LOGPRUNE )!=LOGPRUNE
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
out.join
|
59
|
+
err.join
|
60
|
+
|
61
|
+
stderr.close
|
62
|
+
stdin.close
|
63
|
+
stdout.close
|
64
|
+
status = wait_thr.value
|
65
|
+
if status.exitstatus != 0
|
66
|
+
if !force
|
67
|
+
if retryCount > 0
|
68
|
+
sleep 1
|
69
|
+
run_shell command,force,log_type,retryCount-1
|
70
|
+
return
|
71
|
+
else
|
72
|
+
logE error_lines,(log_type & LOGPRUNE )
|
73
|
+
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
unless block_given?
|
79
|
+
|
80
|
+
return [out_lines,error_lines,status.exitstatus]
|
81
|
+
else
|
82
|
+
yield out_lines, error_lines,status.exitstatus
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def print_console(str)
|
87
|
+
run_shell "echo #{str}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def logInner(color_code,text)
|
91
|
+
clolr="\033[#{color_code}m"
|
92
|
+
nc="\033[0m"
|
93
|
+
puts "#{clolr}#{text}#{nc}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# 打印不同级别的 log。根据级别不同,样式(颜色)不同
|
97
|
+
def logE(text,note = true )
|
98
|
+
if note
|
99
|
+
logInner '31',"[ERROR] !!#{text}"
|
100
|
+
else
|
101
|
+
print(text)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def logP(text)
|
106
|
+
print("\r")
|
107
|
+
print(text)
|
108
|
+
STDOUT.flush
|
109
|
+
end
|
110
|
+
def logW(text,note = true )
|
111
|
+
if note
|
112
|
+
logInner '33',"[WARNING] #{text}"
|
113
|
+
else
|
114
|
+
print(text)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def logN(text,note = true)
|
119
|
+
if note
|
120
|
+
logInner '32',"[NOTE] #{text}"
|
121
|
+
else
|
122
|
+
print(text)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def logC(text,note = true)
|
127
|
+
if note
|
128
|
+
logInner '34',"[COMMAND] #{text}"
|
129
|
+
else
|
130
|
+
print(text)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|