jit 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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