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
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative "./base"
|
2
|
+
require_relative "../revision"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
class Checkout < Base
|
6
|
+
|
7
|
+
DETACHED_HEAD_MESSAGE = <<~MSG
|
8
|
+
You are in 'detached HEAD' state. You can look around, make experimental
|
9
|
+
changes and commit them, and you can discard any commits you make in this
|
10
|
+
state without impacting any branches by performing another checkout.
|
11
|
+
|
12
|
+
If you want to create a new branch to retain commits you create, you may
|
13
|
+
do so (now or later) by using the branch command. Example:
|
14
|
+
|
15
|
+
jit branch <new-branch-name>
|
16
|
+
MSG
|
17
|
+
|
18
|
+
def run
|
19
|
+
@target = @args[0]
|
20
|
+
|
21
|
+
@current_ref = repo.refs.current_ref
|
22
|
+
@current_oid = @current_ref.read_oid
|
23
|
+
|
24
|
+
revision = Revision.new(repo, @target)
|
25
|
+
@target_oid = revision.resolve(Revision::COMMIT)
|
26
|
+
|
27
|
+
repo.index.load_for_update
|
28
|
+
|
29
|
+
tree_diff = repo.database.tree_diff(@current_oid, @target_oid)
|
30
|
+
migration = repo.migration(tree_diff)
|
31
|
+
migration.apply_changes
|
32
|
+
|
33
|
+
repo.index.write_updates
|
34
|
+
repo.refs.set_head(@target, @target_oid)
|
35
|
+
@new_ref = repo.refs.current_ref
|
36
|
+
|
37
|
+
print_previous_head
|
38
|
+
print_detachment_notice
|
39
|
+
print_new_head
|
40
|
+
|
41
|
+
exit 0
|
42
|
+
|
43
|
+
rescue Repository::Migration::Conflict
|
44
|
+
handle_migration_conflict(migration)
|
45
|
+
|
46
|
+
rescue Revision::InvalidObject => error
|
47
|
+
handle_invalid_object(revision, error)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def handle_migration_conflict(migration)
|
53
|
+
repo.index.release_lock
|
54
|
+
|
55
|
+
migration.errors.each do |message|
|
56
|
+
@stderr.puts "error: #{ message }"
|
57
|
+
end
|
58
|
+
@stderr.puts "Aborting"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_invalid_object(revision, error)
|
63
|
+
revision.errors.each do |err|
|
64
|
+
@stderr.puts "error: #{ err.message }"
|
65
|
+
err.hint.each { |line| @stderr.puts "hint: #{ line }" }
|
66
|
+
end
|
67
|
+
@stderr.puts "error: #{ error.message }"
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def print_previous_head
|
72
|
+
if @current_ref.head? and @current_oid != @target_oid
|
73
|
+
print_head_position("Previous HEAD position was", @current_oid)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_detachment_notice
|
78
|
+
return unless @new_ref.head? and not @current_ref.head?
|
79
|
+
|
80
|
+
@stderr.puts "Note: checking out '#{ @target }'."
|
81
|
+
@stderr.puts ""
|
82
|
+
@stderr.puts DETACHED_HEAD_MESSAGE
|
83
|
+
@stderr.puts ""
|
84
|
+
end
|
85
|
+
|
86
|
+
def print_new_head
|
87
|
+
if @new_ref.head?
|
88
|
+
print_head_position("HEAD is now at", @target_oid)
|
89
|
+
elsif @new_ref == @current_ref
|
90
|
+
@stderr.puts "Already on '#{ @target }'"
|
91
|
+
else
|
92
|
+
@stderr.puts "Switched to branch '#{ @target }'"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_head_position(message, oid)
|
97
|
+
commit = repo.database.load(oid)
|
98
|
+
short = repo.database.short_oid(commit.oid)
|
99
|
+
|
100
|
+
@stderr.puts "#{ message } #{ short } #{ commit.title_line }"
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative "./base"
|
2
|
+
require_relative "./shared/sequencing"
|
3
|
+
require_relative "./shared/write_commit"
|
4
|
+
require_relative "../merge/inputs"
|
5
|
+
require_relative "../rev_list"
|
6
|
+
|
7
|
+
module Command
|
8
|
+
class CherryPick < Base
|
9
|
+
|
10
|
+
include Sequencing
|
11
|
+
include WriteCommit
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def merge_type
|
16
|
+
:cherry_pick
|
17
|
+
end
|
18
|
+
|
19
|
+
def store_commit_sequence
|
20
|
+
commits = ::RevList.new(repo, @args.reverse, :walk => false)
|
21
|
+
commits.reverse_each { |commit| sequencer.pick(commit) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def pick(commit)
|
25
|
+
inputs = pick_merge_inputs(commit)
|
26
|
+
resolve_merge(inputs)
|
27
|
+
fail_on_conflict(inputs, commit.message) if repo.index.conflict?
|
28
|
+
|
29
|
+
picked = Database::Commit.new([inputs.left_oid], write_tree.oid,
|
30
|
+
commit.author, current_author,
|
31
|
+
commit.message)
|
32
|
+
|
33
|
+
finish_commit(picked)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pick_merge_inputs(commit)
|
37
|
+
short = repo.database.short_oid(commit.oid)
|
38
|
+
parent = select_parent(commit)
|
39
|
+
|
40
|
+
left_name = Refs::HEAD
|
41
|
+
left_oid = repo.refs.read_head
|
42
|
+
right_name = "#{ short }... #{ commit.title_line.strip }"
|
43
|
+
right_oid = commit.oid
|
44
|
+
|
45
|
+
::Merge::CherryPick.new(left_name, right_name,
|
46
|
+
left_oid, right_oid,
|
47
|
+
[parent])
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require_relative "./base"
|
3
|
+
require_relative "./shared/write_commit"
|
4
|
+
require_relative "../revision"
|
5
|
+
|
6
|
+
module Command
|
7
|
+
class Commit < Base
|
8
|
+
|
9
|
+
include WriteCommit
|
10
|
+
|
11
|
+
COMMIT_NOTES = <<~MSG
|
12
|
+
Please enter the commit message for your changes. Lines starting
|
13
|
+
with '#' will be ignored, and an empty message aborts the commit.
|
14
|
+
MSG
|
15
|
+
|
16
|
+
def define_options
|
17
|
+
define_write_commit_options
|
18
|
+
|
19
|
+
@parser.on("--amend") { @options[:amend] = true }
|
20
|
+
|
21
|
+
@parser.on "-C <commit>", "--reuse-message=<commit>" do |commit|
|
22
|
+
@options[:reuse] = commit
|
23
|
+
@options[:edit] = false
|
24
|
+
end
|
25
|
+
|
26
|
+
@parser.on "-c <commit>", "--reedit-message=<commit>" do |commit|
|
27
|
+
@options[:reuse] = commit
|
28
|
+
@options[:edit] = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def run
|
33
|
+
repo.index.load
|
34
|
+
|
35
|
+
handle_amend if @options[:amend]
|
36
|
+
|
37
|
+
merge_type = pending_commit.merge_type
|
38
|
+
resume_merge(merge_type) if merge_type
|
39
|
+
|
40
|
+
parent = repo.refs.read_head
|
41
|
+
message = compose_message(read_message || reused_message)
|
42
|
+
commit = write_commit([*parent], message)
|
43
|
+
|
44
|
+
print_commit(commit)
|
45
|
+
|
46
|
+
exit 0
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def compose_message(message)
|
52
|
+
edit_file(commit_message_path) do |editor|
|
53
|
+
editor.puts(message || "")
|
54
|
+
editor.puts("")
|
55
|
+
editor.note(COMMIT_NOTES)
|
56
|
+
|
57
|
+
editor.close unless @options[:edit]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def reused_message
|
62
|
+
return nil unless @options.has_key?(:reuse)
|
63
|
+
|
64
|
+
revision = Revision.new(repo, @options[:reuse])
|
65
|
+
commit = repo.database.load(revision.resolve)
|
66
|
+
|
67
|
+
commit.message
|
68
|
+
end
|
69
|
+
|
70
|
+
def handle_amend
|
71
|
+
old = repo.database.load(repo.refs.read_head)
|
72
|
+
tree = write_tree
|
73
|
+
|
74
|
+
message = compose_message(old.message)
|
75
|
+
committer = current_author
|
76
|
+
|
77
|
+
new = Database::Commit.new(old.parents, tree.oid, old.author, committer, message)
|
78
|
+
repo.database.store(new)
|
79
|
+
repo.refs.update_head(new.oid)
|
80
|
+
|
81
|
+
print_commit(new)
|
82
|
+
exit 0
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative "./base"
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class Config < Base
|
5
|
+
|
6
|
+
def define_options
|
7
|
+
@parser.on("--local") { @options[:file] = :local }
|
8
|
+
@parser.on("--global") { @options[:file] = :global }
|
9
|
+
@parser.on("--system") { @options[:file] = :system }
|
10
|
+
|
11
|
+
@parser.on "-f <config-file>", "--file=<config-file>" do |file|
|
12
|
+
@options[:file] = file
|
13
|
+
end
|
14
|
+
|
15
|
+
@parser.on("--add <name>") { |name| @options[:add] = name }
|
16
|
+
@parser.on("--replace-all <name>") { |name| @options[:replace] = name }
|
17
|
+
@parser.on("--get-all <name>") { |name| @options[:get_all] = name }
|
18
|
+
@parser.on("--unset <name>") { |name| @options[:unset] = name }
|
19
|
+
@parser.on("--unset-all <name>") { |name| @options[:unset_all] = name }
|
20
|
+
|
21
|
+
@parser.on "--remove-section <name>" do |name|
|
22
|
+
@options[:remove_section] = name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
add_variable if @options[:add]
|
28
|
+
replace_variable if @options[:replace]
|
29
|
+
get_all_values if @options[:get_all]
|
30
|
+
unset_single if @options[:unset]
|
31
|
+
unset_all if @options[:unset_all]
|
32
|
+
remove_section if @options[:remove_section]
|
33
|
+
|
34
|
+
key, value = parse_key(@args[0]), @args[1]
|
35
|
+
|
36
|
+
if value
|
37
|
+
edit_config { |config| config.set(key, value) }
|
38
|
+
else
|
39
|
+
read_config { |config| [*config.get(key)] }
|
40
|
+
end
|
41
|
+
|
42
|
+
rescue ::Config::ParseError => error
|
43
|
+
@stderr.puts "error: #{ error.message }"
|
44
|
+
exit 3
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def add_variable
|
50
|
+
key = parse_key(@options[:add])
|
51
|
+
edit_config { |config| config.add(key, @args[0]) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def replace_variable
|
55
|
+
key = parse_key(@options[:replace])
|
56
|
+
edit_config { |config| config.replace_all(key, @args[0]) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def unset_single
|
60
|
+
key = parse_key(@options[:unset])
|
61
|
+
edit_config { |config| config.unset(key) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def unset_all
|
65
|
+
key = parse_key(@options[:unset_all])
|
66
|
+
edit_config { |config| config.unset_all(key) }
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_section
|
70
|
+
key = @options[:remove_section].split(".", 2)
|
71
|
+
edit_config { |config| config.remove_section(key) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_all_values
|
75
|
+
key = parse_key(@options[:get_all])
|
76
|
+
read_config { |config| config.get_all(key) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def read_config
|
80
|
+
config = repo.config
|
81
|
+
config = config.file(@options[:file]) if @options[:file]
|
82
|
+
|
83
|
+
config.open
|
84
|
+
values = yield config
|
85
|
+
|
86
|
+
exit 1 if values.empty?
|
87
|
+
|
88
|
+
values.each { |value| puts value }
|
89
|
+
exit 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def edit_config
|
93
|
+
config = repo.config.file(@options.fetch(:file, :local))
|
94
|
+
config.open_for_update
|
95
|
+
yield config
|
96
|
+
config.save
|
97
|
+
|
98
|
+
exit 0
|
99
|
+
|
100
|
+
rescue ::Config::Conflict => error
|
101
|
+
@stderr.puts "error: #{ error.message }"
|
102
|
+
exit 5
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse_key(name)
|
106
|
+
section, *subsection, var = name.split(".")
|
107
|
+
|
108
|
+
unless var
|
109
|
+
@stderr.puts "error: key does not contain a section: #{ name }"
|
110
|
+
exit 2
|
111
|
+
end
|
112
|
+
|
113
|
+
unless ::Config.valid_key?([section, var])
|
114
|
+
@stderr.puts "error: invalid key: #{ name }"
|
115
|
+
exit 1
|
116
|
+
end
|
117
|
+
|
118
|
+
if subsection.empty?
|
119
|
+
[section, var]
|
120
|
+
else
|
121
|
+
[section, subsection.join("."), var]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
data/lib/command/diff.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative "./base"
|
2
|
+
require_relative "./shared/print_diff"
|
3
|
+
|
4
|
+
module Command
|
5
|
+
class Diff < Base
|
6
|
+
|
7
|
+
include PrintDiff
|
8
|
+
|
9
|
+
def define_options
|
10
|
+
@options[:patch] = true
|
11
|
+
define_print_diff_options
|
12
|
+
|
13
|
+
@parser.on "--cached", "--staged" do
|
14
|
+
@options[:cached] = true
|
15
|
+
end
|
16
|
+
|
17
|
+
@parser.on("-1", "--base") { @options[:stage] = 1 }
|
18
|
+
@parser.on("-2", "--ours") { @options[:stage] = 2 }
|
19
|
+
@parser.on("-3", "--theirs") { @options[:stage] = 3 }
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
repo.index.load
|
24
|
+
@status = repo.status
|
25
|
+
|
26
|
+
setup_pager
|
27
|
+
|
28
|
+
if @options[:cached]
|
29
|
+
diff_head_index
|
30
|
+
else
|
31
|
+
diff_index_workspace
|
32
|
+
end
|
33
|
+
|
34
|
+
exit 0
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def diff_head_index
|
40
|
+
return unless @options[:patch]
|
41
|
+
|
42
|
+
@status.index_changes.each do |path, state|
|
43
|
+
case state
|
44
|
+
when :added then print_diff(from_nothing(path), from_index(path))
|
45
|
+
when :modified then print_diff(from_head(path), from_index(path))
|
46
|
+
when :deleted then print_diff(from_head(path), from_nothing(path))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def diff_index_workspace
|
52
|
+
return unless @options[:patch]
|
53
|
+
|
54
|
+
paths = @status.conflicts.keys + @status.workspace_changes.keys
|
55
|
+
|
56
|
+
paths.sort.each do |path|
|
57
|
+
if @status.conflicts.has_key?(path)
|
58
|
+
print_conflict_diff(path)
|
59
|
+
else
|
60
|
+
print_workspace_diff(path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def print_conflict_diff(path)
|
66
|
+
targets = (0..3).map { |stage| from_index(path, stage) }
|
67
|
+
left, right = targets[2], targets[3]
|
68
|
+
|
69
|
+
if @options[:stage]
|
70
|
+
puts "* Unmerged path #{ path }"
|
71
|
+
print_diff(targets[@options[:stage]], from_file(path))
|
72
|
+
elsif left and right
|
73
|
+
print_combined_diff([left, right], from_file(path))
|
74
|
+
else
|
75
|
+
puts "* Unmerged path #{ path }"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_workspace_diff(path)
|
80
|
+
case @status.workspace_changes[path]
|
81
|
+
when :modified then print_diff(from_index(path), from_file(path))
|
82
|
+
when :deleted then print_diff(from_index(path), from_nothing(path))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def from_head(path)
|
87
|
+
entry = @status.head_tree.fetch(path)
|
88
|
+
blob = repo.database.load(entry.oid)
|
89
|
+
|
90
|
+
Target.new(path, entry.oid, entry.mode.to_s(8), blob.data)
|
91
|
+
end
|
92
|
+
|
93
|
+
def from_index(path, stage = 0)
|
94
|
+
entry = repo.index.entry_for_path(path, stage)
|
95
|
+
return nil unless entry
|
96
|
+
|
97
|
+
blob = repo.database.load(entry.oid)
|
98
|
+
Target.new(path, entry.oid, entry.mode.to_s(8), blob.data)
|
99
|
+
end
|
100
|
+
|
101
|
+
def from_file(path)
|
102
|
+
blob = Database::Blob.new(repo.workspace.read_file(path))
|
103
|
+
oid = repo.database.hash_object(blob)
|
104
|
+
mode = Index::Entry.mode_for_stat(@status.stats[path])
|
105
|
+
|
106
|
+
Target.new(path, oid, mode.to_s(8), blob.data)
|
107
|
+
end
|
108
|
+
|
109
|
+
def from_nothing(path)
|
110
|
+
Target.new(path, NULL_OID, nil, "")
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|