/
git.jl
119 lines (109 loc) · 3.41 KB
/
git.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
module git
#
# some utility functions for working with git repos
#
import Base.*
dir() = readchomp(`git rev-parse --git-dir`)
modules(args::Cmd) = readchomp(`git config -f .gitmodules $args`)
different(verA::String, verB::String, path::String) =
!success(`git diff --quiet $verA $verB -- $path`)
dirty() = !success(`git diff --quiet HEAD`)
staged() = !success(`git diff --quiet --cached`)
unstaged() = !success(`git diff --quiet`)
dirty(paths) = !success(`git diff --quiet HEAD -- $paths`)
staged(paths) = !success(`git diff --quiet --cached -- $paths`)
unstaged(paths) = !success(`git diff --quiet -- $paths`)
function each_submodule(f::Function, recursive::Bool, dir::ByteString)
cmd = `git submodule foreach --quiet 'echo "$name\t$path\t$sha1"'`
for line in each_line(cmd)
name, path, sha1 = match(r"^(.*)\t(.*)\t([0-9a-f]{40})$", line).captures
cd(dir) do
f(name, path, sha1)
end
if recursive
cd(path) do
each_submodule(true, dir) do n,p,s
cd(dir) do
f(n,"$path/$p",s)
end
end
end
end
end
end
each_submodule(f::Function, r::Bool) = each_submodule(f, r, cwd())
function read_config(file::String)
cfg = Dict()
# TODO: use --null option for better handling of weird values.
for line in each_line(`git config -f $file --get-regexp '.*'`)
key, val = match(r"^(\S+)\s+(.*)$", line).captures
cfg[key] = has(cfg,key) ? [cfg[key],val] : val
end
return cfg
end
# TODO: provide a clean way to avoid this disaster
function read_config_blob(blob::String)
tmp = tmpnam()
open(tmp,"w") do io
write(io, readall(`git cat-file blob $blob`))
end
cfg = read_config(tmp)
run(`rm -f tmp`)
return cfg
end
function write_config(file::String, cfg::Dict)
tmp = tmpnam()
for key in sort!(keys(cfg))
val = cfg[key]
if isa(val,Array)
for x in val
run(`git config -f $tmp --add $key $x`)
end
else
run(`git config -f $tmp $key $val`)
end
end
open(file,"w") do io
print(io,readall(tmp))
end
end
canonicalize_config(file::String) = write_config(file, read_config(file))
function config_sections(cfg::Dict)
sections = Set{ByteString}()
for (key,_) in cfg
m = match(r"^(.+)\.", key)
if m != nothing add(sections,m.captures[1]) end
end
sections
end
function merge_configs(Bc::Dict, Lc::Dict, Rc::Dict)
# extract section names
Bs = config_sections(Bc)
Ls = config_sections(Lc)
Rs = config_sections(Rc)
# expunge removed submodules from left and right sides
deleted = Set{ByteString}()
for section in Bs - Ls & Rs
filter!((k,v)->!begins_with(k,"$section."),Lc)
filter!((k,v)->!begins_with(k,"$section."),Rc)
add(deleted, section)
end
# merge the remaining config key-value pairs
cfg = Dict()
conflicts = Dict()
for (key,_) in merge(Lc,Rc)
Lv = get(Lc,key,nothing)
Rv = get(Rc,key,nothing)
Bv = get(Bc,key,nothing)
if Lv == Rv || Rv == Bv
if Lv != nothing cfg[key] = Lv end
elseif Lv == Bv
if Rv != nothing cfg[key] = Rv end
else # conflict!
conflicts[key] = [Lv,Rv]
cfg[key] = Bv
end
end
return cfg, conflicts, deleted
end
end # module