esr-rim 1.1.5
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/CHANGELOG +40 -0
- data/README.md +3 -0
- data/Rakefile +56 -0
- data/bin/rim +3 -0
- data/lib/rim/command/command.rb +37 -0
- data/lib/rim/command/status.rb +110 -0
- data/lib/rim/command/sync.rb +69 -0
- data/lib/rim/command/upload.rb +33 -0
- data/lib/rim/command_helper.rb +119 -0
- data/lib/rim/dirty_check.rb +111 -0
- data/lib/rim/file_helper.rb +58 -0
- data/lib/rim/file_logger.rb +21 -0
- data/lib/rim/git.rb +339 -0
- data/lib/rim/manifest/helper.rb +82 -0
- data/lib/rim/manifest/json_reader.rb +40 -0
- data/lib/rim/manifest/manifest.json +7 -0
- data/lib/rim/manifest/model.rb +33 -0
- data/lib/rim/manifest/repo_reader.rb +61 -0
- data/lib/rim/module_helper.rb +52 -0
- data/lib/rim/module_info.rb +30 -0
- data/lib/rim/processor.rb +126 -0
- data/lib/rim/rev_status.rb +61 -0
- data/lib/rim/rim.rb +93 -0
- data/lib/rim/rim_exception.rb +15 -0
- data/lib/rim/rim_info.rb +129 -0
- data/lib/rim/status_builder.rb +219 -0
- data/lib/rim/sync_helper.rb +121 -0
- data/lib/rim/sync_module_helper.rb +115 -0
- data/lib/rim/upload_helper.rb +67 -0
- data/lib/rim/upload_module_helper.rb +152 -0
- data/lib/rim/version.rb +10 -0
- data/test/dirty_check_test.rb +210 -0
- data/test/file_helper_test.rb +132 -0
- data/test/git_test.rb +49 -0
- data/test/manifest_helper_test.rb +29 -0
- data/test/manifest_test_dir/manifest.rim +9 -0
- data/test/manifest_test_dir/subdir/only_to_keep_folder_in_git.txt +0 -0
- data/test/processor_test.rb +32 -0
- data/test/rim_info_test.rb +93 -0
- data/test/status_builder_test.rb +488 -0
- data/test/sync_helper_test.rb +193 -0
- data/test/sync_module_helper_test.rb +96 -0
- data/test/test_helper.rb +39 -0
- data/test/unit_tests.rb +14 -0
- data/test/upload_helper_test.rb +338 -0
- data/test/upload_module_helper_test.rb +92 -0
- metadata +110 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'pathname'
|
3
|
+
require 'rim/rim_info'
|
4
|
+
require 'rim/file_helper'
|
5
|
+
|
6
|
+
module RIM
|
7
|
+
|
8
|
+
# Module dirty state checker.
|
9
|
+
#
|
10
|
+
# Provides means to mark modules as "clean" and check if they become "dirty" later on.
|
11
|
+
#
|
12
|
+
# Once a module has been marked as being clean, it will become dirty if
|
13
|
+
# any of the following is true:
|
14
|
+
#
|
15
|
+
# * Number of contained files has changed
|
16
|
+
# * File names or location have changed
|
17
|
+
# * File contents have changed
|
18
|
+
# * One of the RIM info attributes listed in ChecksumAttributes has changed
|
19
|
+
# * The RIM info file is missing or became invalid
|
20
|
+
#
|
21
|
+
# Ignored files are not considered by this check.
|
22
|
+
#
|
23
|
+
class DirtyCheck
|
24
|
+
|
25
|
+
# raised when there is not enough info for checksum calculation
|
26
|
+
class MissingInfoException < Exception
|
27
|
+
end
|
28
|
+
|
29
|
+
# attributes to be included into checksum calculation
|
30
|
+
# checksum calculation fails if those attributes are not present
|
31
|
+
ChecksumAttributes = [
|
32
|
+
:remote_url,
|
33
|
+
:revision_sha1
|
34
|
+
]
|
35
|
+
|
36
|
+
# rim info must exist in dir and must be valid
|
37
|
+
# it also must contain attributes listed in ChecksumAttributes
|
38
|
+
# otherwise a MissingInfoException is raised
|
39
|
+
def self.mark_clean(dir)
|
40
|
+
mi = RimInfo.from_dir(dir)
|
41
|
+
cs = self.new.calc_checksum(mi, dir)
|
42
|
+
raise MissingInfoException unless cs
|
43
|
+
mi.checksum = cs
|
44
|
+
mi.to_dir(dir)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.dirty?(dir)
|
48
|
+
mi = RimInfo.from_dir(dir)
|
49
|
+
# always fails if there is no checksum
|
50
|
+
!mi.checksum || mi.checksum != self.new.calc_checksum(mi, dir)
|
51
|
+
end
|
52
|
+
|
53
|
+
# returns nil if checksum can't be calculated due to missing info
|
54
|
+
def calc_checksum(mi, dir)
|
55
|
+
if check_required_attributes(mi)
|
56
|
+
sha1 = Digest::SHA1.new
|
57
|
+
# all files and directories within dir
|
58
|
+
files = FileHelper.find_matching_files(dir, false, "/**/*", File::FNM_DOTMATCH)
|
59
|
+
# Dir.glob with FNM_DOTMATCH might return . and ..
|
60
|
+
files.delete(".")
|
61
|
+
files.delete("..")
|
62
|
+
# ignore the info file itself
|
63
|
+
files.delete(RimInfo::InfoFileName)
|
64
|
+
# ignores defined by user
|
65
|
+
files -= FileHelper.find_matching_files(dir, false, mi.ignores)
|
66
|
+
# order of files makes a difference
|
67
|
+
# sort to eliminate platform specific glob behavior
|
68
|
+
files.sort!
|
69
|
+
files.each do |fn|
|
70
|
+
update_file(sha1, dir, fn)
|
71
|
+
end
|
72
|
+
ChecksumAttributes.each do |a|
|
73
|
+
sha1.update(mi.send(a))
|
74
|
+
end
|
75
|
+
sha1.hexdigest
|
76
|
+
else
|
77
|
+
# can't calc checksum
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def ignored_files(mi, dir)
|
85
|
+
find_matching_files(dir, mi.ignores)
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_file(sha1, dir, filename)
|
89
|
+
fn = dir+"/"+filename
|
90
|
+
if File.directory?(fn)
|
91
|
+
if Dir.entries(fn).size > 2 # 2 for . and ..
|
92
|
+
# add directory names but only for non-empty directories
|
93
|
+
sha1.update(filename)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
# file name
|
97
|
+
sha1.update(filename)
|
98
|
+
# file contents
|
99
|
+
File.open(fn, "rb") do |f|
|
100
|
+
sha1.update(f.read.gsub("\r\n", "\n"))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def check_required_attributes(mi)
|
106
|
+
ChecksumAttributes.all?{|a| mi.send(a)}
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module RIM
|
5
|
+
|
6
|
+
class FileHelper
|
7
|
+
|
8
|
+
def self.get_relative_path(path, base)
|
9
|
+
Pathname.new(get_absolute_path(path)).relative_path_from(Pathname.new(base)).to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get_absolute_path(path)
|
13
|
+
File.expand_path(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find_matching_files(dir, absolute = true, patterns = "**/*", flags = 0)
|
17
|
+
files = []
|
18
|
+
dirpath = Pathname.new(dir)
|
19
|
+
normalize_patterns(patterns).each do |i|
|
20
|
+
Dir.glob(File.join(dir, i), flags) do |f|
|
21
|
+
if absolute
|
22
|
+
files.push(f)
|
23
|
+
else
|
24
|
+
files.push(Pathname.new(f).relative_path_from(dirpath).to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
files.sort.uniq
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.remove_empty_dirs(dir, exclude = ".")
|
32
|
+
exclude = File.join(File.expand_path(exclude), "") if exclude
|
33
|
+
Dir.glob(File.join(dir, "/*/**/")).reverse_each do |d|
|
34
|
+
Dir.rmdir d if Dir.entries(d).size == 2 && (!exclude || !exclude.start_with?(d))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.make_empty_dir(dir)
|
39
|
+
FileUtils.rm_rf dir
|
40
|
+
FileUtils.mkdir_p(dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.normalize_patterns(patterns = [])
|
46
|
+
if patterns.is_a?(String)
|
47
|
+
return patterns.split(",").each do |p|
|
48
|
+
p.strip!
|
49
|
+
end
|
50
|
+
elsif !patterns
|
51
|
+
patterns = []
|
52
|
+
end
|
53
|
+
patterns
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module RIM
|
4
|
+
|
5
|
+
class FileLogger < Logger
|
6
|
+
|
7
|
+
def initialize(name, file)
|
8
|
+
super(name)
|
9
|
+
FileUtils.mkdir_p(File.dirname(file))
|
10
|
+
@file_logger = Logger.new(file)
|
11
|
+
@file_logger.level = Logger::DEBUG
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(severity, message = nil, progname = nil, &block)
|
15
|
+
@file_logger.add(severity, message, progname)
|
16
|
+
super(severity, message, progname)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/rim/git.rb
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module RIM
|
5
|
+
|
6
|
+
# raised when there is an error emitted by git call
|
7
|
+
class GitException < Exception
|
8
|
+
attr_reader :cmd, :exitstatus, :out
|
9
|
+
def initialize(cmd, exitstatus, out)
|
10
|
+
super("git \"#{cmd}\" => #{exitstatus}\n#{out}")
|
11
|
+
@cmd = cmd
|
12
|
+
@exitstatus = exitstatus
|
13
|
+
@out = out
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class GitSession
|
18
|
+
|
19
|
+
attr_reader :execute_dir
|
20
|
+
|
21
|
+
def initialize(logger, execute_dir, arg = {})
|
22
|
+
@execute_dir = execute_dir
|
23
|
+
if arg.is_a?(Hash)
|
24
|
+
@work_dir = arg.has_key?(:work_dir) ? arg[:work_dir] : ""
|
25
|
+
@git_dir = arg.has_key?(:git_dir) ? arg[:git_dir] : ""
|
26
|
+
end
|
27
|
+
@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.logger=(logger)
|
31
|
+
@logger = logger
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.open(execute_dir, options = {})
|
35
|
+
log = @logger || Logger.new($stdout)
|
36
|
+
self.new(log, execute_dir, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.next_invocation_id
|
40
|
+
@invocation_id ||= 0
|
41
|
+
@invocation_id += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
class Status
|
45
|
+
attr_accessor :lines
|
46
|
+
|
47
|
+
# X Y Meaning
|
48
|
+
# -------------------------------------------------
|
49
|
+
# [MD] not updated
|
50
|
+
# M [ MD] updated in index
|
51
|
+
# A [ MD] added to index
|
52
|
+
# D [ M] deleted from index
|
53
|
+
# R [ MD] renamed in index
|
54
|
+
# C [ MD] copied in index
|
55
|
+
# [MARC] index and work tree matches
|
56
|
+
# [ MARC] M work tree changed since index
|
57
|
+
# [ MARC] D deleted in work tree
|
58
|
+
# -------------------------------------------------
|
59
|
+
# D D unmerged, both deleted
|
60
|
+
# A U unmerged, added by us
|
61
|
+
# U D unmerged, deleted by them
|
62
|
+
# U A unmerged, added by them
|
63
|
+
# D U unmerged, deleted by us
|
64
|
+
# A A unmerged, both added
|
65
|
+
# U U unmerged, both modified
|
66
|
+
# -------------------------------------------------
|
67
|
+
# ? ? untracked
|
68
|
+
# ! ! ignored
|
69
|
+
# -------------------------------------------------
|
70
|
+
class Line
|
71
|
+
attr_accessor :istat, :wstat, :file, :rename
|
72
|
+
|
73
|
+
def untracked?
|
74
|
+
istat == "?" && wstat == "?"
|
75
|
+
end
|
76
|
+
|
77
|
+
def ignored?
|
78
|
+
istat == "!" && wstat == "!"
|
79
|
+
end
|
80
|
+
|
81
|
+
def unmerged?
|
82
|
+
istat == "D" && wstat == "D" ||
|
83
|
+
istat == "A" && wstat == "A" ||
|
84
|
+
istat == "U" ||
|
85
|
+
wstat == "U"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# returns the current branch
|
92
|
+
def current_branch
|
93
|
+
out = execute "git branch"
|
94
|
+
out.split("\n").each do |l|
|
95
|
+
if l =~ /^\*\s+(\S+)/
|
96
|
+
return $1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# check whether branch exists
|
103
|
+
def has_branch?(branch)
|
104
|
+
execute("git show-ref refs/heads/#{branch}") do |b, e|
|
105
|
+
return !e
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# check whether remote branch exists
|
110
|
+
def has_remote_branch?(branch)
|
111
|
+
out = execute("git ls-remote --heads")
|
112
|
+
out.split("\n").each do |l|
|
113
|
+
return true if l.split(/\s+/)[1] == "refs/heads/#{branch}"
|
114
|
+
end
|
115
|
+
false
|
116
|
+
end
|
117
|
+
|
118
|
+
# check whether remote repository is valid
|
119
|
+
def has_valid_remote_repository?()
|
120
|
+
execute("git ls-remote") do |b, e|
|
121
|
+
return !e
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# checks whether the first (ancestor) revision is is ancestor of the second (child) revision
|
126
|
+
def is_ancestor?(ancestor, child)
|
127
|
+
execute("git merge-base --is-ancestor #{ancestor} #{child}") do |b, e|
|
128
|
+
return !e
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# returns the parent commits of rev as SHA-1s
|
133
|
+
# returns an empty array if there are no parents (e.g. orphan or initial)
|
134
|
+
def parent_revs(rev)
|
135
|
+
out = execute "git rev-list -n 1 --parents #{rev} --"
|
136
|
+
out.strip.split[1..-1]
|
137
|
+
end
|
138
|
+
|
139
|
+
# returns the SHA-1 representation of rev
|
140
|
+
def rev_sha1(rev)
|
141
|
+
sha1 = nil
|
142
|
+
execute "git rev-list -n 1 #{rev} --" do |out, e|
|
143
|
+
sha1 = out.strip if !e
|
144
|
+
end
|
145
|
+
sha1
|
146
|
+
end
|
147
|
+
|
148
|
+
# returns the SHA-1 representations of the heads of all remote branches
|
149
|
+
def remote_branch_revs
|
150
|
+
out = execute "git show-ref"
|
151
|
+
out.split("\n").collect { |l|
|
152
|
+
if l =~ /refs\/remotes\//
|
153
|
+
l.split[0]
|
154
|
+
else
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
}.compact
|
158
|
+
end
|
159
|
+
|
160
|
+
# all commits reachable from rev which are not ancestors of remote branches
|
161
|
+
def all_reachable_non_remote_revs(rev)
|
162
|
+
out = execute "git rev-list #{rev} --not --remotes --"
|
163
|
+
out.split("\n")
|
164
|
+
end
|
165
|
+
|
166
|
+
# export file contents of rev to dir
|
167
|
+
# if +paths+ is given and non-empty, checks out only those parts of the filesystem tree
|
168
|
+
# does not remove any files from dir which existed before
|
169
|
+
def export_rev(rev, dir, paths=[])
|
170
|
+
paths = paths.dup
|
171
|
+
loop do
|
172
|
+
path_args = ""
|
173
|
+
# max command line length on Windows XP and higher is 8191
|
174
|
+
# consider the following extra characters which will be added:
|
175
|
+
# up to 3 paths in execute, 1 path for tar, max path length 260 = 1040
|
176
|
+
# plus some "glue" characters, plus the last path item with 260 max;
|
177
|
+
# use 6000 to be on the safe side
|
178
|
+
while !paths.empty? && path_args.size < 6000
|
179
|
+
path_args << " "
|
180
|
+
path_args << paths.shift
|
181
|
+
end
|
182
|
+
execute "git archive --format tar #{rev} #{path_args} | tar -x -C #{dir}"
|
183
|
+
break if paths.empty?
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# checks out rev to a temporary directory and yields this directory to the given block
|
188
|
+
# if +paths+ is given and non-empty, checks out only those parts of the filesystem tree
|
189
|
+
# returns the value returned by the block
|
190
|
+
def within_exported_rev(rev, paths=[])
|
191
|
+
Dir.mktmpdir("rim") do |d|
|
192
|
+
c = File.join(d, "content")
|
193
|
+
FileUtils.mkdir(c)
|
194
|
+
export_rev(rev, c, paths)
|
195
|
+
# return contents of yielded block
|
196
|
+
# mktmpdir returns value return by our block
|
197
|
+
yield c
|
198
|
+
FileUtils.rm_rf(c)
|
199
|
+
# retry to delete if it hasn't been deleted yet
|
200
|
+
# this could be due to Windows keeping the files locked for some time
|
201
|
+
# this is especially a problem if the machine is at its limits
|
202
|
+
retries = 600
|
203
|
+
while File.exist?(c) && retries > 0
|
204
|
+
sleep(0.1)
|
205
|
+
FileUtils.rm_rf(c)
|
206
|
+
retries -= 1
|
207
|
+
end
|
208
|
+
if File.exist?(c)
|
209
|
+
@logger.warn "could not delete temp dir: #{c}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def uncommited_changes?
|
215
|
+
# either no status lines are all of them due to ignored items
|
216
|
+
!status.lines.all?{|l| l.ignored?}
|
217
|
+
end
|
218
|
+
|
219
|
+
def current_branch_name
|
220
|
+
out = execute "git rev-parse --abbrev-ref HEAD"
|
221
|
+
out.strip
|
222
|
+
end
|
223
|
+
|
224
|
+
ChangedFile = Struct.new(:path, :kind)
|
225
|
+
|
226
|
+
# returns a list of all files which changed in commit +rev+
|
227
|
+
# together with the kind of the change (:modified, :deleted, :added)
|
228
|
+
#
|
229
|
+
# if +from_rev+ is given, lists changes between +from_rev and +rev+
|
230
|
+
# with one argument only, no changes will be returned for merge commits
|
231
|
+
# use the two argument variant for merge commits and decide for one parent
|
232
|
+
def changed_files(rev, rev_from=nil)
|
233
|
+
out = execute "git diff-tree -r --no-commit-id #{rev_from} #{rev}"
|
234
|
+
out.split("\n").collect do |l|
|
235
|
+
cols = l.split
|
236
|
+
path = cols[5]
|
237
|
+
kind = case cols[4]
|
238
|
+
when "M"
|
239
|
+
:modified
|
240
|
+
when "A"
|
241
|
+
:added
|
242
|
+
when "D"
|
243
|
+
:deleted
|
244
|
+
else
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
ChangedFile.new(path, kind)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# 3 most significant numbers of git version of nil if it can't be determined
|
252
|
+
def git_version
|
253
|
+
out = execute("git --version")
|
254
|
+
if out =~ /^git version (\d+\.\d+\.\d+)/
|
255
|
+
$1
|
256
|
+
else
|
257
|
+
nil
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def status(dir = nil)
|
262
|
+
# -s short format
|
263
|
+
# --ignored show ignored
|
264
|
+
out = execute "git status -s --ignored #{dir}"
|
265
|
+
parse_status(out)
|
266
|
+
end
|
267
|
+
|
268
|
+
def execute(cmd)
|
269
|
+
raise "git command has to start with 'git'" unless cmd.start_with? "git "
|
270
|
+
cmd.slice!("git ")
|
271
|
+
# remove any newlines as they will cause the command line to end prematurely
|
272
|
+
cmd.gsub!("\n", "")
|
273
|
+
options = ((!@execute_dir || @execute_dir == ".") ? "" : " -C #{@execute_dir}") \
|
274
|
+
+ (@work_dir.empty? ? "" : " --work-tree=#{File.expand_path(@work_dir)}") \
|
275
|
+
+ (@git_dir.empty? ? "" : " --git-dir=#{File.expand_path(@git_dir)}")
|
276
|
+
cmd = "git#{options} #{cmd} 2>&1"
|
277
|
+
|
278
|
+
out = `#{cmd}`
|
279
|
+
# make sure we don't run into any encoding misinterpretation issues
|
280
|
+
out.force_encoding("binary")
|
281
|
+
|
282
|
+
exitstatus = $?.exitstatus
|
283
|
+
|
284
|
+
invid = self.class.next_invocation_id.to_s.ljust(4)
|
285
|
+
@logger.debug "git##{invid} \"#{cmd}\" => #{exitstatus}"
|
286
|
+
|
287
|
+
out.split(/\r?\n/).each do |ol|
|
288
|
+
@logger.debug "git##{invid} out : #{ol}"
|
289
|
+
end
|
290
|
+
|
291
|
+
exception = exitstatus != 0 ? GitException.new(cmd, exitstatus, out) : nil
|
292
|
+
|
293
|
+
if block_given?
|
294
|
+
yield out, exception
|
295
|
+
elsif exception
|
296
|
+
raise exception
|
297
|
+
end
|
298
|
+
|
299
|
+
out
|
300
|
+
end
|
301
|
+
|
302
|
+
private
|
303
|
+
|
304
|
+
def parse_status(out)
|
305
|
+
status = Status.new
|
306
|
+
status.lines = []
|
307
|
+
out.split(/\r?\n/).each do |l|
|
308
|
+
sl = Status::Line.new
|
309
|
+
sl.istat, sl.wstat = l[0], l[1]
|
310
|
+
f1, f2 = l[3..-1].split(" -> ")
|
311
|
+
f1 = unquote(f1)
|
312
|
+
f2 = unquote(f2) if f2
|
313
|
+
sl.file = f1
|
314
|
+
sl.rename = f2
|
315
|
+
status.lines << sl
|
316
|
+
end
|
317
|
+
status
|
318
|
+
end
|
319
|
+
|
320
|
+
def unquote(s)
|
321
|
+
if s[0] == "\"" && s[-1] == "\""
|
322
|
+
s = s[1..-2]
|
323
|
+
s.gsub!("\\\\", "\\")
|
324
|
+
s.gsub!("\\\"", "\"")
|
325
|
+
s.gsub!("\\t", "\t")
|
326
|
+
s.gsub!("\\r", "\r")
|
327
|
+
s.gsub!("\\n", "\n")
|
328
|
+
end
|
329
|
+
s
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
def RIM.git_session(execute_dir, options = {})
|
335
|
+
s = GitSession.open(execute_dir, options)
|
336
|
+
yield s
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|