jit 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/remotes.rb
ADDED
@@ -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
|
data/lib/repository.rb
ADDED
@@ -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
|