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