jit 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +674 -0
  3. data/bin/jit +21 -0
  4. data/lib/color.rb +32 -0
  5. data/lib/command.rb +62 -0
  6. data/lib/command/add.rb +65 -0
  7. data/lib/command/base.rb +92 -0
  8. data/lib/command/branch.rb +199 -0
  9. data/lib/command/checkout.rb +104 -0
  10. data/lib/command/cherry_pick.rb +51 -0
  11. data/lib/command/commit.rb +86 -0
  12. data/lib/command/config.rb +126 -0
  13. data/lib/command/diff.rb +114 -0
  14. data/lib/command/fetch.rb +116 -0
  15. data/lib/command/init.rb +41 -0
  16. data/lib/command/log.rb +188 -0
  17. data/lib/command/merge.rb +148 -0
  18. data/lib/command/push.rb +172 -0
  19. data/lib/command/receive_pack.rb +92 -0
  20. data/lib/command/remote.rb +55 -0
  21. data/lib/command/reset.rb +64 -0
  22. data/lib/command/rev_list.rb +33 -0
  23. data/lib/command/revert.rb +69 -0
  24. data/lib/command/rm.rb +105 -0
  25. data/lib/command/shared/fast_forward.rb +19 -0
  26. data/lib/command/shared/print_diff.rb +116 -0
  27. data/lib/command/shared/receive_objects.rb +37 -0
  28. data/lib/command/shared/remote_agent.rb +44 -0
  29. data/lib/command/shared/remote_client.rb +82 -0
  30. data/lib/command/shared/send_objects.rb +24 -0
  31. data/lib/command/shared/sequencing.rb +146 -0
  32. data/lib/command/shared/write_commit.rb +167 -0
  33. data/lib/command/status.rb +210 -0
  34. data/lib/command/upload_pack.rb +54 -0
  35. data/lib/config.rb +240 -0
  36. data/lib/config/stack.rb +42 -0
  37. data/lib/database.rb +112 -0
  38. data/lib/database/author.rb +27 -0
  39. data/lib/database/backends.rb +57 -0
  40. data/lib/database/blob.rb +24 -0
  41. data/lib/database/commit.rb +70 -0
  42. data/lib/database/entry.rb +7 -0
  43. data/lib/database/loose.rb +70 -0
  44. data/lib/database/packed.rb +75 -0
  45. data/lib/database/tree.rb +77 -0
  46. data/lib/database/tree_diff.rb +88 -0
  47. data/lib/diff.rb +46 -0
  48. data/lib/diff/combined.rb +72 -0
  49. data/lib/diff/hunk.rb +64 -0
  50. data/lib/diff/myers.rb +90 -0
  51. data/lib/editor.rb +59 -0
  52. data/lib/index.rb +212 -0
  53. data/lib/index/checksum.rb +44 -0
  54. data/lib/index/entry.rb +91 -0
  55. data/lib/lockfile.rb +55 -0
  56. data/lib/merge/bases.rb +38 -0
  57. data/lib/merge/common_ancestors.rb +77 -0
  58. data/lib/merge/diff3.rb +156 -0
  59. data/lib/merge/inputs.rb +42 -0
  60. data/lib/merge/resolve.rb +178 -0
  61. data/lib/pack.rb +45 -0
  62. data/lib/pack/compressor.rb +83 -0
  63. data/lib/pack/delta.rb +58 -0
  64. data/lib/pack/entry.rb +54 -0
  65. data/lib/pack/expander.rb +54 -0
  66. data/lib/pack/index.rb +100 -0
  67. data/lib/pack/indexer.rb +200 -0
  68. data/lib/pack/numbers.rb +79 -0
  69. data/lib/pack/reader.rb +98 -0
  70. data/lib/pack/stream.rb +80 -0
  71. data/lib/pack/unpacker.rb +62 -0
  72. data/lib/pack/window.rb +47 -0
  73. data/lib/pack/writer.rb +92 -0
  74. data/lib/pack/xdelta.rb +118 -0
  75. data/lib/pager.rb +24 -0
  76. data/lib/progress.rb +78 -0
  77. data/lib/refs.rb +260 -0
  78. data/lib/remotes.rb +82 -0
  79. data/lib/remotes/protocol.rb +82 -0
  80. data/lib/remotes/refspec.rb +70 -0
  81. data/lib/remotes/remote.rb +57 -0
  82. data/lib/repository.rb +64 -0
  83. data/lib/repository/divergence.rb +21 -0
  84. data/lib/repository/hard_reset.rb +35 -0
  85. data/lib/repository/inspector.rb +49 -0
  86. data/lib/repository/migration.rb +168 -0
  87. data/lib/repository/pending_commit.rb +60 -0
  88. data/lib/repository/sequencer.rb +118 -0
  89. data/lib/repository/status.rb +98 -0
  90. data/lib/rev_list.rb +244 -0
  91. data/lib/revision.rb +155 -0
  92. data/lib/sorted_hash.rb +17 -0
  93. data/lib/temp_file.rb +34 -0
  94. data/lib/workspace.rb +107 -0
  95. metadata +103 -9
@@ -0,0 +1,156 @@
1
+ require_relative "../diff"
2
+
3
+ module Merge
4
+ class Diff3
5
+
6
+ Clean = Struct.new(:lines) do
7
+ def to_s(*)
8
+ lines.join("")
9
+ end
10
+ end
11
+
12
+ Conflict = Struct.new(:o_lines, :a_lines, :b_lines) do
13
+ def to_s(a_name = nil, b_name = nil)
14
+ text = ""
15
+ separator(text, "<", a_name)
16
+ a_lines.each { |line| text.concat(line) }
17
+ separator(text, "=")
18
+ b_lines.each { |line| text.concat(line) }
19
+ separator(text, ">", b_name)
20
+ text
21
+ end
22
+
23
+ def separator(text, char, name = nil)
24
+ text.concat(char * 7)
25
+ text.concat(" #{ name }") if name
26
+ text.concat("\n")
27
+ end
28
+ end
29
+
30
+ Result = Struct.new(:chunks) do
31
+ def clean?
32
+ chunks.none? { |chunk| chunk.is_a?(Conflict) }
33
+ end
34
+
35
+ def to_s(a_name = nil, b_name = nil)
36
+ chunks.map { |chunk| chunk.to_s(a_name, b_name) }.join("")
37
+ end
38
+ end
39
+
40
+ def self.merge(o, a, b)
41
+ o = o.lines if o.is_a?(String)
42
+ a = a.lines if a.is_a?(String)
43
+ b = b.lines if b.is_a?(String)
44
+
45
+ new(o, a, b).merge
46
+ end
47
+
48
+ def initialize(o, a, b)
49
+ @o, @a, @b = o, a, b
50
+ end
51
+
52
+ def merge
53
+ setup
54
+ generate_chunks
55
+ Result.new(@chunks)
56
+ end
57
+
58
+ def setup
59
+ @chunks = []
60
+ @line_o = @line_a = @line_b = 0
61
+
62
+ @match_a = match_set(@a)
63
+ @match_b = match_set(@b)
64
+ end
65
+
66
+ def match_set(file)
67
+ matches = {}
68
+
69
+ Diff.diff(@o, file).each do |edit|
70
+ next unless edit.type == :eql
71
+ matches[edit.a_line.number] = edit.b_line.number
72
+ end
73
+
74
+ matches
75
+ end
76
+
77
+ def generate_chunks
78
+ loop do
79
+ i = find_next_mismatch
80
+
81
+ if i == 1
82
+ o, a, b = find_next_match
83
+
84
+ if a and b
85
+ emit_chunk(o, a, b)
86
+ else
87
+ emit_final_chunk
88
+ return
89
+ end
90
+
91
+ elsif i
92
+ emit_chunk(@line_o + i, @line_a + i, @line_b + i)
93
+
94
+ else
95
+ emit_final_chunk
96
+ return
97
+ end
98
+ end
99
+ end
100
+
101
+ def find_next_mismatch
102
+ i = 1
103
+ while in_bounds?(i) and
104
+ match?(@match_a, @line_a, i) and
105
+ match?(@match_b, @line_b, i)
106
+ i += 1
107
+ end
108
+ in_bounds?(i) ? i : nil
109
+ end
110
+
111
+ def in_bounds?(i)
112
+ @line_o + i <= @o.size or
113
+ @line_a + i <= @a.size or
114
+ @line_b + i <= @b.size
115
+ end
116
+
117
+ def match?(matches, offset, i)
118
+ matches[@line_o + i] == offset + i
119
+ end
120
+
121
+ def find_next_match
122
+ o = @line_o + 1
123
+ until o > @o.size or (@match_a.has_key?(o) and @match_b.has_key?(o))
124
+ o += 1
125
+ end
126
+ [o, @match_a[o], @match_b[o]]
127
+ end
128
+
129
+ def emit_chunk(o, a, b)
130
+ write_chunk(
131
+ @o[@line_o ... o - 1],
132
+ @a[@line_a ... a - 1],
133
+ @b[@line_b ... b - 1])
134
+
135
+ @line_o, @line_a, @line_b = o - 1, a - 1, b - 1
136
+ end
137
+
138
+ def emit_final_chunk
139
+ write_chunk(
140
+ @o[@line_o .. -1],
141
+ @a[@line_a .. -1],
142
+ @b[@line_b .. -1])
143
+ end
144
+
145
+ def write_chunk(o, a, b)
146
+ if a == o or a == b
147
+ @chunks.push(Clean.new(b))
148
+ elsif b == o
149
+ @chunks.push(Clean.new(a))
150
+ else
151
+ @chunks.push(Conflict.new(o, a, b))
152
+ end
153
+ end
154
+
155
+ end
156
+ end
@@ -0,0 +1,42 @@
1
+ require_relative "./bases"
2
+ require_relative "../revision"
3
+
4
+ module Merge
5
+ class Inputs
6
+
7
+ ATTRS = [ :left_name, :right_name,
8
+ :left_oid, :right_oid,
9
+ :base_oids ]
10
+
11
+ attr_reader(*ATTRS)
12
+
13
+ def initialize(repository, left_name, right_name)
14
+ @repo = repository
15
+ @left_name = left_name
16
+ @right_name = right_name
17
+
18
+ @left_oid = resolve_rev(@left_name)
19
+ @right_oid = resolve_rev(@right_name)
20
+
21
+ common = Bases.new(@repo.database, @left_oid, @right_oid)
22
+ @base_oids = common.find
23
+ end
24
+
25
+ def already_merged?
26
+ @base_oids == [@right_oid]
27
+ end
28
+
29
+ def fast_forward?
30
+ @base_oids == [@left_oid]
31
+ end
32
+
33
+ private
34
+
35
+ def resolve_rev(rev)
36
+ Revision.new(@repo, rev).resolve(Revision::COMMIT)
37
+ end
38
+
39
+ end
40
+
41
+ CherryPick = Struct.new(*Inputs::ATTRS)
42
+ end
@@ -0,0 +1,178 @@
1
+ require_relative "./diff3"
2
+
3
+ module Merge
4
+ class Resolve
5
+
6
+ def initialize(repository, inputs)
7
+ @repo = repository
8
+ @inputs = inputs
9
+
10
+ @on_progress = nil
11
+ end
12
+
13
+ def on_progress(&block)
14
+ @on_progress = block
15
+ end
16
+
17
+ def execute
18
+ prepare_tree_diffs
19
+
20
+ migration = @repo.migration(@clean_diff)
21
+ migration.apply_changes
22
+
23
+ add_conflicts_to_index
24
+ write_untracked_files
25
+ end
26
+
27
+ private
28
+
29
+ def prepare_tree_diffs
30
+ base_oid = @inputs.base_oids.first
31
+ @left_diff = @repo.database.tree_diff(base_oid, @inputs.left_oid)
32
+ @right_diff = @repo.database.tree_diff(base_oid, @inputs.right_oid)
33
+ @clean_diff = {}
34
+ @conflicts = {}
35
+ @untracked = {}
36
+
37
+ @right_diff.each do |path, (old_item, new_item)|
38
+ file_dir_conflict(path, @left_diff, @inputs.left_name) if new_item
39
+ same_path_conflict(path, old_item, new_item)
40
+ end
41
+
42
+ @left_diff.each do |path, (_, new_item)|
43
+ file_dir_conflict(path, @right_diff, @inputs.right_name) if new_item
44
+ end
45
+ end
46
+
47
+ def same_path_conflict(path, base, right)
48
+ return if @conflicts[path]
49
+
50
+ unless @left_diff.has_key?(path)
51
+ @clean_diff[path] = [base, right]
52
+ return
53
+ end
54
+
55
+ left = @left_diff[path][1]
56
+ return if left == right
57
+
58
+ log "Auto-merging #{ path }" if left and right
59
+
60
+ oid_ok, oid = merge_blobs(base&.oid, left&.oid, right&.oid)
61
+ mode_ok, mode = merge_modes(base&.mode, left&.mode, right&.mode)
62
+
63
+ @clean_diff[path] = [left, Database::Entry.new(oid, mode)]
64
+ return if oid_ok and mode_ok
65
+
66
+ @conflicts[path] = [base, left, right]
67
+ log_conflict(path)
68
+ end
69
+
70
+ def merge_blobs(base_oid, left_oid, right_oid)
71
+ result = merge3(base_oid, left_oid, right_oid)
72
+ return result if result
73
+
74
+ oids = [base_oid, left_oid, right_oid]
75
+ blobs = oids.map { |oid| oid ? @repo.database.load(oid).data : "" }
76
+ merge = Diff3.merge(*blobs)
77
+
78
+ data = merge.to_s(@inputs.left_name, @inputs.right_name)
79
+ blob = Database::Blob.new(data)
80
+ @repo.database.store(blob)
81
+
82
+ [merge.clean?, blob.oid]
83
+ end
84
+
85
+ def merge_modes(base_mode, left_mode, right_mode)
86
+ merge3(base_mode, left_mode, right_mode) || [false, left_mode]
87
+ end
88
+
89
+ def merge3(base, left, right)
90
+ return [false, right] unless left
91
+ return [false, left] unless right
92
+
93
+ if left == base or left == right
94
+ [true, right]
95
+ elsif right == base
96
+ [true, left]
97
+ end
98
+ end
99
+
100
+ def file_dir_conflict(path, diff, name)
101
+ path.dirname.ascend do |parent|
102
+ old_item, new_item = diff[parent]
103
+ next unless new_item
104
+
105
+ @conflicts[parent] = case name
106
+ when @inputs.left_name then [old_item, new_item, nil]
107
+ when @inputs.right_name then [old_item, nil, new_item]
108
+ end
109
+
110
+ @clean_diff.delete(parent)
111
+ rename = "#{ parent }~#{ name }"
112
+ @untracked[rename] = new_item
113
+
114
+ log "Adding #{ path }" unless diff[path]
115
+ log_conflict(parent, rename)
116
+ end
117
+ end
118
+
119
+ def add_conflicts_to_index
120
+ @conflicts.each do |path, items|
121
+ @repo.index.add_conflict_set(path, items)
122
+ end
123
+ end
124
+
125
+ def write_untracked_files
126
+ @untracked.each do |path, item|
127
+ blob = @repo.database.load(item.oid)
128
+ @repo.workspace.write_file(path, blob.data)
129
+ end
130
+ end
131
+
132
+ def log(message)
133
+ @on_progress&.call(message)
134
+ end
135
+
136
+ def log_conflict(path, rename = nil)
137
+ base, left, right = @conflicts[path]
138
+
139
+ if left and right
140
+ log_left_right_conflict(path)
141
+ elsif base and (left or right)
142
+ log_modify_delete_conflict(path, rename)
143
+ else
144
+ log_file_directory_conflict(path, rename)
145
+ end
146
+ end
147
+
148
+ def log_left_right_conflict(path)
149
+ type = @conflicts[path][0] ? "content" : "add/add"
150
+ log "CONFLICT (#{ type }): Merge conflict in #{ path }"
151
+ end
152
+
153
+ def log_modify_delete_conflict(path, rename)
154
+ deleted, modified = log_branch_names(path)
155
+
156
+ rename = rename ? " at #{ rename }" : ""
157
+
158
+ log "CONFLICT (modify/delete): #{ path } " +
159
+ "deleted in #{ deleted } and modified in #{ modified }. " +
160
+ "Version #{ modified } of #{ path } left in tree#{ rename }."
161
+ end
162
+
163
+ def log_file_directory_conflict(path, rename)
164
+ type = @conflicts[path][1] ? "file/directory" : "directory/file"
165
+ branch, _ = log_branch_names(path)
166
+
167
+ log "CONFLICT (#{ type }): There is a directory " +
168
+ "with name #{ path } in #{ branch }. " +
169
+ "Adding #{ path } as #{ rename }"
170
+ end
171
+
172
+ def log_branch_names(path)
173
+ a, b = @inputs.left_name, @inputs.right_name
174
+ @conflicts[path][1] ? [b, a] : [a, b]
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,45 @@
1
+ require_relative "./pack/reader"
2
+ require_relative "./pack/writer"
3
+ require_relative "./pack/stream"
4
+ require_relative "./pack/indexer"
5
+ require_relative "./pack/unpacker"
6
+
7
+ module Pack
8
+ HEADER_SIZE = 12
9
+ HEADER_FORMAT = "a4N2"
10
+ SIGNATURE = "PACK"
11
+ VERSION = 2
12
+
13
+ GIT_MAX_COPY = 0x10000
14
+ MAX_COPY_SIZE = 0xffffff
15
+ MAX_INSERT_SIZE = 0x7f
16
+
17
+ IDX_SIGNATURE = 0xff744f63
18
+ IDX_MAX_OFFSET = 0x80000000
19
+
20
+ COMMIT = 1
21
+ TREE = 2
22
+ BLOB = 3
23
+
24
+ OFS_DELTA = 6
25
+ REF_DELTA = 7
26
+
27
+ TYPE_CODES = {
28
+ "commit" => COMMIT,
29
+ "tree" => TREE,
30
+ "blob" => BLOB
31
+ }
32
+
33
+ InvalidPack = Class.new(StandardError)
34
+
35
+ Record = Struct.new(:type, :data) do
36
+ attr_accessor :oid
37
+
38
+ def to_s
39
+ data
40
+ end
41
+ end
42
+
43
+ OfsDelta = Struct.new(:base_ofs, :delta_data)
44
+ RefDelta = Struct.new(:base_oid, :delta_data)
45
+ end
@@ -0,0 +1,83 @@
1
+ require_relative "./delta"
2
+ require_relative "./window"
3
+
4
+ module Pack
5
+ class Compressor
6
+
7
+ OBJECT_SIZE = 50..0x20000000
8
+ MAX_DEPTH = 50
9
+ WINDOW_SIZE = 8
10
+
11
+ def initialize(database, progress)
12
+ @database = database
13
+ @window = Window.new(WINDOW_SIZE)
14
+ @progress = progress
15
+ @objects = []
16
+ end
17
+
18
+ def add(entry)
19
+ return unless OBJECT_SIZE.include?(entry.size)
20
+ @objects.push(entry)
21
+ end
22
+
23
+ def build_deltas
24
+ @progress&.start("Compressing objects", @objects.size)
25
+
26
+ @objects.sort! { |a, b| b.sort_key <=> a.sort_key }
27
+
28
+ @objects.each do |entry|
29
+ build_delta(entry)
30
+ @progress&.tick
31
+ end
32
+ @progress&.stop
33
+ end
34
+
35
+ private
36
+
37
+ def build_delta(entry)
38
+ object = @database.load_raw(entry.oid)
39
+ target = @window.add(entry, object.data)
40
+
41
+ @window.each { |source| try_delta(source, target) }
42
+ end
43
+
44
+ def try_delta(source, target)
45
+ return unless source.type == target.type
46
+ return unless source.depth < MAX_DEPTH
47
+
48
+ max_size = max_size_heuristic(source, target)
49
+ return unless compatible_sizes?(source, target, max_size)
50
+
51
+ delta = Delta.new(source, target)
52
+ size = target.entry.packed_size
53
+
54
+ return if delta.size > max_size
55
+ return if delta.size == size and delta.base.depth + 1 >= target.depth
56
+
57
+ target.entry.assign_delta(delta)
58
+ end
59
+
60
+ def max_size_heuristic(source, target)
61
+ if target.delta
62
+ max_size = target.delta.size
63
+ ref_depth = target.depth
64
+ else
65
+ max_size = target.size / 2 - 20
66
+ ref_depth = 1
67
+ end
68
+
69
+ max_size * (MAX_DEPTH - source.depth) / (MAX_DEPTH + 1 - ref_depth)
70
+ end
71
+
72
+ def compatible_sizes?(source, target, max_size)
73
+ size_diff = [target.size - source.size, 0].max
74
+
75
+ return false if max_size == 0
76
+ return false if size_diff >= max_size
77
+ return false if target.size < source.size / 32
78
+
79
+ true
80
+ end
81
+
82
+ end
83
+ end