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