jit 0.0.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,116 @@
1
+ require "set"
2
+
3
+ require_relative "./base"
4
+ require_relative "./shared/fast_forward"
5
+ require_relative "./shared/receive_objects"
6
+ require_relative "./shared/remote_client"
7
+ require_relative "../remotes"
8
+ require_relative "../rev_list"
9
+
10
+ module Command
11
+ class Fetch < Base
12
+
13
+ include FastForward
14
+ include ReceiveObjects
15
+ include RemoteClient
16
+
17
+ CAPABILITIES = ["ofs-delta"]
18
+ UPLOAD_PACK = "git-upload-pack"
19
+
20
+ def define_options
21
+ @parser.on("-f", "--force") { @options[:force] = true }
22
+
23
+ @parser.on "--upload-pack=<upload-pack>" do |uploader|
24
+ @options[:uploader] = uploader
25
+ end
26
+ end
27
+
28
+ def run
29
+ configure
30
+ start_agent("fetch", @uploader, @fetch_url, CAPABILITIES)
31
+
32
+ recv_references
33
+ send_want_list
34
+ send_have_list
35
+ recv_objects
36
+ update_remote_refs
37
+
38
+ exit (@errors.empty? ? 0 : 1)
39
+ end
40
+
41
+ private
42
+
43
+ def configure
44
+ current_branch = repo.refs.current_ref.short_name
45
+ branch_remote = repo.config.get(["branch", current_branch, "remote"])
46
+
47
+ name = @args.fetch(0, branch_remote || Remotes::DEFAULT_REMOTE)
48
+ remote = repo.remotes.get(name)
49
+
50
+ @fetch_url = remote&.fetch_url || @args[0]
51
+ @uploader = @options[:uploader] || remote&.uploader || UPLOAD_PACK
52
+ @fetch_specs = (@args.size > 1) ? @args.drop(1) : remote&.fetch_specs
53
+ end
54
+
55
+ def send_want_list
56
+ @targets = Remotes::Refspec.expand(@fetch_specs, @remote_refs.keys)
57
+ wanted = Set.new
58
+
59
+ @local_refs = {}
60
+
61
+ @targets.each do |target, (source, _)|
62
+ local_oid = repo.refs.read_ref(target)
63
+ remote_oid = @remote_refs[source]
64
+
65
+ next if local_oid == remote_oid
66
+
67
+ @local_refs[target] = local_oid
68
+ wanted.add(remote_oid)
69
+ end
70
+
71
+ wanted.each { |oid| @conn.send_packet("want #{ oid }") }
72
+ @conn.send_packet(nil)
73
+
74
+ exit 0 if wanted.empty?
75
+ end
76
+
77
+ def send_have_list
78
+ options = { :all => true, :missing => true }
79
+ rev_list = ::RevList.new(repo, [], options)
80
+
81
+ rev_list.each { |commit| @conn.send_packet("have #{ commit.oid }") }
82
+ @conn.send_packet("done")
83
+
84
+ @conn.recv_until(Pack::SIGNATURE) {}
85
+ end
86
+
87
+ def recv_objects
88
+ unpack_limit = repo.config.get(["fetch", "unpackLimit"])
89
+ recv_packed_objects(unpack_limit, Pack::SIGNATURE)
90
+ end
91
+
92
+ def update_remote_refs
93
+ @stderr.puts "From #{ @fetch_url }"
94
+
95
+ @errors = {}
96
+ @local_refs.each { |target, oid| attempt_ref_update(target, oid) }
97
+ end
98
+
99
+ def attempt_ref_update(target, old_oid)
100
+ source, forced = @targets[target]
101
+
102
+ new_oid = @remote_refs[source]
103
+ ref_names = [source, target]
104
+ ff_error = fast_forward_error(old_oid, new_oid)
105
+
106
+ if @options[:force] or forced or ff_error == nil
107
+ repo.refs.update_ref(target, new_oid)
108
+ else
109
+ error = @errors[target] = ff_error
110
+ end
111
+
112
+ report_ref_update(ref_names, error, old_oid, new_oid, ff_error == nil)
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,41 @@
1
+ require "fileutils"
2
+
3
+ require_relative "./base"
4
+ require_relative "../config"
5
+ require_relative "../refs"
6
+
7
+ module Command
8
+ class Init < Base
9
+
10
+ DEFAULT_BRANCH = "master"
11
+
12
+ def run
13
+ path = @args.fetch(0, @dir)
14
+
15
+ root_path = expanded_pathname(path)
16
+ git_path = root_path.join(".git")
17
+
18
+ ["objects", "refs/heads"].each do |dir|
19
+ begin
20
+ FileUtils.mkdir_p(git_path.join(dir))
21
+ rescue Errno::EACCES => error
22
+ @stderr.puts "fatal: #{ error.message }"
23
+ exit 1
24
+ end
25
+ end
26
+
27
+ config = ::Config.new(git_path.join("config"))
28
+ config.open_for_update
29
+ config.set(["core", "bare"], false)
30
+ config.save
31
+
32
+ refs = Refs.new(git_path)
33
+ path = File.join("refs", "heads", DEFAULT_BRANCH)
34
+ refs.update_head("ref: #{ path }")
35
+
36
+ puts "Initialized empty Jit repository in #{ git_path }"
37
+ exit 0
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,188 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/print_diff"
3
+ require_relative "../rev_list"
4
+
5
+ module Command
6
+ class Log < Base
7
+
8
+ include PrintDiff
9
+
10
+ def define_options
11
+ @rev_list_opts = {}
12
+ @parser.on("--all") { @rev_list_opts[:all] = true }
13
+ @parser.on("--branches") { @rev_list_opts[:branches] = true }
14
+ @parser.on("--remotes") { @rev_list_opts[:remotes] = true }
15
+
16
+ @options[:decorate] = "auto"
17
+ @options[:abbrev] = :auto
18
+ @options[:format] = "medium"
19
+ @options[:patch] = false
20
+
21
+ define_print_diff_options
22
+
23
+ @parser.on "--decorate[=<format>]" do |format|
24
+ @options[:decorate] = format || "short"
25
+ end
26
+
27
+ @parser.on "--no-decorate" do
28
+ @options[:decorate] = "no"
29
+ end
30
+
31
+ @parser.on "--[no-]abbrev-commit" do |value|
32
+ @options[:abbrev] = value
33
+ end
34
+
35
+ @parser.on "--pretty=<format>", "--format=<format>" do |format|
36
+ @options[:format] = format
37
+ end
38
+
39
+ @parser.on "--oneline" do
40
+ @options[:abbrev] = true if @options[:abbrev] == :auto
41
+ @options[:format] = "oneline"
42
+ end
43
+
44
+ @parser.on "--cc" do
45
+ @options[:combined] = @options[:patch] = true
46
+ end
47
+ end
48
+
49
+ def run
50
+ setup_pager
51
+
52
+ @reverse_refs = repo.refs.reverse_refs
53
+ @current_ref = repo.refs.current_ref
54
+
55
+ @rev_list = ::RevList.new(repo, @args, @rev_list_opts)
56
+ @rev_list.each { |commit| show_commit(commit) }
57
+
58
+ exit 0
59
+ end
60
+
61
+ private
62
+
63
+ def blank_line
64
+ return if @options[:format] == "oneline"
65
+ puts "" if defined? @blank_line
66
+ @blank_line = true
67
+ end
68
+
69
+ def show_commit(commit)
70
+ case @options[:format]
71
+ when "medium" then show_commit_medium(commit)
72
+ when "oneline" then show_commit_oneline(commit)
73
+ end
74
+
75
+ show_patch(commit)
76
+ end
77
+
78
+ def show_commit_medium(commit)
79
+ author = commit.author
80
+
81
+ blank_line
82
+ puts fmt(:yellow, "commit #{ abbrev(commit) }") + decorate(commit)
83
+
84
+ if commit.merge?
85
+ oids = commit.parents.map { |oid| repo.database.short_oid(oid) }
86
+ puts "Merge: #{ oids.join(" ") }"
87
+ end
88
+
89
+ puts "Author: #{ author.name } <#{ author.email }>"
90
+ puts "Date: #{ author.readable_time }"
91
+ blank_line
92
+ commit.message.each_line { |line| puts " #{ line }" }
93
+ end
94
+
95
+ def show_commit_oneline(commit)
96
+ id = fmt(:yellow, abbrev(commit)) + decorate(commit)
97
+ puts "#{ id } #{ commit.title_line }"
98
+ end
99
+
100
+ def abbrev(commit)
101
+ if @options[:abbrev] == true
102
+ repo.database.short_oid(commit.oid)
103
+ else
104
+ commit.oid
105
+ end
106
+ end
107
+
108
+ def decorate(commit)
109
+ case @options[:decorate]
110
+ when "auto" then return "" unless @isatty
111
+ when "no" then return ""
112
+ end
113
+
114
+ refs = @reverse_refs[commit.oid]
115
+ return "" if refs.empty?
116
+
117
+ head, refs = refs.partition { |ref| ref.head? and not @current_ref.head? }
118
+ names = refs.map { |ref| decoration_name(head.first, ref) }
119
+
120
+ fmt(:yellow, " (") + names.join(fmt(:yellow, ", ")) + fmt(:yellow, ")")
121
+ end
122
+
123
+ def decoration_name(head, ref)
124
+ case @options[:decorate]
125
+ when "short", "auto" then name = ref.short_name
126
+ when "full" then name = ref.path
127
+ end
128
+
129
+ name = fmt(ref_color(ref), name)
130
+
131
+ if head and ref == @current_ref
132
+ name = fmt(ref_color(head), "#{ head.path } -> #{ name }")
133
+ end
134
+
135
+ name
136
+ end
137
+
138
+ def ref_color(ref)
139
+ return [:bold, :cyan] if ref.head?
140
+ return [:bold, :green] if ref.branch?
141
+ return [:bold, :red] if ref.remote?
142
+ end
143
+
144
+ def show_patch(commit)
145
+ return unless @options[:patch]
146
+ return show_merge_patch(commit) if commit.merge?
147
+
148
+ diff = @rev_list.tree_diff(commit.parent, commit.oid)
149
+ paths = diff.keys.sort_by(&:to_s)
150
+
151
+ blank_line
152
+
153
+ paths.each do |path|
154
+ old_item, new_item = diff[path]
155
+ print_diff(from_diff_item(path, old_item), from_diff_item(path, new_item))
156
+ end
157
+ end
158
+
159
+ def show_merge_patch(commit)
160
+ return unless @options[:combined]
161
+
162
+ diffs = commit.parents.map { |oid| @rev_list.tree_diff(oid, commit.oid) }
163
+
164
+ paths = diffs.first.keys.select do |path|
165
+ diffs.drop(1).all? { |diff| diff.has_key?(path) }
166
+ end
167
+
168
+ blank_line
169
+
170
+ paths.each do |path|
171
+ parents = diffs.map { |diff| from_diff_item(path, diff[path][0]) }
172
+ child = from_diff_item(path, diffs.first[path][1])
173
+
174
+ print_combined_diff(parents, child)
175
+ end
176
+ end
177
+
178
+ def from_diff_item(path, item)
179
+ if item
180
+ blob = repo.database.load(item.oid)
181
+ Target.new(path, item.oid, item.mode.to_s(8), blob.data)
182
+ else
183
+ Target.new(path, NULL_OID, nil, "")
184
+ end
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,148 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/write_commit"
3
+ require_relative "../merge/inputs"
4
+ require_relative "../merge/resolve"
5
+ require_relative "../revision"
6
+
7
+ module Command
8
+ class Merge < Base
9
+
10
+ include WriteCommit
11
+
12
+ COMMIT_NOTES = <<~MSG
13
+ Please enter a commit message to explain why this merge is necessary,
14
+ especially if it merges an updated upstream into a topic branch.
15
+
16
+ Lines starting with '#' will be ignored, and an empty message aborts
17
+ the commit.
18
+ MSG
19
+
20
+ def define_options
21
+ define_write_commit_options
22
+
23
+ @options[:mode] = :run
24
+
25
+ @parser.on("--abort") { @options[:mode] = :abort }
26
+ @parser.on("--continue") { @options[:mode] = :continue }
27
+ end
28
+
29
+ def run
30
+ handle_abort if @options[:mode] == :abort
31
+ handle_continue if @options[:mode] == :continue
32
+ handle_in_progress_merge if pending_commit.in_progress?
33
+
34
+ @inputs = ::Merge::Inputs.new(repo, Revision::HEAD, @args[0])
35
+ repo.refs.update_ref(Refs::ORIG_HEAD, @inputs.left_oid)
36
+
37
+ handle_merged_ancestor if @inputs.already_merged?
38
+ handle_fast_forward if @inputs.fast_forward?
39
+
40
+ pending_commit.start(@inputs.right_oid)
41
+ resolve_merge
42
+ commit_merge
43
+
44
+ exit 0
45
+ end
46
+
47
+ private
48
+
49
+ def resolve_merge
50
+ repo.index.load_for_update
51
+
52
+ merge = ::Merge::Resolve.new(repo, @inputs)
53
+ merge.on_progress { |info| puts info }
54
+ merge.execute
55
+
56
+ repo.index.write_updates
57
+ fail_on_conflict if repo.index.conflict?
58
+ end
59
+
60
+ def fail_on_conflict
61
+ edit_file(pending_commit.message_path) do |editor|
62
+ editor.puts(read_message || default_commit_message)
63
+ editor.puts("")
64
+ editor.note("Conflicts:")
65
+ repo.index.conflict_paths.each { |name| editor.note("\t#{ name }") }
66
+ editor.close
67
+ end
68
+
69
+ puts "Automatic merge failed; fix conflicts and then commit the result."
70
+ exit 1
71
+ end
72
+
73
+ def commit_merge
74
+ parents = [@inputs.left_oid, @inputs.right_oid]
75
+ message = compose_message
76
+
77
+ write_commit(parents, message)
78
+
79
+ pending_commit.clear
80
+ end
81
+
82
+ def compose_message
83
+ edit_file(pending_commit.message_path) do |editor|
84
+ editor.puts(read_message || default_commit_message)
85
+ editor.puts("")
86
+ editor.note(COMMIT_NOTES)
87
+
88
+ editor.close unless @options[:edit]
89
+ end
90
+ end
91
+
92
+ def default_commit_message
93
+ "Merge commit '#{ @inputs.right_name }'"
94
+ end
95
+
96
+ def handle_merged_ancestor
97
+ puts "Already up to date."
98
+ exit 0
99
+ end
100
+
101
+ def handle_fast_forward
102
+ a = repo.database.short_oid(@inputs.left_oid)
103
+ b = repo.database.short_oid(@inputs.right_oid)
104
+
105
+ puts "Updating #{ a }..#{ b }"
106
+ puts "Fast-forward"
107
+
108
+ repo.index.load_for_update
109
+
110
+ tree_diff = repo.database.tree_diff(@inputs.left_oid, @inputs.right_oid)
111
+ repo.migration(tree_diff).apply_changes
112
+
113
+ repo.index.write_updates
114
+ repo.refs.update_head(@inputs.right_oid)
115
+
116
+ exit 0
117
+ end
118
+
119
+ def handle_abort
120
+ repo.pending_commit.clear
121
+
122
+ repo.index.load_for_update
123
+ repo.hard_reset(repo.refs.read_head)
124
+ repo.index.write_updates
125
+
126
+ exit 0
127
+ rescue Repository::PendingCommit::Error => error
128
+ @stderr.puts "fatal: #{ error.message }"
129
+ exit 128
130
+ end
131
+
132
+ def handle_continue
133
+ repo.index.load
134
+ resume_merge(:merge)
135
+ rescue Repository::PendingCommit::Error => error
136
+ @stderr.puts "fatal: #{ error.message }"
137
+ exit 128
138
+ end
139
+
140
+ def handle_in_progress_merge
141
+ message = "Merging is not possible because you have unmerged files"
142
+ @stderr.puts "error: #{ message }."
143
+ @stderr.puts CONFLICT_MESSAGE
144
+ exit 128
145
+ end
146
+
147
+ end
148
+ end