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,172 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/fast_forward"
3
+ require_relative "./shared/remote_client"
4
+ require_relative "./shared/send_objects"
5
+ require_relative "../remotes"
6
+ require_relative "../revision"
7
+
8
+ module Command
9
+ class Push < Base
10
+
11
+ include FastForward
12
+ include RemoteClient
13
+ include SendObjects
14
+
15
+ CAPABILITIES = ["report-status"]
16
+ RECEIVE_PACK = "git-receive-pack"
17
+
18
+ UNPACK_LINE = /^unpack (.+)$/
19
+ UPDATE_LINE = /^(ok|ng) (\S+)(.*)$/
20
+
21
+ def define_options
22
+ @parser.on("-f", "--force") { @options[:force] = true }
23
+
24
+ @parser.on "--receive-pack=<receive-pack>" do |receiver|
25
+ @options[:receiver] = receiver
26
+ end
27
+ end
28
+
29
+ def run
30
+ configure
31
+ start_agent("push", @receiver, @push_url, CAPABILITIES)
32
+
33
+ recv_references
34
+ send_update_requests
35
+ send_objects
36
+ print_summary
37
+ recv_report_status
38
+
39
+ exit (@errors.empty? ? 0 : 1)
40
+ end
41
+
42
+ private
43
+
44
+ def configure
45
+ current_branch = repo.refs.current_ref.short_name
46
+ branch_remote = repo.config.get(["branch", current_branch, "remote"])
47
+ branch_merge = repo.config.get(["branch", current_branch, "merge"])
48
+
49
+ name = @args.fetch(0, branch_remote || Remotes::DEFAULT_REMOTE)
50
+ remote = repo.remotes.get(name)
51
+
52
+ @push_url = remote&.push_url || @args[0]
53
+ @fetch_specs = remote&.fetch_specs || []
54
+ @receiver = @options[:receiver] || remote&.receiver || RECEIVE_PACK
55
+
56
+ if @args.size > 1
57
+ @push_specs = @args.drop(1)
58
+ elsif branch_merge
59
+ spec = Remotes::Refspec.new(current_branch, branch_merge, false)
60
+ @push_specs = [spec.to_s]
61
+ else
62
+ @push_specs = remote&.push_specs
63
+ end
64
+ end
65
+
66
+ def send_update_requests
67
+ @updates = {}
68
+ @errors = []
69
+
70
+ local_refs = repo.refs.list_all_refs.map(&:path).sort
71
+ targets = Remotes::Refspec.expand(@push_specs, local_refs)
72
+
73
+ targets.each do |target, (source, forced)|
74
+ select_update(target, source, forced)
75
+ end
76
+
77
+ @updates.each { |ref, (*, old, new)| send_update(ref, old, new) }
78
+ @conn.send_packet(nil)
79
+ end
80
+
81
+ def select_update(target, source, forced)
82
+ return select_deletion(target) unless source
83
+
84
+ old_oid = @remote_refs[target]
85
+ new_oid = Revision.new(repo, source).resolve
86
+
87
+ return if old_oid == new_oid
88
+
89
+ ff_error = fast_forward_error(old_oid, new_oid)
90
+
91
+ if @options[:force] or forced or ff_error == nil
92
+ @updates[target] = [source, ff_error, old_oid, new_oid]
93
+ else
94
+ @errors.push([[source, target], ff_error])
95
+ end
96
+ end
97
+
98
+ def select_deletion(target)
99
+ if @conn.capable?("delete-refs")
100
+ @updates[target] = [nil, nil, @remote_refs[target], nil]
101
+ else
102
+ @errors.push([[nil, target], "remote does not support deleting refs"])
103
+ end
104
+ end
105
+
106
+ def send_update(ref, old_oid, new_oid)
107
+ old_oid = nil_to_zero(old_oid)
108
+ new_oid = nil_to_zero(new_oid)
109
+
110
+ @conn.send_packet("#{ old_oid } #{ new_oid } #{ ref }")
111
+ end
112
+
113
+ def nil_to_zero(oid)
114
+ oid == nil ? ZERO_OID : oid
115
+ end
116
+
117
+ def send_objects
118
+ revs = @updates.values.map(&:last).compact
119
+ return if revs.empty?
120
+
121
+ revs += @remote_refs.values.map { |oid| "^#{ oid }" }
122
+
123
+ send_packed_objects(revs)
124
+ end
125
+
126
+ def print_summary
127
+ if @updates.empty? and @errors.empty?
128
+ @stderr.puts "Everything up-to-date"
129
+ else
130
+ @stderr.puts "To #{ @push_url }"
131
+ @errors.each { |ref_names, error| report_ref_update(ref_names, error) }
132
+ end
133
+ end
134
+
135
+ def recv_report_status
136
+ return unless @conn.capable?("report-status") and not @updates.empty?
137
+
138
+ unpack_result = UNPACK_LINE.match(@conn.recv_packet)[1]
139
+
140
+ unless unpack_result == "ok"
141
+ @stderr.puts "error: remote unpack failed: #{ unpack_result }"
142
+ end
143
+
144
+ @conn.recv_until(nil) { |line| handle_status(line) }
145
+ end
146
+
147
+ def handle_status(line)
148
+ return unless match = UPDATE_LINE.match(line)
149
+
150
+ status = match[1]
151
+ ref = match[2]
152
+ error = (status == "ok") ? nil : match[3].strip
153
+
154
+ @errors.push([ref, error]) if error
155
+ report_update(ref, error)
156
+
157
+ targets = Remotes::Refspec.expand(@fetch_specs, [ref])
158
+
159
+ targets.each do |local_ref, (remote_ref, _)|
160
+ new_oid = @updates[remote_ref].last
161
+ repo.refs.update_ref(local_ref, new_oid) unless error
162
+ end
163
+ end
164
+
165
+ def report_update(target, error)
166
+ source, ff_error, old_oid, new_oid = @updates[target]
167
+ ref_names = [source, target]
168
+ report_ref_update(ref_names, error, old_oid, new_oid, ff_error == nil)
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,92 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/fast_forward"
3
+ require_relative "./shared/receive_objects"
4
+ require_relative "./shared/remote_agent"
5
+
6
+ module Command
7
+ class ReceivePack < Base
8
+
9
+ include FastForward
10
+ include ReceiveObjects
11
+ include RemoteAgent
12
+
13
+ CAPABILITIES = ["no-thin", "report-status", "delete-refs", "ofs-delta"]
14
+
15
+ def run
16
+ accept_client("receive-pack", CAPABILITIES)
17
+
18
+ send_references
19
+ recv_update_requests
20
+ recv_objects
21
+ update_refs
22
+
23
+ exit 0
24
+ end
25
+
26
+ private
27
+
28
+ def recv_update_requests
29
+ @requests = {}
30
+
31
+ @conn.recv_until(nil) do |line|
32
+ old_oid, new_oid, ref = line.split(/ +/)
33
+ @requests[ref] = [old_oid, new_oid].map { |oid| zero_to_nil(oid) }
34
+ end
35
+ end
36
+
37
+ def zero_to_nil(oid)
38
+ oid == ZERO_OID ? nil : oid
39
+ end
40
+
41
+ def recv_objects
42
+ @unpack_error = nil
43
+ unpack_limit = repo.config.get(["receive", "unpackLimit"])
44
+ recv_packed_objects(unpack_limit) if @requests.values.any?(&:last)
45
+ report_status("unpack ok")
46
+ rescue => error
47
+ @unpack_error = error
48
+ report_status("unpack #{ error.message }")
49
+ end
50
+
51
+ def update_refs
52
+ @requests.each { |ref, (old, new)| update_ref(ref, old, new) }
53
+ report_status(nil)
54
+ end
55
+
56
+ def update_ref(ref, old_oid, new_oid)
57
+ return report_status("ng #{ ref } unpacker error") if @unpack_error
58
+
59
+ validate_update(ref, old_oid, new_oid)
60
+ repo.refs.compare_and_swap(ref, old_oid, new_oid)
61
+ report_status("ok #{ ref }")
62
+ rescue => error
63
+ report_status("ng #{ ref } #{ error.message }")
64
+ end
65
+
66
+ def validate_update(ref, old_oid, new_oid)
67
+ if repo.config.get(["receive", "denyDeletes"])
68
+ raise "deletion prohibited" unless new_oid
69
+ end
70
+
71
+ if repo.config.get(["receive", "denyNonFastForwards"])
72
+ raise "non-fast-forward" if fast_forward_error(old_oid, new_oid)
73
+ end
74
+
75
+ return unless repo.config.get(["core", "bare"]) == false and
76
+ repo.refs.current_ref.path == ref
77
+
78
+ unless repo.config.get(["receive", "denyCurrentBranch"]) == false
79
+ raise "branch is currently checked out" if new_oid
80
+ end
81
+
82
+ unless repo.config.get(["receive", "denyDeleteCurrent"]) == false
83
+ raise "deletion of the current branch prohibited" unless new_oid
84
+ end
85
+ end
86
+
87
+ def report_status(line)
88
+ @conn.send_packet(line) if @conn.capable?("report-status")
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,55 @@
1
+ require_relative "./base"
2
+
3
+ module Command
4
+ class Remote < Base
5
+
6
+ def define_options
7
+ @parser.on("-v", "--verbose") { @options[:verbose] = true }
8
+
9
+ @options[:tracked] = []
10
+ @parser.on("-t <branch>") { |branch| @options[:tracked].push(branch) }
11
+ end
12
+
13
+ def run
14
+ case @args.shift
15
+ when "add" then add_remote
16
+ when "remove" then remove_remote
17
+ else list_remotes
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def add_remote
24
+ name, url = @args[0], @args[1]
25
+ repo.remotes.add(name, url, @options[:tracked])
26
+ exit 0
27
+ rescue Remotes::InvalidRemote => error
28
+ @stderr.puts "fatal: #{ error.message }"
29
+ exit 128
30
+ end
31
+
32
+ def remove_remote
33
+ repo.remotes.remove(@args[0])
34
+ exit 0
35
+ rescue Remotes::InvalidRemote => error
36
+ @stderr.puts "fatal: #{ error.message }"
37
+ exit 128
38
+ end
39
+
40
+ def list_remotes
41
+ repo.remotes.list_remotes.each { |name| list_remote(name) }
42
+ exit 0
43
+ end
44
+
45
+ def list_remote(name)
46
+ return puts name unless @options[:verbose]
47
+
48
+ remote = repo.remotes.get(name)
49
+
50
+ puts "#{ name }\t#{ remote.fetch_url } (fetch)"
51
+ puts "#{ name }\t#{ remote.push_url } (push)"
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,64 @@
1
+ require "pathname"
2
+
3
+ require_relative "./base"
4
+ require_relative "../revision"
5
+
6
+ module Command
7
+ class Reset < Base
8
+
9
+ def define_options
10
+ @options[:mode] = :mixed
11
+
12
+ @parser.on("--soft") { @options[:mode] = :soft }
13
+ @parser.on("--mixed") { @options[:mode] = :mixed }
14
+ @parser.on("--hard") { @options[:mode] = :hard }
15
+ end
16
+
17
+ def run
18
+ select_commit_oid
19
+
20
+ repo.index.load_for_update
21
+ reset_files
22
+ repo.index.write_updates
23
+
24
+ if @args.empty?
25
+ head_oid = repo.refs.update_head(@commit_oid)
26
+ repo.refs.update_ref(Refs::ORIG_HEAD, head_oid)
27
+ end
28
+
29
+ exit 0
30
+ end
31
+
32
+ private
33
+
34
+ def select_commit_oid
35
+ revision = @args.fetch(0, Revision::HEAD)
36
+ @commit_oid = Revision.new(repo, revision).resolve
37
+ @args.shift
38
+ rescue Revision::InvalidObject
39
+ @commit_oid = repo.refs.read_head
40
+ end
41
+
42
+ def reset_files
43
+ return if @options[:mode] == :soft
44
+ return repo.hard_reset(@commit_oid) if @options[:mode] == :hard
45
+
46
+ if @args.empty?
47
+ repo.index.clear!
48
+ reset_path(nil)
49
+ else
50
+ @args.each { |path| reset_path(Pathname.new(path)) }
51
+ end
52
+ end
53
+
54
+ def reset_path(pathname)
55
+ listing = repo.database.load_tree_list(@commit_oid, pathname)
56
+ repo.index.remove(pathname) if pathname
57
+
58
+ listing.each do |path, entry|
59
+ repo.index.add_from_db(path, entry)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "./base"
2
+ require_relative "../rev_list"
3
+
4
+ module Command
5
+ class RevList < Base
6
+
7
+ def define_options
8
+ @parser.on("--all") { @options[:all] = true }
9
+ @parser.on("--branches") { @options[:branches] = true }
10
+ @parser.on("--remotes") { @options[:remotes] = true }
11
+
12
+ @parser.on("--ignore-missing") { @options[:missing] = true }
13
+ @parser.on("--objects") { @options[:objects] = true }
14
+ @parser.on("--reverse") { @options[:reverse] = true }
15
+
16
+ @options[:walk] = true
17
+ @parser.on("--do-walk") { @options[:walk] = true }
18
+ @parser.on("--no-walk") { @options[:walk] = false }
19
+ end
20
+
21
+ def run
22
+ rev_list = ::RevList.new(repo, @args, @options)
23
+ iterator = @options[:reverse] ? :reverse_each : :each
24
+
25
+ rev_list.__send__(iterator) do |object, path|
26
+ puts "#{ object.oid } #{ path }".strip
27
+ end
28
+
29
+ exit 0
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/sequencing"
3
+ require_relative "./shared/write_commit"
4
+ require_relative "../merge/inputs"
5
+ require_relative "../rev_list"
6
+
7
+ module Command
8
+ class Revert < Base
9
+
10
+ include Sequencing
11
+ include WriteCommit
12
+
13
+ private
14
+
15
+ def merge_type
16
+ :revert
17
+ end
18
+
19
+ def store_commit_sequence
20
+ commits = ::RevList.new(repo, @args, :walk => false)
21
+ commits.each { |commit| sequencer.revert(commit) }
22
+ end
23
+
24
+ def revert(commit)
25
+ inputs = revert_merge_inputs(commit)
26
+ message = revert_commit_message(commit)
27
+
28
+ resolve_merge(inputs)
29
+ fail_on_conflict(inputs, message) if repo.index.conflict?
30
+
31
+ author = current_author
32
+ message = edit_revert_message(message)
33
+ picked = Database::Commit.new([inputs.left_oid], write_tree.oid,
34
+ author, author, message)
35
+
36
+ finish_commit(picked)
37
+ end
38
+
39
+ def revert_merge_inputs(commit)
40
+ short = repo.database.short_oid(commit.oid)
41
+
42
+ left_name = Refs::HEAD
43
+ left_oid = repo.refs.read_head
44
+ right_name = "parent of #{ short }... #{ commit.title_line.strip }"
45
+ right_oid = select_parent(commit)
46
+
47
+ ::Merge::CherryPick.new(left_name, right_name,
48
+ left_oid, right_oid,
49
+ [commit.oid])
50
+ end
51
+
52
+ def revert_commit_message(commit)
53
+ <<~MESSAGE
54
+ Revert "#{ commit.title_line.strip }"
55
+
56
+ This reverts commit #{ commit.oid }.
57
+ MESSAGE
58
+ end
59
+
60
+ def edit_revert_message(message)
61
+ edit_file(commit_message_path) do |editor|
62
+ editor.puts(message)
63
+ editor.puts("")
64
+ editor.note(Commit::COMMIT_NOTES)
65
+ end
66
+ end
67
+
68
+ end
69
+ end