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/lib/command/rm.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require_relative "./base"
|
4
|
+
require_relative "../repository/inspector"
|
5
|
+
|
6
|
+
module Command
|
7
|
+
class Rm < Base
|
8
|
+
|
9
|
+
BOTH_CHANGED = "staged content different from both the file and the HEAD"
|
10
|
+
INDEX_CHANGED = "changes staged in the index"
|
11
|
+
WORKSPACE_CHANGED = "local modifications"
|
12
|
+
|
13
|
+
def define_options
|
14
|
+
@parser.on("--cached") { @options[:cached] = true }
|
15
|
+
@parser.on("-f", "--force") { @options[:force] = true }
|
16
|
+
@parser.on("-r") { @options[:recursive] = true }
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
repo.index.load_for_update
|
21
|
+
|
22
|
+
@head_oid = repo.refs.read_head
|
23
|
+
@inspector = Repository::Inspector.new(repo)
|
24
|
+
@uncommitted = []
|
25
|
+
@unstaged = []
|
26
|
+
@both_changed = []
|
27
|
+
|
28
|
+
@args = @args.flat_map { |path| expand_path(path) }
|
29
|
+
.map { |path| Pathname.new(path) }
|
30
|
+
|
31
|
+
@args.each { |path| plan_removal(path) }
|
32
|
+
exit_on_errors
|
33
|
+
|
34
|
+
@args.each { |path| remove_file(path) }
|
35
|
+
repo.index.write_updates
|
36
|
+
|
37
|
+
exit 0
|
38
|
+
|
39
|
+
rescue => error
|
40
|
+
repo.index.release_lock
|
41
|
+
@stderr.puts "fatal: #{ error.message }"
|
42
|
+
exit 128
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def expand_path(path)
|
48
|
+
if repo.index.tracked_directory?(path)
|
49
|
+
return repo.index.child_paths(path) if @options[:recursive]
|
50
|
+
raise "not removing '#{ path }' recursively without -r"
|
51
|
+
end
|
52
|
+
|
53
|
+
return [path] if repo.index.tracked_file?(path)
|
54
|
+
raise "pathspec '#{ path }' did not match any files"
|
55
|
+
end
|
56
|
+
|
57
|
+
def plan_removal(path)
|
58
|
+
return if @options[:force]
|
59
|
+
|
60
|
+
stat = repo.workspace.stat_file(path)
|
61
|
+
raise "jit rm: '#{ path }': Operation not permitted" if stat&.directory?
|
62
|
+
|
63
|
+
item = repo.database.load_tree_entry(@head_oid, path)
|
64
|
+
entry = repo.index.entry_for_path(path)
|
65
|
+
|
66
|
+
staged_change = @inspector.compare_tree_to_index(item, entry)
|
67
|
+
unstaged_change = @inspector.compare_index_to_workspace(entry, stat) if stat
|
68
|
+
|
69
|
+
if staged_change and unstaged_change
|
70
|
+
@both_changed.push(path)
|
71
|
+
elsif staged_change
|
72
|
+
@uncommitted.push(path) unless @options[:cached]
|
73
|
+
elsif unstaged_change
|
74
|
+
@unstaged.push(path) unless @options[:cached]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_file(path)
|
79
|
+
repo.index.remove(path)
|
80
|
+
repo.workspace.remove(path) unless @options[:cached]
|
81
|
+
puts "rm '#{ path }'"
|
82
|
+
end
|
83
|
+
|
84
|
+
def exit_on_errors
|
85
|
+
return if [@both_changed, @uncommitted, @unstaged].all?(&:empty?)
|
86
|
+
|
87
|
+
print_errors(@both_changed, BOTH_CHANGED)
|
88
|
+
print_errors(@uncommitted, INDEX_CHANGED)
|
89
|
+
print_errors(@unstaged, WORKSPACE_CHANGED)
|
90
|
+
|
91
|
+
repo.index.release_lock
|
92
|
+
exit 1
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_errors(paths, message)
|
96
|
+
return if paths.empty?
|
97
|
+
|
98
|
+
files_have = (paths.size == 1) ? "file has" : "files have"
|
99
|
+
|
100
|
+
@stderr.puts "error: the following #{ files_have } #{ message }:"
|
101
|
+
paths.each { |path| @stderr.puts " #{ path }" }
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../../merge/common_ancestors"
|
2
|
+
|
3
|
+
module Command
|
4
|
+
module FastForward
|
5
|
+
|
6
|
+
def fast_forward_error(old_oid, new_oid)
|
7
|
+
return nil unless old_oid and new_oid
|
8
|
+
return "fetch first" unless repo.database.has?(old_oid)
|
9
|
+
return "non-fast-forward" unless fast_forward?(old_oid, new_oid)
|
10
|
+
end
|
11
|
+
|
12
|
+
def fast_forward?(old_oid, new_oid)
|
13
|
+
common = ::Merge::CommonAncestors.new(repo.database, old_oid, [new_oid])
|
14
|
+
common.find
|
15
|
+
common.marked?(old_oid, :parent2)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require_relative "../../diff"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
module PrintDiff
|
6
|
+
|
7
|
+
DIFF_FORMATS = {
|
8
|
+
:context => :normal,
|
9
|
+
:meta => :bold,
|
10
|
+
:frag => :cyan,
|
11
|
+
:old => :red,
|
12
|
+
:new => :green
|
13
|
+
}
|
14
|
+
|
15
|
+
NULL_OID = "0" * 40
|
16
|
+
NULL_PATH = "/dev/null"
|
17
|
+
|
18
|
+
Target = Struct.new(:path, :oid, :mode, :data) do
|
19
|
+
def diff_path
|
20
|
+
mode ? path : NULL_PATH
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_print_diff_options
|
25
|
+
@parser.on("-p", "-u", "--patch") { @options[:patch] = true }
|
26
|
+
@parser.on("-s", "--no-patch") { @options[:patch] = false }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def diff_fmt(name, text)
|
32
|
+
key = ["color", "diff", name]
|
33
|
+
style = repo.config.get(key)&.split(/ +/) || DIFF_FORMATS.fetch(name)
|
34
|
+
|
35
|
+
fmt(style, text)
|
36
|
+
end
|
37
|
+
|
38
|
+
def header(string)
|
39
|
+
puts diff_fmt(:meta, string)
|
40
|
+
end
|
41
|
+
|
42
|
+
def short(oid)
|
43
|
+
repo.database.short_oid(oid)
|
44
|
+
end
|
45
|
+
|
46
|
+
def print_diff(a, b)
|
47
|
+
return if a.oid == b.oid and a.mode == b.mode
|
48
|
+
|
49
|
+
a.path = Pathname.new("a").join(a.path)
|
50
|
+
b.path = Pathname.new("b").join(b.path)
|
51
|
+
|
52
|
+
header("diff --git #{ a.path } #{ b.path }")
|
53
|
+
print_diff_mode(a, b)
|
54
|
+
print_diff_content(a, b)
|
55
|
+
end
|
56
|
+
|
57
|
+
def print_diff_mode(a, b)
|
58
|
+
if a.mode == nil
|
59
|
+
header("new file mode #{ b.mode }")
|
60
|
+
elsif b.mode == nil
|
61
|
+
header("deleted file mode #{ a.mode }")
|
62
|
+
elsif a.mode != b.mode
|
63
|
+
header("old mode #{ a.mode }")
|
64
|
+
header("new mode #{ b.mode }")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def print_diff_content(a, b)
|
69
|
+
return if a.oid == b.oid
|
70
|
+
|
71
|
+
oid_range = "index #{ short a.oid }..#{ short b.oid }"
|
72
|
+
oid_range.concat(" #{ a.mode }") if a.mode == b.mode
|
73
|
+
|
74
|
+
header(oid_range)
|
75
|
+
header("--- #{ a.diff_path }")
|
76
|
+
header("+++ #{ b.diff_path }")
|
77
|
+
|
78
|
+
hunks = ::Diff.diff_hunks(a.data, b.data)
|
79
|
+
hunks.each { |hunk| print_diff_hunk(hunk) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def print_combined_diff(as, b)
|
83
|
+
header("diff --cc #{ b.path }")
|
84
|
+
|
85
|
+
a_oids = as.map { |a| short a.oid }
|
86
|
+
oid_range = "index #{ a_oids.join(",") }..#{ short b.oid }"
|
87
|
+
header(oid_range)
|
88
|
+
|
89
|
+
unless as.all? { |a| a.mode == b.mode }
|
90
|
+
header("mode #{ as.map(&:mode).join(",") }..#{ b.mode }")
|
91
|
+
end
|
92
|
+
|
93
|
+
header("--- a/#{ b.diff_path }")
|
94
|
+
header("+++ b/#{ b.diff_path }")
|
95
|
+
|
96
|
+
hunks = ::Diff.combined_hunks(as.map(&:data), b.data)
|
97
|
+
hunks.each { |hunk| print_diff_hunk(hunk) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_diff_hunk(hunk)
|
101
|
+
puts diff_fmt(:frag, hunk.header)
|
102
|
+
hunk.edits.each { |edit| print_diff_edit(edit) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def print_diff_edit(edit)
|
106
|
+
text = edit.to_s.rstrip
|
107
|
+
|
108
|
+
case edit.type
|
109
|
+
when :eql then puts diff_fmt(:context, text)
|
110
|
+
when :ins then puts diff_fmt(:new, text)
|
111
|
+
when :del then puts diff_fmt(:old, text)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "../../pack"
|
2
|
+
require_relative "../../progress"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
module ReceiveObjects
|
6
|
+
|
7
|
+
UNPACK_LIMIT = 100
|
8
|
+
|
9
|
+
def recv_packed_objects(unpack_limit = nil, prefix = "")
|
10
|
+
stream = Pack::Stream.new(@conn.input, prefix)
|
11
|
+
reader = Pack::Reader.new(stream)
|
12
|
+
progress = Progress.new(@stderr) unless @conn.input == STDIN
|
13
|
+
|
14
|
+
reader.read_header
|
15
|
+
|
16
|
+
factory = select_processor_class(reader, unpack_limit)
|
17
|
+
processor = factory.new(repo.database, reader, stream, progress)
|
18
|
+
|
19
|
+
processor.process_pack
|
20
|
+
end
|
21
|
+
|
22
|
+
def select_processor_class(reader, unpack_limit)
|
23
|
+
unpack_limit ||= transfer_unpack_limit
|
24
|
+
|
25
|
+
if unpack_limit and reader.count > unpack_limit
|
26
|
+
Pack::Indexer
|
27
|
+
else
|
28
|
+
Pack::Unpacker
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def transfer_unpack_limit
|
33
|
+
repo.config.get(["transfer", "unpackLimit"]) || UNPACK_LIMIT
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative "../../repository"
|
2
|
+
require_relative "../../remotes/protocol"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
module RemoteAgent
|
6
|
+
|
7
|
+
ZERO_OID = "0" * 40
|
8
|
+
|
9
|
+
def accept_client(name, capabilities = [])
|
10
|
+
@conn = Remotes::Protocol.new(name, @stdin, @stdout, capabilities)
|
11
|
+
end
|
12
|
+
|
13
|
+
def repo
|
14
|
+
@repo ||= Repository.new(detect_git_dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
def detect_git_dir
|
18
|
+
pathname = expanded_pathname(@args[0])
|
19
|
+
dirs = pathname.ascend.flat_map { |dir| [dir, dir.join(".git")] }
|
20
|
+
dirs.find { |dir| git_repository?(dir) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def git_repository?(dirname)
|
24
|
+
File.file?(dirname.join("HEAD")) and
|
25
|
+
File.directory?(dirname.join("objects")) and
|
26
|
+
File.directory?(dirname.join("refs"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_references
|
30
|
+
refs = repo.refs.list_all_refs
|
31
|
+
sent = false
|
32
|
+
|
33
|
+
refs.sort_by(&:path).each do |symref|
|
34
|
+
next unless oid = symref.read_oid
|
35
|
+
@conn.send_packet("#{ oid.downcase } #{ symref.path }")
|
36
|
+
sent = true
|
37
|
+
end
|
38
|
+
|
39
|
+
@conn.send_packet("#{ ZERO_OID } capabilities^{}") unless sent
|
40
|
+
@conn.send_packet(nil)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "shellwords"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
require_relative "../../remotes/protocol"
|
6
|
+
|
7
|
+
module Command
|
8
|
+
module RemoteClient
|
9
|
+
|
10
|
+
REF_LINE = /^([0-9a-f]+) (.*)$/
|
11
|
+
ZERO_OID = "0" * 40
|
12
|
+
|
13
|
+
def start_agent(name, program, url, capabilities = [])
|
14
|
+
argv = build_agent_command(program, url)
|
15
|
+
input, output, _ = Open3.popen2(Shellwords.shelljoin(argv))
|
16
|
+
@conn = Remotes::Protocol.new(name, output, input, capabilities)
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_agent_command(program, url)
|
20
|
+
uri = URI.parse(url)
|
21
|
+
argv = Shellwords.shellsplit(program) + [uri.path]
|
22
|
+
|
23
|
+
case uri.scheme
|
24
|
+
when "file" then argv
|
25
|
+
when "ssh" then ssh_command(uri, argv)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ssh_command(uri, argv)
|
30
|
+
ssh = ["ssh", uri.host]
|
31
|
+
ssh += ["-p", uri.port.to_s] if uri.port
|
32
|
+
ssh += ["-l", uri.user] if uri.user
|
33
|
+
|
34
|
+
ssh + [Shellwords.shelljoin(argv)]
|
35
|
+
end
|
36
|
+
|
37
|
+
def recv_references
|
38
|
+
@remote_refs = {}
|
39
|
+
|
40
|
+
@conn.recv_until(nil) do |line|
|
41
|
+
oid, ref = REF_LINE.match(line).captures
|
42
|
+
@remote_refs[ref] = oid.downcase unless oid == ZERO_OID
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def report_ref_update(ref_names, error, old_oid = nil, new_oid = nil, is_ff = false)
|
47
|
+
return show_ref_update("!", "[rejected]", ref_names, error) if error
|
48
|
+
return if old_oid == new_oid
|
49
|
+
|
50
|
+
if old_oid == nil
|
51
|
+
show_ref_update("*", "[new branch]", ref_names)
|
52
|
+
elsif new_oid == nil
|
53
|
+
show_ref_update("-", "[deleted]", ref_names)
|
54
|
+
else
|
55
|
+
report_range_update(ref_names, old_oid, new_oid, is_ff)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def report_range_update(ref_names, old_oid, new_oid, is_ff)
|
60
|
+
old_oid = repo.database.short_oid(old_oid)
|
61
|
+
new_oid = repo.database.short_oid(new_oid)
|
62
|
+
|
63
|
+
if is_ff
|
64
|
+
revisions = "#{ old_oid }..#{ new_oid }"
|
65
|
+
show_ref_update(" ", revisions, ref_names)
|
66
|
+
else
|
67
|
+
revisions = "#{ old_oid }...#{ new_oid }"
|
68
|
+
show_ref_update("+", revisions, ref_names, "forced update")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def show_ref_update(flag, summary, ref_names, reason = nil)
|
73
|
+
names = ref_names.compact.map { |name| repo.refs.short_name(name) }
|
74
|
+
|
75
|
+
message = " #{ flag } #{ summary } #{ names.join(" -> ") }"
|
76
|
+
message.concat(" (#{ reason })") if reason
|
77
|
+
|
78
|
+
@stderr.puts message
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "../../pack"
|
2
|
+
require_relative "../../progress"
|
3
|
+
require_relative "../../rev_list"
|
4
|
+
|
5
|
+
module Command
|
6
|
+
module SendObjects
|
7
|
+
|
8
|
+
def send_packed_objects(revs)
|
9
|
+
rev_opts = { :objects => true, :missing => true }
|
10
|
+
rev_list = ::RevList.new(repo, revs, rev_opts)
|
11
|
+
|
12
|
+
pack_compression = repo.config.get(["pack", "compression"]) ||
|
13
|
+
repo.config.get(["core", "compression"])
|
14
|
+
|
15
|
+
writer = Pack::Writer.new(@conn.output, repo.database,
|
16
|
+
:compression => pack_compression,
|
17
|
+
:allow_ofs => @conn.capable?("ofs-delta"),
|
18
|
+
:progress => Progress.new(@stderr))
|
19
|
+
|
20
|
+
writer.write_objects(rev_list)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require_relative "../../merge/resolve"
|
2
|
+
require_relative "../../repository/sequencer"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
module Sequencing
|
6
|
+
|
7
|
+
CONFLICT_NOTES = <<~MSG
|
8
|
+
after resolving the conflicts, mark the corrected paths
|
9
|
+
with 'jit add <paths>' or 'jit rm <paths>'
|
10
|
+
and commit the result with 'jit commit'
|
11
|
+
MSG
|
12
|
+
|
13
|
+
def define_options
|
14
|
+
@options[:mode] = :run
|
15
|
+
|
16
|
+
@parser.on("--continue") { @options[:mode] = :continue }
|
17
|
+
@parser.on("--abort") { @options[:mode] = :abort }
|
18
|
+
@parser.on("--quit" ) { @options[:mode] = :quit }
|
19
|
+
|
20
|
+
@parser.on "-m <parent>", "--mainline=<parent>", Integer do |parent|
|
21
|
+
@options[:mainline] = parent
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
case @options[:mode]
|
27
|
+
when :continue then handle_continue
|
28
|
+
when :abort then handle_abort
|
29
|
+
when :quit then handle_quit
|
30
|
+
end
|
31
|
+
|
32
|
+
sequencer.start(@options)
|
33
|
+
store_commit_sequence
|
34
|
+
resume_sequencer
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def sequencer
|
40
|
+
@sequencer ||= Repository::Sequencer.new(repo)
|
41
|
+
end
|
42
|
+
|
43
|
+
def resolve_merge(inputs)
|
44
|
+
repo.index.load_for_update
|
45
|
+
::Merge::Resolve.new(repo, inputs).execute
|
46
|
+
repo.index.write_updates
|
47
|
+
end
|
48
|
+
|
49
|
+
def fail_on_conflict(inputs, message)
|
50
|
+
sequencer.dump
|
51
|
+
pending_commit.start(inputs.right_oid, merge_type)
|
52
|
+
|
53
|
+
edit_file(pending_commit.message_path) do |editor|
|
54
|
+
editor.puts(message)
|
55
|
+
editor.puts("")
|
56
|
+
editor.note("Conflicts:")
|
57
|
+
repo.index.conflict_paths.each { |name| editor.note("\t#{ name }") }
|
58
|
+
editor.close
|
59
|
+
end
|
60
|
+
|
61
|
+
@stderr.puts "error: could not apply #{ inputs.right_name }"
|
62
|
+
CONFLICT_NOTES.each_line { |line| @stderr.puts "hint: #{ line }" }
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def finish_commit(commit)
|
67
|
+
repo.database.store(commit)
|
68
|
+
repo.refs.update_head(commit.oid)
|
69
|
+
print_commit(commit)
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_continue
|
73
|
+
repo.index.load
|
74
|
+
|
75
|
+
case pending_commit.merge_type
|
76
|
+
when :cherry_pick then write_cherry_pick_commit
|
77
|
+
when :revert then write_revert_commit
|
78
|
+
end
|
79
|
+
|
80
|
+
sequencer.load
|
81
|
+
sequencer.drop_command
|
82
|
+
resume_sequencer
|
83
|
+
|
84
|
+
rescue Repository::PendingCommit::Error => error
|
85
|
+
@stderr.puts "fatal: #{ error.message }"
|
86
|
+
exit 128
|
87
|
+
end
|
88
|
+
|
89
|
+
def resume_sequencer
|
90
|
+
loop do
|
91
|
+
action, commit = sequencer.next_command
|
92
|
+
break unless commit
|
93
|
+
|
94
|
+
case action
|
95
|
+
when :pick then pick(commit)
|
96
|
+
when :revert then revert(commit)
|
97
|
+
end
|
98
|
+
sequencer.drop_command
|
99
|
+
end
|
100
|
+
|
101
|
+
sequencer.quit
|
102
|
+
exit 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def select_parent(commit)
|
106
|
+
mainline = sequencer.get_option("mainline")
|
107
|
+
|
108
|
+
if commit.merge?
|
109
|
+
return commit.parents[mainline - 1] if mainline
|
110
|
+
|
111
|
+
@stderr.puts <<~ERROR
|
112
|
+
error: commit #{ commit.oid } is a merge but no -m option was given
|
113
|
+
ERROR
|
114
|
+
exit 1
|
115
|
+
else
|
116
|
+
return commit.parent unless mainline
|
117
|
+
|
118
|
+
@stderr.puts <<~ERROR
|
119
|
+
error: mainline was specified but commit #{ commit.oid } is not a merge
|
120
|
+
ERROR
|
121
|
+
exit 1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def handle_abort
|
126
|
+
pending_commit.clear(merge_type) if pending_commit.in_progress?
|
127
|
+
repo.index.load_for_update
|
128
|
+
|
129
|
+
begin
|
130
|
+
sequencer.abort
|
131
|
+
rescue => error
|
132
|
+
@stderr.puts "warning: #{ error.message }"
|
133
|
+
end
|
134
|
+
|
135
|
+
repo.index.write_updates
|
136
|
+
exit 0
|
137
|
+
end
|
138
|
+
|
139
|
+
def handle_quit
|
140
|
+
pending_commit.clear(merge_type) if pending_commit.in_progress?
|
141
|
+
sequencer.quit
|
142
|
+
exit 0
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|