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,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
@@ -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
@@ -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
@@ -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
@@ -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