jit 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE.txt +674 -0
- data/bin/jit +21 -0
- data/lib/color.rb +32 -0
- data/lib/command.rb +62 -0
- data/lib/command/add.rb +65 -0
- data/lib/command/base.rb +92 -0
- data/lib/command/branch.rb +199 -0
- data/lib/command/checkout.rb +104 -0
- data/lib/command/cherry_pick.rb +51 -0
- data/lib/command/commit.rb +86 -0
- data/lib/command/config.rb +126 -0
- data/lib/command/diff.rb +114 -0
- data/lib/command/fetch.rb +116 -0
- data/lib/command/init.rb +41 -0
- data/lib/command/log.rb +188 -0
- data/lib/command/merge.rb +148 -0
- data/lib/command/push.rb +172 -0
- data/lib/command/receive_pack.rb +92 -0
- data/lib/command/remote.rb +55 -0
- data/lib/command/reset.rb +64 -0
- data/lib/command/rev_list.rb +33 -0
- data/lib/command/revert.rb +69 -0
- data/lib/command/rm.rb +105 -0
- data/lib/command/shared/fast_forward.rb +19 -0
- data/lib/command/shared/print_diff.rb +116 -0
- data/lib/command/shared/receive_objects.rb +37 -0
- data/lib/command/shared/remote_agent.rb +44 -0
- data/lib/command/shared/remote_client.rb +82 -0
- data/lib/command/shared/send_objects.rb +24 -0
- data/lib/command/shared/sequencing.rb +146 -0
- data/lib/command/shared/write_commit.rb +167 -0
- data/lib/command/status.rb +210 -0
- data/lib/command/upload_pack.rb +54 -0
- data/lib/config.rb +240 -0
- data/lib/config/stack.rb +42 -0
- data/lib/database.rb +112 -0
- data/lib/database/author.rb +27 -0
- data/lib/database/backends.rb +57 -0
- data/lib/database/blob.rb +24 -0
- data/lib/database/commit.rb +70 -0
- data/lib/database/entry.rb +7 -0
- data/lib/database/loose.rb +70 -0
- data/lib/database/packed.rb +75 -0
- data/lib/database/tree.rb +77 -0
- data/lib/database/tree_diff.rb +88 -0
- data/lib/diff.rb +46 -0
- data/lib/diff/combined.rb +72 -0
- data/lib/diff/hunk.rb +64 -0
- data/lib/diff/myers.rb +90 -0
- data/lib/editor.rb +59 -0
- data/lib/index.rb +212 -0
- data/lib/index/checksum.rb +44 -0
- data/lib/index/entry.rb +91 -0
- data/lib/lockfile.rb +55 -0
- data/lib/merge/bases.rb +38 -0
- data/lib/merge/common_ancestors.rb +77 -0
- data/lib/merge/diff3.rb +156 -0
- data/lib/merge/inputs.rb +42 -0
- data/lib/merge/resolve.rb +178 -0
- data/lib/pack.rb +45 -0
- data/lib/pack/compressor.rb +83 -0
- data/lib/pack/delta.rb +58 -0
- data/lib/pack/entry.rb +54 -0
- data/lib/pack/expander.rb +54 -0
- data/lib/pack/index.rb +100 -0
- data/lib/pack/indexer.rb +200 -0
- data/lib/pack/numbers.rb +79 -0
- data/lib/pack/reader.rb +98 -0
- data/lib/pack/stream.rb +80 -0
- data/lib/pack/unpacker.rb +62 -0
- data/lib/pack/window.rb +47 -0
- data/lib/pack/writer.rb +92 -0
- data/lib/pack/xdelta.rb +118 -0
- data/lib/pager.rb +24 -0
- data/lib/progress.rb +78 -0
- data/lib/refs.rb +260 -0
- data/lib/remotes.rb +82 -0
- data/lib/remotes/protocol.rb +82 -0
- data/lib/remotes/refspec.rb +70 -0
- data/lib/remotes/remote.rb +57 -0
- data/lib/repository.rb +64 -0
- data/lib/repository/divergence.rb +21 -0
- data/lib/repository/hard_reset.rb +35 -0
- data/lib/repository/inspector.rb +49 -0
- data/lib/repository/migration.rb +168 -0
- data/lib/repository/pending_commit.rb +60 -0
- data/lib/repository/sequencer.rb +118 -0
- data/lib/repository/status.rb +98 -0
- data/lib/rev_list.rb +244 -0
- data/lib/revision.rb +155 -0
- data/lib/sorted_hash.rb +17 -0
- data/lib/temp_file.rb +34 -0
- data/lib/workspace.rb +107 -0
- metadata +103 -9
data/lib/pack/xdelta.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Pack
|
2
|
+
class XDelta
|
3
|
+
|
4
|
+
BLOCK_SIZE = 16
|
5
|
+
|
6
|
+
def self.create_index(source)
|
7
|
+
blocks = source.bytesize / BLOCK_SIZE
|
8
|
+
index = {}
|
9
|
+
|
10
|
+
(0 ... blocks).each do |i|
|
11
|
+
offset = i * BLOCK_SIZE
|
12
|
+
slice = source.byteslice(offset, BLOCK_SIZE)
|
13
|
+
|
14
|
+
index[slice] ||= []
|
15
|
+
index[slice].push(offset)
|
16
|
+
end
|
17
|
+
|
18
|
+
new(source, index)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(source, index)
|
22
|
+
@source = source
|
23
|
+
@index = index
|
24
|
+
end
|
25
|
+
|
26
|
+
def compress(target)
|
27
|
+
@target = target
|
28
|
+
@offset = 0
|
29
|
+
@insert = []
|
30
|
+
@ops = []
|
31
|
+
|
32
|
+
generate_ops while @offset < @target.bytesize
|
33
|
+
flush_insert
|
34
|
+
|
35
|
+
@ops
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def generate_ops
|
41
|
+
m_offset, m_size = longest_match
|
42
|
+
return push_insert if m_size == 0
|
43
|
+
|
44
|
+
m_offset, m_size = expand_match(m_offset, m_size)
|
45
|
+
|
46
|
+
flush_insert
|
47
|
+
@ops.push(Delta::Copy.new(m_offset, m_size))
|
48
|
+
end
|
49
|
+
|
50
|
+
def longest_match
|
51
|
+
slice = @target.byteslice(@offset, BLOCK_SIZE)
|
52
|
+
return [0, 0] unless @index.has_key?(slice)
|
53
|
+
|
54
|
+
m_offset = m_size = 0
|
55
|
+
|
56
|
+
@index[slice].each do |pos|
|
57
|
+
remaining = remaining_bytes(pos)
|
58
|
+
break if remaining <= m_size
|
59
|
+
|
60
|
+
s = match_from(pos, remaining)
|
61
|
+
next if m_size >= s - pos
|
62
|
+
|
63
|
+
m_offset = pos
|
64
|
+
m_size = s - pos
|
65
|
+
end
|
66
|
+
|
67
|
+
[m_offset, m_size]
|
68
|
+
end
|
69
|
+
|
70
|
+
def remaining_bytes(pos)
|
71
|
+
source_remaining = @source.bytesize - pos
|
72
|
+
target_remaining = @target.bytesize - @offset
|
73
|
+
|
74
|
+
[source_remaining, target_remaining, MAX_COPY_SIZE].min
|
75
|
+
end
|
76
|
+
|
77
|
+
def match_from(pos, remaining)
|
78
|
+
s, t = pos, @offset
|
79
|
+
|
80
|
+
while remaining > 0 and @source.getbyte(s) == @target.getbyte(t)
|
81
|
+
s, t = s + 1, t + 1
|
82
|
+
remaining -= 1
|
83
|
+
end
|
84
|
+
|
85
|
+
s
|
86
|
+
end
|
87
|
+
|
88
|
+
def expand_match(m_offset, m_size)
|
89
|
+
while m_offset > 0 and @source.getbyte(m_offset - 1) == @insert.last
|
90
|
+
break if m_size == MAX_COPY_SIZE
|
91
|
+
|
92
|
+
@offset -= 1
|
93
|
+
m_offset -= 1
|
94
|
+
m_size += 1
|
95
|
+
|
96
|
+
@insert.pop
|
97
|
+
end
|
98
|
+
|
99
|
+
@offset += m_size
|
100
|
+
[m_offset, m_size]
|
101
|
+
end
|
102
|
+
|
103
|
+
def push_insert
|
104
|
+
@insert.push(@target.getbyte(@offset))
|
105
|
+
@offset += 1
|
106
|
+
flush_insert(MAX_INSERT_SIZE)
|
107
|
+
end
|
108
|
+
|
109
|
+
def flush_insert(size = nil)
|
110
|
+
return if size and @insert.size < size
|
111
|
+
return if @insert.empty?
|
112
|
+
|
113
|
+
@ops.push(Delta::Insert.new(@insert.pack("C*")))
|
114
|
+
@insert = []
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
data/lib/pager.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class Pager
|
2
|
+
PAGER_CMD = "less"
|
3
|
+
PAGER_ENV = { "LESS" => "FRX", "LV" => "-c" }
|
4
|
+
|
5
|
+
attr_reader :input
|
6
|
+
|
7
|
+
def initialize(env = {}, stdout = $stdout, stderr = $stderr)
|
8
|
+
env = PAGER_ENV.merge(env)
|
9
|
+
cmd = env["GIT_PAGER"] || env["PAGER"] || PAGER_CMD
|
10
|
+
|
11
|
+
reader, writer = IO.pipe
|
12
|
+
options = { :in => reader, :out => stdout, :err => stderr }
|
13
|
+
|
14
|
+
@pid = Process.spawn(env, cmd, options)
|
15
|
+
@input = writer
|
16
|
+
|
17
|
+
reader.close
|
18
|
+
end
|
19
|
+
|
20
|
+
def wait
|
21
|
+
Process.waitpid(@pid) if @pid
|
22
|
+
@pid = nil
|
23
|
+
end
|
24
|
+
end
|
data/lib/progress.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class Progress
|
2
|
+
UNITS = ["B", "KiB", "MiB", "GiB"]
|
3
|
+
SCALE = 1024.0
|
4
|
+
|
5
|
+
def initialize(output)
|
6
|
+
@output = output
|
7
|
+
@message = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def start(message, total = nil)
|
11
|
+
return if ENV["NO_PROGRESS"] or not @output.isatty
|
12
|
+
|
13
|
+
@message = message
|
14
|
+
@total = total
|
15
|
+
@count = 0
|
16
|
+
@bytes = 0
|
17
|
+
@write_at = get_time
|
18
|
+
end
|
19
|
+
|
20
|
+
def tick(bytes = 0)
|
21
|
+
return unless @message
|
22
|
+
|
23
|
+
@count += 1
|
24
|
+
@bytes = bytes
|
25
|
+
|
26
|
+
current_time = get_time
|
27
|
+
return if current_time < @write_at + 0.05
|
28
|
+
@write_at = current_time
|
29
|
+
|
30
|
+
clear_line
|
31
|
+
@output.write(status_line)
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
return unless @message
|
36
|
+
|
37
|
+
@total = @count
|
38
|
+
|
39
|
+
clear_line
|
40
|
+
@output.puts(status_line)
|
41
|
+
@message = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def get_time
|
47
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
48
|
+
end
|
49
|
+
|
50
|
+
def clear_line
|
51
|
+
@output.write("\e[G\e[K")
|
52
|
+
end
|
53
|
+
|
54
|
+
def status_line
|
55
|
+
line = "#{ @message }: #{ format_count }"
|
56
|
+
|
57
|
+
line.concat(", #{ format_bytes }") if @bytes > 0
|
58
|
+
line.concat(", done.") if @count == @total
|
59
|
+
|
60
|
+
line
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_count
|
64
|
+
if @total
|
65
|
+
percent = (@total == 0) ? 100 : 100 * @count / @total
|
66
|
+
"#{ percent }% (#{ @count }/#{ @total })"
|
67
|
+
else
|
68
|
+
"(#{ @count })"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_bytes
|
73
|
+
power = Math.log(@bytes, SCALE).floor
|
74
|
+
scaled = @bytes / (SCALE ** power)
|
75
|
+
|
76
|
+
format("%.2f #{ UNITS[power] }", scaled)
|
77
|
+
end
|
78
|
+
end
|
data/lib/refs.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
require_relative "./lockfile"
|
5
|
+
require_relative "./revision"
|
6
|
+
|
7
|
+
class Refs
|
8
|
+
InvalidBranch = Class.new(StandardError)
|
9
|
+
StaleValue = Class.new(StandardError)
|
10
|
+
|
11
|
+
SymRef = Struct.new(:refs, :path) do
|
12
|
+
def read_oid
|
13
|
+
refs.read_ref(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def head?
|
17
|
+
path == HEAD
|
18
|
+
end
|
19
|
+
|
20
|
+
def branch?
|
21
|
+
path.start_with?("refs/heads/")
|
22
|
+
end
|
23
|
+
|
24
|
+
def remote?
|
25
|
+
path.start_with?("refs/remotes/")
|
26
|
+
end
|
27
|
+
|
28
|
+
def short_name
|
29
|
+
refs.short_name(path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Ref = Struct.new(:oid) do
|
34
|
+
def read_oid
|
35
|
+
oid
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
HEAD = "HEAD"
|
40
|
+
ORIG_HEAD = "ORIG_HEAD"
|
41
|
+
SYMREF = /^ref: (.+)$/
|
42
|
+
|
43
|
+
REFS_DIR = Pathname.new("refs")
|
44
|
+
HEADS_DIR = REFS_DIR.join("heads")
|
45
|
+
REMOTES_DIR = REFS_DIR.join("remotes")
|
46
|
+
|
47
|
+
def initialize(pathname)
|
48
|
+
@pathname = pathname
|
49
|
+
@refs_path = @pathname.join(REFS_DIR)
|
50
|
+
@heads_path = @pathname.join(HEADS_DIR)
|
51
|
+
@remotes_path = @pathname.join(REMOTES_DIR)
|
52
|
+
end
|
53
|
+
|
54
|
+
def read_head
|
55
|
+
read_symref(@pathname.join(HEAD))
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_head(oid)
|
59
|
+
update_symref(@pathname.join(HEAD), oid)
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_head(revision, oid)
|
63
|
+
head = @pathname.join(HEAD)
|
64
|
+
path = @heads_path.join(revision)
|
65
|
+
|
66
|
+
if File.file?(path)
|
67
|
+
relative = path.relative_path_from(@pathname)
|
68
|
+
update_ref_file(head, "ref: #{ relative }")
|
69
|
+
else
|
70
|
+
update_ref_file(head, oid)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def read_ref(name)
|
75
|
+
path = path_for_name(name)
|
76
|
+
path ? read_symref(path) : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_ref(name, oid)
|
80
|
+
update_ref_file(@pathname.join(name), oid)
|
81
|
+
end
|
82
|
+
|
83
|
+
def compare_and_swap(name, old_oid, new_oid)
|
84
|
+
path = @pathname.join(name)
|
85
|
+
|
86
|
+
update_ref_file(path, new_oid) do
|
87
|
+
unless old_oid == read_symref(path)
|
88
|
+
raise StaleValue, "value of #{ name } changed since last read"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_branch(branch_name, start_oid)
|
94
|
+
path = @heads_path.join(branch_name)
|
95
|
+
|
96
|
+
unless Revision.valid_ref?(branch_name)
|
97
|
+
raise InvalidBranch, "'#{ branch_name }' is not a valid branch name."
|
98
|
+
end
|
99
|
+
|
100
|
+
if File.file?(path)
|
101
|
+
raise InvalidBranch, "A branch named '#{ branch_name }' already exists."
|
102
|
+
end
|
103
|
+
|
104
|
+
FileUtils.mkdir_p(path.dirname)
|
105
|
+
update_ref_file(path, start_oid)
|
106
|
+
end
|
107
|
+
|
108
|
+
def delete_branch(branch_name)
|
109
|
+
path = @heads_path.join(branch_name)
|
110
|
+
|
111
|
+
lockfile = Lockfile.new(path)
|
112
|
+
lockfile.hold_for_update
|
113
|
+
|
114
|
+
oid = read_symref(path)
|
115
|
+
raise InvalidBranch, "branch '#{ branch_name }' not found." unless oid
|
116
|
+
|
117
|
+
File.unlink(path)
|
118
|
+
oid
|
119
|
+
ensure
|
120
|
+
lockfile.rollback
|
121
|
+
end
|
122
|
+
|
123
|
+
def current_ref(source = HEAD)
|
124
|
+
ref = read_oid_or_symref(@pathname.join(source))
|
125
|
+
|
126
|
+
case ref
|
127
|
+
when SymRef then current_ref(ref.path)
|
128
|
+
when Ref, nil then SymRef.new(self, source)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def list_all_refs
|
133
|
+
[SymRef.new(self, HEAD)] + list_refs(@refs_path)
|
134
|
+
end
|
135
|
+
|
136
|
+
def list_branches
|
137
|
+
list_refs(@heads_path)
|
138
|
+
end
|
139
|
+
|
140
|
+
def list_remotes
|
141
|
+
list_refs(@remotes_path)
|
142
|
+
end
|
143
|
+
|
144
|
+
def reverse_refs
|
145
|
+
table = Hash.new { |hash, key| hash[key] = [] }
|
146
|
+
|
147
|
+
list_all_refs.each do |ref|
|
148
|
+
oid = ref.read_oid
|
149
|
+
table[oid].push(ref) if oid
|
150
|
+
end
|
151
|
+
|
152
|
+
table
|
153
|
+
end
|
154
|
+
|
155
|
+
def short_name(path)
|
156
|
+
path = @pathname.join(path)
|
157
|
+
|
158
|
+
prefix = [@remotes_path, @heads_path, @pathname].find do |dir|
|
159
|
+
path.dirname.ascend.any? { |parent| parent == dir }
|
160
|
+
end
|
161
|
+
|
162
|
+
path.relative_path_from(prefix).to_s
|
163
|
+
end
|
164
|
+
|
165
|
+
def long_name(ref)
|
166
|
+
path = path_for_name(ref)
|
167
|
+
return path.relative_path_from(@pathname).to_s if path
|
168
|
+
|
169
|
+
raise InvalidBranch,
|
170
|
+
"the requested upstream branch '#{ ref }' does not exist"
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def list_refs(dirname)
|
176
|
+
names = Dir.entries(dirname) - [".", ".."]
|
177
|
+
|
178
|
+
names.map { |name| dirname.join(name) }.flat_map do |path|
|
179
|
+
if File.directory?(path)
|
180
|
+
list_refs(path)
|
181
|
+
else
|
182
|
+
path = path.relative_path_from(@pathname)
|
183
|
+
SymRef.new(self, path.to_s)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
rescue Errno::ENOENT
|
188
|
+
[]
|
189
|
+
end
|
190
|
+
|
191
|
+
def path_for_name(name)
|
192
|
+
prefixes = [@pathname, @refs_path, @heads_path, @remotes_path]
|
193
|
+
prefix = prefixes.find { |path| File.file? path.join(name) }
|
194
|
+
|
195
|
+
prefix ? prefix.join(name) : nil
|
196
|
+
end
|
197
|
+
|
198
|
+
def read_oid_or_symref(path)
|
199
|
+
data = File.read(path).strip
|
200
|
+
match = SYMREF.match(data)
|
201
|
+
|
202
|
+
match ? SymRef.new(self, match[1]) : Ref.new(data)
|
203
|
+
rescue Errno::ENOENT
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def read_symref(path)
|
208
|
+
ref = read_oid_or_symref(path)
|
209
|
+
|
210
|
+
case ref
|
211
|
+
when SymRef then read_symref(@pathname.join(ref.path))
|
212
|
+
when Ref then ref.oid
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def update_ref_file(path, oid)
|
217
|
+
lockfile = Lockfile.new(path)
|
218
|
+
|
219
|
+
lockfile.hold_for_update
|
220
|
+
yield if block_given?
|
221
|
+
|
222
|
+
if oid
|
223
|
+
write_lockfile(lockfile, oid)
|
224
|
+
else
|
225
|
+
File.unlink(path) rescue Errno::ENOENT
|
226
|
+
lockfile.rollback
|
227
|
+
end
|
228
|
+
|
229
|
+
rescue Lockfile::MissingParent
|
230
|
+
FileUtils.mkdir_p(path.dirname)
|
231
|
+
retry
|
232
|
+
rescue => error
|
233
|
+
lockfile.rollback
|
234
|
+
raise error
|
235
|
+
end
|
236
|
+
|
237
|
+
def update_symref(path, oid)
|
238
|
+
lockfile = Lockfile.new(path)
|
239
|
+
lockfile.hold_for_update
|
240
|
+
|
241
|
+
ref = read_oid_or_symref(path)
|
242
|
+
|
243
|
+
unless ref.is_a?(SymRef)
|
244
|
+
write_lockfile(lockfile, oid)
|
245
|
+
return ref&.oid
|
246
|
+
end
|
247
|
+
|
248
|
+
begin
|
249
|
+
update_symref(@pathname.join(ref.path), oid)
|
250
|
+
ensure
|
251
|
+
lockfile.rollback
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def write_lockfile(lockfile, oid)
|
256
|
+
lockfile.write(oid)
|
257
|
+
lockfile.write("\n")
|
258
|
+
lockfile.commit
|
259
|
+
end
|
260
|
+
end
|