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