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