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
@@ -0,0 +1,77 @@
|
|
1
|
+
class Database
|
2
|
+
class Tree
|
3
|
+
|
4
|
+
ENTRY_FORMAT = "Z*H40"
|
5
|
+
TREE_MODE = 040000
|
6
|
+
|
7
|
+
attr_accessor :oid, :entries
|
8
|
+
|
9
|
+
def self.parse(scanner)
|
10
|
+
entries = {}
|
11
|
+
|
12
|
+
until scanner.eos?
|
13
|
+
mode = scanner.scan_until(/ /).strip.to_i(8)
|
14
|
+
name = scanner.scan_until(/\0/)[0..-2]
|
15
|
+
|
16
|
+
oid = scanner.peek(20).unpack("H40").first
|
17
|
+
scanner.pos += 20
|
18
|
+
|
19
|
+
entries[name] = Entry.new(oid, mode)
|
20
|
+
end
|
21
|
+
|
22
|
+
Tree.new(entries)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.build(entries)
|
26
|
+
root = Tree.new
|
27
|
+
|
28
|
+
entries.each do |entry|
|
29
|
+
root.add_entry(entry.parent_directories, entry)
|
30
|
+
end
|
31
|
+
|
32
|
+
root
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(entries = {})
|
36
|
+
@entries = entries
|
37
|
+
end
|
38
|
+
|
39
|
+
def each_entry(&block)
|
40
|
+
@entries.each(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_entry(parents, entry)
|
44
|
+
if parents.empty?
|
45
|
+
@entries[entry.basename] = entry
|
46
|
+
else
|
47
|
+
tree = @entries[parents.first.basename] ||= Tree.new
|
48
|
+
tree.add_entry(parents.drop(1), entry)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def traverse(&block)
|
53
|
+
@entries.each do |name, entry|
|
54
|
+
entry.traverse(&block) if entry.is_a?(Tree)
|
55
|
+
end
|
56
|
+
block.call(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def mode
|
60
|
+
TREE_MODE
|
61
|
+
end
|
62
|
+
|
63
|
+
def type
|
64
|
+
"tree"
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
entries = @entries.map do |name, entry|
|
69
|
+
mode = entry.mode.to_s(8)
|
70
|
+
["#{ mode } #{ name }", entry.oid].pack(ENTRY_FORMAT)
|
71
|
+
end
|
72
|
+
|
73
|
+
entries.join("")
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
class Database
|
4
|
+
class TreeDiff
|
5
|
+
|
6
|
+
attr_reader :changes
|
7
|
+
|
8
|
+
def initialize(database, prune = [])
|
9
|
+
@database = database
|
10
|
+
@changes = {}
|
11
|
+
|
12
|
+
build_routing_table(prune)
|
13
|
+
end
|
14
|
+
|
15
|
+
def compare_oids(a, b, prefix = Pathname.new(""))
|
16
|
+
return if a == b
|
17
|
+
|
18
|
+
a_entries = a ? oid_to_tree(a).entries : {}
|
19
|
+
b_entries = b ? oid_to_tree(b).entries : {}
|
20
|
+
|
21
|
+
detect_deletions(a_entries, b_entries, prefix)
|
22
|
+
detect_additions(a_entries, b_entries, prefix)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def build_routing_table(prune)
|
28
|
+
@routes = {}
|
29
|
+
|
30
|
+
prune.each do |path|
|
31
|
+
table = @routes
|
32
|
+
path.each_filename { |name| table = table[name] ||= {} }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def routes_for_prefix(prefix)
|
37
|
+
prefix.each_filename.reduce(@routes) { |table, name| table[name] || {} }
|
38
|
+
end
|
39
|
+
|
40
|
+
def oid_to_tree(oid)
|
41
|
+
object = @database.load(oid)
|
42
|
+
|
43
|
+
case object
|
44
|
+
when Commit then @database.load(object.tree)
|
45
|
+
when Tree then object
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def detect_deletions(a, b, prefix)
|
50
|
+
routes = routes_for_prefix(prefix)
|
51
|
+
|
52
|
+
a.each do |name, entry|
|
53
|
+
next unless routes.empty? or routes.has_key?(name)
|
54
|
+
|
55
|
+
path = prefix.join(name)
|
56
|
+
other = b[name]
|
57
|
+
|
58
|
+
next if entry == other
|
59
|
+
|
60
|
+
tree_a, tree_b = [entry, other].map { |e| e&.tree? ? e.oid : nil }
|
61
|
+
compare_oids(tree_a, tree_b, path)
|
62
|
+
|
63
|
+
blobs = [entry, other].map { |e| e&.tree? ? nil : e }
|
64
|
+
@changes[path] = blobs if blobs.any?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def detect_additions(a, b, prefix)
|
69
|
+
routes = routes_for_prefix(prefix)
|
70
|
+
|
71
|
+
b.each do |name, entry|
|
72
|
+
next unless routes.empty? or routes.has_key?(name)
|
73
|
+
|
74
|
+
path = prefix.join(name)
|
75
|
+
other = a[name]
|
76
|
+
|
77
|
+
next if other
|
78
|
+
|
79
|
+
if entry.tree?
|
80
|
+
compare_oids(nil, entry.oid, path)
|
81
|
+
else
|
82
|
+
@changes[path] = [nil, entry]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/lib/diff.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "./diff/combined"
|
2
|
+
require_relative "./diff/hunk"
|
3
|
+
require_relative "./diff/myers"
|
4
|
+
|
5
|
+
module Diff
|
6
|
+
SYMBOLS = {
|
7
|
+
:eql => " ",
|
8
|
+
:ins => "+",
|
9
|
+
:del => "-"
|
10
|
+
}
|
11
|
+
|
12
|
+
Line = Struct.new(:number, :text)
|
13
|
+
|
14
|
+
Edit = Struct.new(:type, :a_line, :b_line) do
|
15
|
+
def a_lines
|
16
|
+
[a_line]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
line = a_line || b_line
|
21
|
+
SYMBOLS.fetch(type) + line.text
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.lines(document)
|
26
|
+
document = document.lines if document.is_a?(String)
|
27
|
+
document.map.with_index { |text, i| Line.new(i + 1, text) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.diff(a, b)
|
31
|
+
Myers.diff(lines(a), lines(b))
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.diff_hunks(a, b)
|
35
|
+
Hunk.filter(diff(a, b))
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.combined(as, b)
|
39
|
+
diffs = as.map { |a| diff(a, b) }
|
40
|
+
Combined.new(diffs).to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.combined_hunks(as, b)
|
44
|
+
Hunk.filter(combined(as, b))
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Diff
|
2
|
+
class Combined
|
3
|
+
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
Row = Struct.new(:edits) do
|
7
|
+
def type
|
8
|
+
types = edits.compact.map(&:type)
|
9
|
+
types.include?(:ins) ? :ins : types.first
|
10
|
+
end
|
11
|
+
|
12
|
+
def a_lines
|
13
|
+
edits.map { |edit| edit&.a_line }
|
14
|
+
end
|
15
|
+
|
16
|
+
def b_line
|
17
|
+
edits.first&.b_line
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
symbols = edits.map { |edit| SYMBOLS.fetch(edit&.type, " ") }
|
22
|
+
|
23
|
+
del = edits.find { |edit| edit&.type == :del }
|
24
|
+
line = del ? del.a_line : edits.first.b_line
|
25
|
+
|
26
|
+
symbols.join("") + line.text
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(diffs)
|
31
|
+
@diffs = diffs
|
32
|
+
end
|
33
|
+
|
34
|
+
def each
|
35
|
+
@offsets = @diffs.map { 0 }
|
36
|
+
|
37
|
+
loop do
|
38
|
+
@diffs.each_with_index do |diff, i|
|
39
|
+
consume_deletions(diff, i) { |row| yield row }
|
40
|
+
end
|
41
|
+
|
42
|
+
return if complete?
|
43
|
+
|
44
|
+
edits = offset_diffs.map { |offset, diff| diff[offset] }
|
45
|
+
@offsets.map! { |offset| offset + 1 }
|
46
|
+
|
47
|
+
yield Row.new(edits)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def complete?
|
54
|
+
offset_diffs.all? { |offset, diff| offset == diff.size }
|
55
|
+
end
|
56
|
+
|
57
|
+
def offset_diffs
|
58
|
+
@offsets.zip(@diffs)
|
59
|
+
end
|
60
|
+
|
61
|
+
def consume_deletions(diff, i)
|
62
|
+
while @offsets[i] < diff.size and diff[@offsets[i]].type == :del
|
63
|
+
edits = Array.new(@diffs.size)
|
64
|
+
edits[i] = diff[@offsets[i]]
|
65
|
+
@offsets[i] += 1
|
66
|
+
|
67
|
+
yield Row.new(edits)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/lib/diff/hunk.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Diff
|
2
|
+
|
3
|
+
HUNK_CONTEXT = 3
|
4
|
+
|
5
|
+
Hunk = Struct.new(:a_starts, :b_start, :edits) do
|
6
|
+
def self.filter(edits)
|
7
|
+
hunks = []
|
8
|
+
offset = 0
|
9
|
+
|
10
|
+
loop do
|
11
|
+
offset += 1 while edits[offset]&.type == :eql
|
12
|
+
return hunks if offset >= edits.size
|
13
|
+
|
14
|
+
offset -= HUNK_CONTEXT + 1
|
15
|
+
|
16
|
+
a_starts = (offset < 0) ? [] : edits[offset].a_lines.map(&:number)
|
17
|
+
b_start = (offset < 0) ? nil : edits[offset].b_line.number
|
18
|
+
|
19
|
+
hunks.push(Hunk.new(a_starts, b_start, []))
|
20
|
+
offset = build_hunk(hunks.last, edits, offset)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.build_hunk(hunk, edits, offset)
|
25
|
+
counter = -1
|
26
|
+
|
27
|
+
until counter == 0
|
28
|
+
hunk.edits.push(edits[offset]) if offset >= 0 and counter > 0
|
29
|
+
|
30
|
+
offset += 1
|
31
|
+
break if offset >= edits.size
|
32
|
+
|
33
|
+
case edits[offset + HUNK_CONTEXT]&.type
|
34
|
+
when :ins, :del
|
35
|
+
counter = 2 * HUNK_CONTEXT + 1
|
36
|
+
else
|
37
|
+
counter -= 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
offset
|
42
|
+
end
|
43
|
+
|
44
|
+
def header
|
45
|
+
a_lines = edits.map(&:a_lines).transpose
|
46
|
+
offsets = a_lines.map.with_index { |lines, i| format("-", lines, a_starts[i]) }
|
47
|
+
|
48
|
+
offsets.push(format("+", edits.map(&:b_line), b_start))
|
49
|
+
sep = "@" * offsets.size
|
50
|
+
|
51
|
+
[sep, *offsets, sep].join(" ")
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def format(sign, lines, start)
|
57
|
+
lines = lines.compact
|
58
|
+
start = lines.first&.number || start || 0
|
59
|
+
|
60
|
+
"#{ sign }#{ start },#{ lines.size }"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/diff/myers.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Diff
|
2
|
+
class Myers
|
3
|
+
|
4
|
+
def self.diff(a, b)
|
5
|
+
new(a, b).diff
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(a, b)
|
9
|
+
@a, @b = a, b
|
10
|
+
end
|
11
|
+
|
12
|
+
def diff
|
13
|
+
diff = []
|
14
|
+
|
15
|
+
backtrack do |prev_x, prev_y, x, y|
|
16
|
+
a_line, b_line = @a[prev_x], @b[prev_y]
|
17
|
+
|
18
|
+
if x == prev_x
|
19
|
+
diff.push(Edit.new(:ins, nil, b_line))
|
20
|
+
elsif y == prev_y
|
21
|
+
diff.push(Edit.new(:del, a_line, nil))
|
22
|
+
else
|
23
|
+
diff.push(Edit.new(:eql, a_line, b_line))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
diff.reverse
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def backtrack
|
33
|
+
x, y = @a.size, @b.size
|
34
|
+
|
35
|
+
shortest_edit.each_with_index.reverse_each do |v, d|
|
36
|
+
k = x - y
|
37
|
+
|
38
|
+
if k == -d or (k != d and v[k - 1] < v[k + 1])
|
39
|
+
prev_k = k + 1
|
40
|
+
else
|
41
|
+
prev_k = k - 1
|
42
|
+
end
|
43
|
+
|
44
|
+
prev_x = v[prev_k]
|
45
|
+
prev_y = prev_x - prev_k
|
46
|
+
|
47
|
+
while x > prev_x and y > prev_y
|
48
|
+
yield x - 1, y - 1, x, y
|
49
|
+
x, y = x - 1, y - 1
|
50
|
+
end
|
51
|
+
|
52
|
+
yield prev_x, prev_y, x, y if d > 0
|
53
|
+
|
54
|
+
x, y = prev_x, prev_y
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def shortest_edit
|
59
|
+
n, m = @a.size, @b.size
|
60
|
+
max = n + m
|
61
|
+
|
62
|
+
v = Array.new(2 * max + 1)
|
63
|
+
v[1] = 0
|
64
|
+
trace = []
|
65
|
+
|
66
|
+
(0 .. max).step do |d|
|
67
|
+
trace.push(v.clone)
|
68
|
+
|
69
|
+
(-d .. d).step(2) do |k|
|
70
|
+
if k == -d or (k != d and v[k - 1] < v[k + 1])
|
71
|
+
x = v[k + 1]
|
72
|
+
else
|
73
|
+
x = v[k - 1] + 1
|
74
|
+
end
|
75
|
+
|
76
|
+
y = x - k
|
77
|
+
|
78
|
+
while x < n and y < m and @a[x].text == @b[y].text
|
79
|
+
x, y = x + 1, y + 1
|
80
|
+
end
|
81
|
+
|
82
|
+
v[k] = x
|
83
|
+
|
84
|
+
return trace if x >= n and y >= m
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/lib/editor.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "shellwords"
|
2
|
+
|
3
|
+
class Editor
|
4
|
+
DEFAULT_EDITOR = "vi"
|
5
|
+
|
6
|
+
def self.edit(path, command)
|
7
|
+
editor = new(path, command)
|
8
|
+
yield editor
|
9
|
+
editor.edit_file
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(path, command)
|
13
|
+
@path = path
|
14
|
+
@command = command || DEFAULT_EDITOR
|
15
|
+
@closed = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def puts(string)
|
19
|
+
return if @closed
|
20
|
+
file.puts(string)
|
21
|
+
end
|
22
|
+
|
23
|
+
def note(string)
|
24
|
+
return if @closed
|
25
|
+
string.each_line { |line| file.puts("# #{ line }") }
|
26
|
+
end
|
27
|
+
|
28
|
+
def close
|
29
|
+
@closed = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit_file
|
33
|
+
file.close
|
34
|
+
editor_argv = Shellwords.shellsplit(@command) + [@path.to_s]
|
35
|
+
|
36
|
+
unless @closed or system(*editor_argv)
|
37
|
+
raise "There was a problem with the editor '#{ @command }'."
|
38
|
+
end
|
39
|
+
|
40
|
+
remove_notes(File.read(@path))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def remove_notes(string)
|
46
|
+
lines = string.lines.reject { |line| line.start_with?("#") }
|
47
|
+
|
48
|
+
if lines.all? { |line| /^\s*$/ =~ line }
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
"#{ lines.join("").strip }\n"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def file
|
56
|
+
flags = File::WRONLY | File::CREAT | File::TRUNC
|
57
|
+
@file ||= File.open(@path, flags)
|
58
|
+
end
|
59
|
+
end
|