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,82 @@
1
+ require_relative "./refs"
2
+ require_relative "./remotes/refspec"
3
+ require_relative "./remotes/remote"
4
+
5
+ class Remotes
6
+ DEFAULT_REMOTE = "origin"
7
+
8
+ InvalidBranch = Class.new(StandardError)
9
+ InvalidRemote = Class.new(StandardError)
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def add(name, url, branches = [])
16
+ branches = ["*"] if branches.empty?
17
+ @config.open_for_update
18
+
19
+ if @config.get(["remote", name, "url"])
20
+ @config.save
21
+ raise InvalidRemote, "remote #{ name } already exists."
22
+ end
23
+
24
+ @config.set(["remote", name, "url"], url)
25
+
26
+ branches.each do |branch|
27
+ source = Refs::HEADS_DIR.join(branch)
28
+ target = Refs::REMOTES_DIR.join(name, branch)
29
+ refspec = Refspec.new(source, target, true)
30
+
31
+ @config.add(["remote", name, "fetch"], refspec.to_s)
32
+ end
33
+
34
+ @config.save
35
+ end
36
+
37
+ def remove(name)
38
+ @config.open_for_update
39
+
40
+ unless @config.remove_section(["remote", name])
41
+ raise InvalidRemote, "No such remote: #{ name }"
42
+ end
43
+ ensure
44
+ @config.save
45
+ end
46
+
47
+ def list_remotes
48
+ @config.open
49
+ @config.subsections("remote")
50
+ end
51
+
52
+ def get(name)
53
+ @config.open
54
+ return nil unless @config.section?(["remote", name])
55
+
56
+ Remote.new(@config, name)
57
+ end
58
+
59
+ def get_upstream(branch)
60
+ @config.open
61
+ name = @config.get(["branch", branch, "remote"])
62
+ get(name)&.get_upstream(branch)
63
+ end
64
+
65
+ def set_upstream(branch, upstream)
66
+ list_remotes.each do |name|
67
+ ref = get(name).set_upstream(branch, upstream)
68
+ return [name, ref] if ref
69
+ end
70
+
71
+ raise InvalidBranch,
72
+ "Cannot setup tracking information; " +
73
+ "starting point '#{ upstream }' is not a branch"
74
+ end
75
+
76
+ def unset_upstream(branch)
77
+ @config.open_for_update
78
+ @config.unset(["branch", branch, "remote"])
79
+ @config.unset(["branch", branch, "merge"])
80
+ @config.save
81
+ end
82
+ end
@@ -0,0 +1,82 @@
1
+ class Remotes
2
+ class Protocol
3
+
4
+ attr_reader :input, :output
5
+
6
+ def initialize(command, input, output, capabilities = [])
7
+ @command = command
8
+ @input = input
9
+ @output = output
10
+
11
+ @input.sync = @output.sync = true
12
+
13
+ @caps_local = capabilities
14
+ @caps_remote = nil
15
+ @caps_sent = false
16
+ end
17
+
18
+ def capable?(ability)
19
+ @caps_remote&.include?(ability)
20
+ end
21
+
22
+ def send_packet(line)
23
+ return @output.write("0000") if line == nil
24
+
25
+ line = append_caps(line)
26
+
27
+ size = line.bytesize + 5
28
+ @output.write(size.to_s(16).rjust(4, "0"))
29
+ @output.write(line)
30
+ @output.write("\n")
31
+ end
32
+
33
+ def recv_packet
34
+ head = @input.read(4)
35
+ return head unless /[0-9a-f]{4}/ =~ head
36
+
37
+ size = head.to_i(16)
38
+ return nil if size == 0
39
+
40
+ line = @input.read(size - 4).sub(/\n$/, "")
41
+ detect_caps(line)
42
+ end
43
+
44
+ def recv_until(terminator)
45
+ loop do
46
+ line = recv_packet
47
+ break if line == terminator
48
+ yield line
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def append_caps(line)
55
+ return line if @caps_sent
56
+ @caps_sent = true
57
+
58
+ sep = (@command == "fetch") ? " " : "\0"
59
+ caps = @caps_local
60
+ caps &= @caps_remote if @caps_remote
61
+
62
+ line + sep + caps.join(" ")
63
+ end
64
+
65
+ def detect_caps(line)
66
+ return line if @caps_remote
67
+
68
+ if @command == "upload-pack"
69
+ sep, n = " ", 3
70
+ else
71
+ sep, n = "\0", 2
72
+ end
73
+
74
+ parts = line.split(sep, n)
75
+ caps = (parts.size == n) ? parts.pop : ""
76
+
77
+ @caps_remote = caps.split(/ +/)
78
+ parts.join(" ")
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,70 @@
1
+ require "pathname"
2
+
3
+ require_relative "../refs"
4
+ require_relative "../revision"
5
+
6
+ class Remotes
7
+
8
+ REFSPEC_FORMAT = /^(\+?)([^:]*)(:([^:]*))?$/
9
+
10
+ Refspec = Struct.new(:source, :target, :forced) do
11
+ def self.parse(spec)
12
+ match = REFSPEC_FORMAT.match(spec)
13
+ source = canonical(match[2])
14
+ target = canonical(match[4]) || source
15
+
16
+ Refspec.new(source, target, match[1] == "+")
17
+ end
18
+
19
+ def self.canonical(name)
20
+ return nil if name.to_s == ""
21
+ return name unless Revision.valid_ref?(name)
22
+
23
+ first = Pathname.new(name).each_filename.first
24
+ dirs = [Refs::REFS_DIR, Refs::HEADS_DIR, Refs::REMOTES_DIR]
25
+ prefix = dirs.find { |dir| first == dir.basename.to_s }
26
+
27
+ (prefix&.dirname || Refs::HEADS_DIR).join(name).to_s
28
+ end
29
+
30
+ def self.expand(specs, refs)
31
+ specs = specs.map { |spec| parse(spec) }
32
+
33
+ specs.reduce({}) do |mappings, spec|
34
+ mappings.merge(spec.match_refs(refs))
35
+ end
36
+ end
37
+
38
+ def self.invert(specs, ref)
39
+ specs = specs.map { |spec| parse(spec) }
40
+
41
+ map = specs.reduce({}) do |mappings, spec|
42
+ spec.source, spec.target = spec.target, spec.source
43
+ mappings.merge(spec.match_refs([ref]))
44
+ end
45
+
46
+ map.keys.first
47
+ end
48
+
49
+ def match_refs(refs)
50
+ return { target => [source, forced] } unless source.to_s.include?("*")
51
+
52
+ pattern = /^#{ source.sub("*", "(.*)") }$/
53
+ mappings = {}
54
+
55
+ refs.each do |ref|
56
+ next unless match = pattern.match(ref)
57
+ dst = match[1] ? target.sub("*", match[1]) : target
58
+ mappings[dst] = [ref, forced]
59
+ end
60
+
61
+ mappings
62
+ end
63
+
64
+ def to_s
65
+ spec = forced ? "+" : ""
66
+ spec + [source, target].join(":")
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,57 @@
1
+ require_relative "./refspec"
2
+
3
+ class Remotes
4
+ class Remote
5
+
6
+ def initialize(config, name)
7
+ @config = config
8
+ @name = name
9
+
10
+ @config.open
11
+ end
12
+
13
+ def fetch_url
14
+ @config.get(["remote", @name, "url"])
15
+ end
16
+
17
+ def fetch_specs
18
+ @config.get_all(["remote", @name, "fetch"])
19
+ end
20
+
21
+ def push_url
22
+ @config.get(["remote", @name, "pushurl"]) || fetch_url
23
+ end
24
+
25
+ def push_specs
26
+ @config.get_all(["remote", @name, "push"])
27
+ end
28
+
29
+ def uploader
30
+ @config.get(["remote", @name, "uploadpack"])
31
+ end
32
+
33
+ def receiver
34
+ @config.get(["remote", @name, "receivepack"])
35
+ end
36
+
37
+ def get_upstream(branch)
38
+ merge = @config.get(["branch", branch, "merge"])
39
+ targets = Refspec.expand(fetch_specs, [merge])
40
+
41
+ targets.keys.first
42
+ end
43
+
44
+ def set_upstream(branch, upstream)
45
+ ref_name = Refspec.invert(fetch_specs, upstream)
46
+ return nil unless ref_name
47
+
48
+ @config.open_for_update
49
+ @config.set(["branch", branch, "remote"], @name)
50
+ @config.set(["branch", branch, "merge"], ref_name)
51
+ @config.save
52
+
53
+ ref_name
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require_relative "./config/stack"
2
+ require_relative "./database"
3
+ require_relative "./index"
4
+ require_relative "./refs"
5
+ require_relative "./remotes"
6
+ require_relative "./workspace"
7
+
8
+ require_relative "./repository/divergence"
9
+ require_relative "./repository/hard_reset"
10
+ require_relative "./repository/migration"
11
+ require_relative "./repository/pending_commit"
12
+ require_relative "./repository/status"
13
+
14
+ class Repository
15
+ attr_reader :git_path
16
+
17
+ def initialize(git_path)
18
+ @git_path = git_path
19
+ end
20
+
21
+ def config
22
+ @config ||= Config::Stack.new(@git_path)
23
+ end
24
+
25
+ def database
26
+ @database ||= Database.new(@git_path.join("objects"))
27
+ end
28
+
29
+ def divergence(ref)
30
+ Divergence.new(self, ref)
31
+ end
32
+
33
+ def hard_reset(oid)
34
+ HardReset.new(self, oid).execute
35
+ end
36
+
37
+ def index
38
+ @index ||= Index.new(@git_path.join("index"))
39
+ end
40
+
41
+ def migration(tree_diff)
42
+ Migration.new(self, tree_diff)
43
+ end
44
+
45
+ def pending_commit
46
+ PendingCommit.new(@git_path)
47
+ end
48
+
49
+ def refs
50
+ @refs ||= Refs.new(@git_path)
51
+ end
52
+
53
+ def remotes
54
+ @remotes ||= Remotes.new(config.file(:local))
55
+ end
56
+
57
+ def status(commit_oid = nil)
58
+ Status.new(self, commit_oid)
59
+ end
60
+
61
+ def workspace
62
+ @workspace ||= Workspace.new(@git_path.dirname)
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "../merge/common_ancestors"
2
+
3
+ class Repository
4
+ class Divergence
5
+
6
+ attr_reader :upstream, :ahead, :behind
7
+
8
+ def initialize(repo, ref)
9
+ @upstream = repo.remotes.get_upstream(ref.short_name)
10
+ return unless @upstream
11
+
12
+ left = ref.read_oid
13
+ right = repo.refs.read_ref(@upstream)
14
+ common = Merge::CommonAncestors.new(repo.database, left, [right])
15
+
16
+ common.find
17
+ @ahead, @behind = common.counts
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ require "pathname"
2
+
3
+ class Repository
4
+ class HardReset
5
+
6
+ def initialize(repo, oid)
7
+ @repo = repo
8
+ @oid = oid
9
+ end
10
+
11
+ def execute
12
+ @status = @repo.status(@oid)
13
+ changed = @status.changed.map { |path| Pathname.new(path) }
14
+
15
+ changed.each { |path| reset_path(path) }
16
+ end
17
+
18
+ private
19
+
20
+ def reset_path(path)
21
+ @repo.index.remove(path)
22
+ @repo.workspace.remove(path)
23
+
24
+ entry = @status.head_tree[path.to_s]
25
+ return unless entry
26
+
27
+ blob = @repo.database.load(entry.oid)
28
+ @repo.workspace.write_file(path, blob.data, entry.mode, true)
29
+
30
+ stat = @repo.workspace.stat_file(path)
31
+ @repo.index.add(path, entry.oid, stat)
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ class Repository
2
+ class Inspector
3
+
4
+ def initialize(repository)
5
+ @repo = repository
6
+ end
7
+
8
+ def trackable_file?(path, stat)
9
+ return false unless stat
10
+
11
+ return !@repo.index.tracked_file?(path) if stat.file?
12
+ return false unless stat.directory?
13
+
14
+ items = @repo.workspace.list_dir(path)
15
+ files = items.select { |_, item_stat| item_stat.file? }
16
+ dirs = items.select { |_, item_stat| item_stat.directory? }
17
+
18
+ [files, dirs].any? do |list|
19
+ list.any? { |item_path, item_stat| trackable_file?(item_path, item_stat) }
20
+ end
21
+ end
22
+
23
+ def compare_index_to_workspace(entry, stat)
24
+ return :untracked unless entry
25
+ return :deleted unless stat
26
+ return :modified unless entry.stat_match?(stat)
27
+ return nil if entry.times_match?(stat)
28
+
29
+ data = @repo.workspace.read_file(entry.path)
30
+ blob = Database::Blob.new(data)
31
+ oid = @repo.database.hash_object(blob)
32
+
33
+ unless entry.oid == oid
34
+ :modified
35
+ end
36
+ end
37
+
38
+ def compare_tree_to_index(item, entry)
39
+ return nil unless item or entry
40
+ return :added unless item
41
+ return :deleted unless entry
42
+
43
+ unless entry.mode == item.mode and entry.oid == item.oid
44
+ :modified
45
+ end
46
+ end
47
+
48
+ end
49
+ end