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
data/bin/jit ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/command"
4
+
5
+ begin
6
+ cmd = Command.execute(Dir.getwd, ENV, ARGV, $stdin, $stdout, $stderr)
7
+ exit cmd.status
8
+
9
+ rescue Command::Unknown => error
10
+ $stderr.puts "jit: #{ error.message }"
11
+ exit 1
12
+
13
+ rescue => error
14
+ $stderr.puts "fatal: #{ error.message }"
15
+ if ENV["DEBUG"]
16
+ error.backtrace.each do |line|
17
+ $stderr.puts " from #{ line }"
18
+ end
19
+ end
20
+ exit 1
21
+ end
@@ -0,0 +1,32 @@
1
+ module Color
2
+ SGR_CODES = {
3
+ "normal" => 0,
4
+ "bold" => 1,
5
+ "dim" => 2,
6
+ "italic" => 3,
7
+ "ul" => 4,
8
+ "reverse" => 7,
9
+ "strike" => 9,
10
+ "black" => 30,
11
+ "red" => 31,
12
+ "green" => 32,
13
+ "yellow" => 33,
14
+ "blue" => 34,
15
+ "magenta" => 35,
16
+ "cyan" => 36,
17
+ "white" => 37
18
+ }
19
+
20
+ def self.format(style, string)
21
+ codes = [*style].map { |name| SGR_CODES.fetch(name.to_s) }
22
+ color = false
23
+
24
+ codes.each_with_index do |code, i|
25
+ next unless code >= 30
26
+ codes[i] += 10 if color
27
+ color = true
28
+ end
29
+
30
+ "\e[#{ codes.join(";") }m#{ string }\e[m"
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ require_relative "./command/add"
2
+ require_relative "./command/branch"
3
+ require_relative "./command/checkout"
4
+ require_relative "./command/cherry_pick"
5
+ require_relative "./command/commit"
6
+ require_relative "./command/config"
7
+ require_relative "./command/diff"
8
+ require_relative "./command/fetch"
9
+ require_relative "./command/init"
10
+ require_relative "./command/log"
11
+ require_relative "./command/merge"
12
+ require_relative "./command/push"
13
+ require_relative "./command/remote"
14
+ require_relative "./command/receive_pack"
15
+ require_relative "./command/reset"
16
+ require_relative "./command/rev_list"
17
+ require_relative "./command/revert"
18
+ require_relative "./command/rm"
19
+ require_relative "./command/status"
20
+ require_relative "./command/upload_pack"
21
+
22
+ module Command
23
+ Unknown = Class.new(StandardError)
24
+
25
+ COMMANDS = {
26
+ "init" => Init,
27
+ "config" => Config,
28
+ "add" => Add,
29
+ "rm" => Rm,
30
+ "commit" => Commit,
31
+ "status" => Status,
32
+ "diff" => Diff,
33
+ "branch" => Branch,
34
+ "checkout" => Checkout,
35
+ "reset" => Reset,
36
+ "rev-list" => RevList,
37
+ "log" => Log,
38
+ "merge" => Merge,
39
+ "cherry-pick" => CherryPick,
40
+ "revert" => Revert,
41
+ "remote" => Remote,
42
+ "fetch" => Fetch,
43
+ "push" => Push,
44
+ "upload-pack" => UploadPack,
45
+ "receive-pack" => ReceivePack
46
+ }
47
+
48
+ def self.execute(dir, env, argv, stdin, stdout, stderr)
49
+ name = argv.first
50
+ args = argv.drop(1)
51
+
52
+ unless COMMANDS.has_key?(name)
53
+ raise Unknown, "'#{ name }' is not a jit command."
54
+ end
55
+
56
+ command_class = COMMANDS[name]
57
+ command = command_class.new(dir, env, args, stdin, stdout, stderr)
58
+
59
+ command.execute
60
+ command
61
+ end
62
+ end
@@ -0,0 +1,65 @@
1
+ require "pathname"
2
+ require_relative "./base"
3
+
4
+ module Command
5
+ class Add < Base
6
+
7
+ LOCKED_INDEX_MESSAGE = <<~MSG
8
+ Another jit process seems to be running in this repository.
9
+ Please make sure all processes are terminated then try again.
10
+ If it still fails, a jit process may have crashed in this
11
+ repository earlier: remove the file manually to continue.
12
+ MSG
13
+
14
+ def run
15
+ repo.index.load_for_update
16
+ expanded_paths.each { |path| add_to_index(path) }
17
+ repo.index.write_updates
18
+ exit 0
19
+ rescue Lockfile::LockDenied => error
20
+ handle_locked_index(error)
21
+ rescue Workspace::MissingFile => error
22
+ handle_missing_file(error)
23
+ rescue Workspace::NoPermission => error
24
+ handle_unreadable_file(error)
25
+ end
26
+
27
+ private
28
+
29
+ def expanded_paths
30
+ @args.flat_map do |path|
31
+ repo.workspace.list_files(expanded_pathname(path))
32
+ end
33
+ end
34
+
35
+ def add_to_index(path)
36
+ data = repo.workspace.read_file(path)
37
+ stat = repo.workspace.stat_file(path)
38
+
39
+ blob = Database::Blob.new(data)
40
+ repo.database.store(blob)
41
+ repo.index.add(path, blob.oid, stat)
42
+ end
43
+
44
+ def handle_locked_index(error)
45
+ @stderr.puts "fatal: #{ error.message }"
46
+ @stderr.puts
47
+ @stderr.puts LOCKED_INDEX_MESSAGE
48
+ exit 128
49
+ end
50
+
51
+ def handle_missing_file(error)
52
+ @stderr.puts "fatal: #{ error.message }"
53
+ repo.index.release_lock
54
+ exit 128
55
+ end
56
+
57
+ def handle_unreadable_file(error)
58
+ @stderr.puts "error: #{ error.message }"
59
+ @stderr.puts "fatal: adding files failed"
60
+ repo.index.release_lock
61
+ exit 128
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,92 @@
1
+ require "optparse"
2
+ require "pathname"
3
+
4
+ require_relative "../color"
5
+ require_relative "../editor"
6
+ require_relative "../pager"
7
+ require_relative "../repository"
8
+
9
+ module Command
10
+ class Base
11
+
12
+ attr_reader :status
13
+
14
+ def initialize(dir, env, args, stdin, stdout, stderr)
15
+ @dir = dir
16
+ @env = env
17
+ @args = args
18
+ @stdin = stdin
19
+ @stdout = stdout
20
+ @stderr = stderr
21
+
22
+ @isatty = @stdout.isatty
23
+ end
24
+
25
+ def execute
26
+ parse_options
27
+ catch(:exit) { run }
28
+
29
+ if defined? @pager
30
+ @stdout.close_write
31
+ @pager.wait
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def repo
38
+ @repo ||= Repository.new(Pathname.new(@dir).join(".git"))
39
+ end
40
+
41
+ def expanded_pathname(path)
42
+ Pathname.new(File.expand_path(path, @dir))
43
+ end
44
+
45
+ def parse_options
46
+ @options = {}
47
+ @parser = OptionParser.new
48
+
49
+ define_options
50
+ @parser.parse!(@args)
51
+ end
52
+
53
+ def define_options
54
+ end
55
+
56
+ def setup_pager
57
+ return if defined? @pager
58
+ return unless @isatty
59
+
60
+ @pager = Pager.new(@env, @stdout, @stderr)
61
+ @stdout = @pager.input
62
+ end
63
+
64
+ def edit_file(path)
65
+ Editor.edit(path, editor_command) do |editor|
66
+ yield editor
67
+ editor.close unless @isatty
68
+ end
69
+ end
70
+
71
+ def editor_command
72
+ core_editor = repo.config.get(["core", "editor"])
73
+ @env["GIT_EDITOR"] || core_editor || @env["VISUAL"] || @env["EDITOR"]
74
+ end
75
+
76
+ def fmt(style, string)
77
+ @isatty ? Color.format(style, string) : string
78
+ end
79
+
80
+ def puts(string)
81
+ @stdout.puts(string)
82
+ rescue Errno::EPIPE
83
+ exit 0
84
+ end
85
+
86
+ def exit(status = 0)
87
+ @status = status
88
+ throw :exit
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,199 @@
1
+ require_relative "./base"
2
+ require_relative "./shared/fast_forward"
3
+ require_relative "../revision"
4
+
5
+ module Command
6
+ class Branch < Base
7
+
8
+ include FastForward
9
+
10
+ def define_options
11
+ @parser.on("-a", "--all") { @options[:all] = true }
12
+ @parser.on("-r", "--remotes") { @options[:remotes] = true }
13
+
14
+ @options[:verbose] = 0
15
+ @parser.on("-v", "--verbose") { @options[:verbose] += 1 }
16
+
17
+ @parser.on("-d", "--delete") { @options[:delete] = true }
18
+ @parser.on("-f", "--force") { @options[:force] = true }
19
+
20
+ @parser.on "-D" do
21
+ @options[:delete] = @options[:force] = true
22
+ end
23
+
24
+ @parser.on "-u <upstream>", "--set-upstream-to=<upstream>" do |upstream|
25
+ @options[:upstream] = upstream
26
+ end
27
+
28
+ @parser.on("-t", "--track") { @options[:track] = true }
29
+ @parser.on("--unset-upstream") { @options[:upstream] = :unset }
30
+ end
31
+
32
+ def run
33
+ if @options[:upstream]
34
+ set_upstream_branch
35
+ elsif @options[:delete]
36
+ delete_branches
37
+ elsif @args.empty?
38
+ list_branches
39
+ else
40
+ create_branch
41
+ end
42
+
43
+ exit 0
44
+ end
45
+
46
+ private
47
+
48
+ def list_branches
49
+ current = repo.refs.current_ref
50
+ branches = branch_refs.sort_by(&:path)
51
+ max_width = branches.map { |b| b.short_name.size }.max
52
+
53
+ setup_pager
54
+
55
+ branches.each do |ref|
56
+ info = format_ref(ref, current)
57
+ info.concat(extended_branch_info(ref, max_width))
58
+ puts info
59
+ end
60
+ end
61
+
62
+ def branch_refs
63
+ branches = repo.refs.list_branches
64
+ remotes = repo.refs.list_remotes
65
+
66
+ return branches + remotes if @options[:all]
67
+ return remotes if @options[:remotes]
68
+
69
+ branches
70
+ end
71
+
72
+ def format_ref(ref, current)
73
+ if ref == current
74
+ "* #{ fmt :green, ref.short_name }"
75
+ elsif ref.remote?
76
+ " #{ fmt :red, ref.short_name }"
77
+ else
78
+ " #{ ref.short_name }"
79
+ end
80
+ end
81
+
82
+ def extended_branch_info(ref, max_width)
83
+ return "" unless @options[:verbose] > 0
84
+
85
+ commit = repo.database.load(ref.read_oid)
86
+ short = repo.database.short_oid(commit.oid)
87
+ space = " " * (max_width - ref.short_name.size)
88
+ upstream = upstream_info(ref)
89
+
90
+ "#{ space } #{ short }#{ upstream } #{ commit.title_line }"
91
+ end
92
+
93
+ def upstream_info(ref)
94
+ divergence = repo.divergence(ref)
95
+ return "" unless divergence.upstream
96
+
97
+ ahead = divergence.ahead
98
+ behind = divergence.behind
99
+
100
+ return "" if ahead == 0 and behind == 0
101
+
102
+ info = []
103
+
104
+ if @options[:verbose] > 1
105
+ info.push(fmt(:blue, repo.refs.short_name(divergence.upstream)))
106
+ end
107
+ info.push("ahead #{ ahead }") if ahead > 0
108
+ info.push("behind #{ behind }") if behind > 0
109
+
110
+ " [#{ info.join(", ") }]"
111
+ end
112
+
113
+ def create_branch
114
+ branch_name = @args[0]
115
+ start_point = @args[1]
116
+
117
+ if start_point
118
+ revision = Revision.new(repo, start_point)
119
+ start_oid = revision.resolve(Revision::COMMIT)
120
+ else
121
+ start_oid = repo.refs.read_head
122
+ end
123
+
124
+ repo.refs.create_branch(branch_name, start_oid)
125
+ set_upstream(branch_name, start_point) if @options[:track]
126
+
127
+ rescue Refs::InvalidBranch => error
128
+ @stderr.puts "fatal: #{ error.message }"
129
+ exit 128
130
+
131
+ rescue Revision::InvalidObject => error
132
+ revision.errors.each do |err|
133
+ @stderr.puts "error: #{ err.message }"
134
+ err.hint.each { |line| @stderr.puts "hint: #{ line }" }
135
+ end
136
+ @stderr.puts "fatal: #{ error.message }"
137
+ exit 128
138
+ end
139
+
140
+ def delete_branches
141
+ @args.each { |branch_name| delete_branch(branch_name) }
142
+ end
143
+
144
+ def delete_branch(branch_name)
145
+ check_merge_status(branch_name) unless @options[:force]
146
+
147
+ oid = repo.refs.delete_branch(branch_name)
148
+ short = repo.database.short_oid(oid)
149
+
150
+ repo.remotes.unset_upstream(branch_name)
151
+
152
+ puts "Deleted branch #{ branch_name } (was #{ short })."
153
+
154
+ rescue Refs::InvalidBranch => error
155
+ @stderr.puts "error: #{ error }"
156
+ exit 1
157
+ end
158
+
159
+ def check_merge_status(branch_name)
160
+ upstream = repo.remotes.get_upstream(branch_name)
161
+ head_oid = upstream ? repo.refs.read_ref(upstream) : repo.refs.read_head
162
+ branch_oid = repo.refs.read_ref(branch_name)
163
+
164
+ if fast_forward_error(branch_oid, head_oid)
165
+ @stderr.puts "error: The branch '#{ branch_name }' is not fully merged."
166
+ exit 1
167
+ end
168
+ end
169
+
170
+ def set_upstream_branch
171
+ branch_name = @args.first || repo.refs.current_ref.short_name
172
+
173
+ if @options[:upstream] == :unset
174
+ repo.remotes.unset_upstream(branch_name)
175
+ else
176
+ set_upstream(branch_name, @options[:upstream])
177
+ end
178
+ end
179
+
180
+ def set_upstream(branch_name, upstream)
181
+ upstream = repo.refs.long_name(upstream)
182
+ remote, ref = repo.remotes.set_upstream(branch_name, upstream)
183
+
184
+ base = repo.refs.short_name(ref)
185
+
186
+ puts "Branch '#{ branch_name }' set up to track remote " +
187
+ "branch '#{ base }' from '#{ remote }'."
188
+
189
+ rescue Refs::InvalidBranch => error
190
+ @stderr.puts "error: #{ error.message }"
191
+ exit 1
192
+
193
+ rescue Remotes::InvalidBranch => error
194
+ @stderr.puts "fatal: #{ error.message }"
195
+ exit 128
196
+ end
197
+
198
+ end
199
+ end