jit 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE.txt +674 -0
- data/bin/jit +21 -0
- data/lib/color.rb +32 -0
- data/lib/command.rb +62 -0
- data/lib/command/add.rb +65 -0
- data/lib/command/base.rb +92 -0
- data/lib/command/branch.rb +199 -0
- data/lib/command/checkout.rb +104 -0
- data/lib/command/cherry_pick.rb +51 -0
- data/lib/command/commit.rb +86 -0
- data/lib/command/config.rb +126 -0
- data/lib/command/diff.rb +114 -0
- data/lib/command/fetch.rb +116 -0
- data/lib/command/init.rb +41 -0
- data/lib/command/log.rb +188 -0
- data/lib/command/merge.rb +148 -0
- data/lib/command/push.rb +172 -0
- data/lib/command/receive_pack.rb +92 -0
- data/lib/command/remote.rb +55 -0
- data/lib/command/reset.rb +64 -0
- data/lib/command/rev_list.rb +33 -0
- data/lib/command/revert.rb +69 -0
- data/lib/command/rm.rb +105 -0
- data/lib/command/shared/fast_forward.rb +19 -0
- data/lib/command/shared/print_diff.rb +116 -0
- data/lib/command/shared/receive_objects.rb +37 -0
- data/lib/command/shared/remote_agent.rb +44 -0
- data/lib/command/shared/remote_client.rb +82 -0
- data/lib/command/shared/send_objects.rb +24 -0
- data/lib/command/shared/sequencing.rb +146 -0
- data/lib/command/shared/write_commit.rb +167 -0
- data/lib/command/status.rb +210 -0
- data/lib/command/upload_pack.rb +54 -0
- data/lib/config.rb +240 -0
- data/lib/config/stack.rb +42 -0
- data/lib/database.rb +112 -0
- data/lib/database/author.rb +27 -0
- data/lib/database/backends.rb +57 -0
- data/lib/database/blob.rb +24 -0
- data/lib/database/commit.rb +70 -0
- data/lib/database/entry.rb +7 -0
- data/lib/database/loose.rb +70 -0
- data/lib/database/packed.rb +75 -0
- data/lib/database/tree.rb +77 -0
- data/lib/database/tree_diff.rb +88 -0
- data/lib/diff.rb +46 -0
- data/lib/diff/combined.rb +72 -0
- data/lib/diff/hunk.rb +64 -0
- data/lib/diff/myers.rb +90 -0
- data/lib/editor.rb +59 -0
- data/lib/index.rb +212 -0
- data/lib/index/checksum.rb +44 -0
- data/lib/index/entry.rb +91 -0
- data/lib/lockfile.rb +55 -0
- data/lib/merge/bases.rb +38 -0
- data/lib/merge/common_ancestors.rb +77 -0
- data/lib/merge/diff3.rb +156 -0
- data/lib/merge/inputs.rb +42 -0
- data/lib/merge/resolve.rb +178 -0
- data/lib/pack.rb +45 -0
- data/lib/pack/compressor.rb +83 -0
- data/lib/pack/delta.rb +58 -0
- data/lib/pack/entry.rb +54 -0
- data/lib/pack/expander.rb +54 -0
- data/lib/pack/index.rb +100 -0
- data/lib/pack/indexer.rb +200 -0
- data/lib/pack/numbers.rb +79 -0
- data/lib/pack/reader.rb +98 -0
- data/lib/pack/stream.rb +80 -0
- data/lib/pack/unpacker.rb +62 -0
- data/lib/pack/window.rb +47 -0
- data/lib/pack/writer.rb +92 -0
- data/lib/pack/xdelta.rb +118 -0
- data/lib/pager.rb +24 -0
- data/lib/progress.rb +78 -0
- data/lib/refs.rb +260 -0
- data/lib/remotes.rb +82 -0
- data/lib/remotes/protocol.rb +82 -0
- data/lib/remotes/refspec.rb +70 -0
- data/lib/remotes/remote.rb +57 -0
- data/lib/repository.rb +64 -0
- data/lib/repository/divergence.rb +21 -0
- data/lib/repository/hard_reset.rb +35 -0
- data/lib/repository/inspector.rb +49 -0
- data/lib/repository/migration.rb +168 -0
- data/lib/repository/pending_commit.rb +60 -0
- data/lib/repository/sequencer.rb +118 -0
- data/lib/repository/status.rb +98 -0
- data/lib/rev_list.rb +244 -0
- data/lib/revision.rb +155 -0
- data/lib/sorted_hash.rb +17 -0
- data/lib/temp_file.rb +34 -0
- data/lib/workspace.rb +107 -0
- 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
|
data/lib/color.rb
ADDED
@@ -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
|
data/lib/command.rb
ADDED
@@ -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
|
data/lib/command/add.rb
ADDED
@@ -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
|
data/lib/command/base.rb
ADDED
@@ -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
|