|
| 1 | +# This file is a part of Julia. License is MIT: http://julialang.org/license |
| 2 | + |
| 3 | +module Git |
| 4 | +# |
| 5 | +# some utility functions for working with git repos |
| 6 | +# |
| 7 | +import Base: shell_escape |
| 8 | + |
| 9 | +function dir(d) |
| 10 | + g = joinpath(d,".git") |
| 11 | + isdir(g) && return g |
| 12 | + normpath(d, Base.readchomp(setenv(`git rev-parse --git-dir`, dir=d))) |
| 13 | +end |
| 14 | + |
| 15 | +function git(d) |
| 16 | + isempty(d) && return `git` |
| 17 | + work_tree = abspath(d) |
| 18 | + git_dir = joinpath(work_tree, dir(work_tree)) |
| 19 | + normpath(work_tree, ".") == normpath(git_dir, ".") ? # is it a bare repo? |
| 20 | + `git --git-dir=$work_tree` : `git --work-tree=$work_tree --git-dir=$git_dir` |
| 21 | +end |
| 22 | + |
| 23 | +cmd(args::Cmd; dir="") = `$(git(dir)) $args` |
| 24 | +run(args::Cmd; dir="", out=STDOUT) = Base.run(pipeline(cmd(args,dir=dir), out)) |
| 25 | +readall(args::Cmd; dir="") = Base.readall(cmd(args,dir=dir)) |
| 26 | +readchomp(args::Cmd; dir="") = Base.readchomp(cmd(args,dir=dir)) |
| 27 | + |
| 28 | +function success(args::Cmd; dir="") |
| 29 | + g = git(dir) |
| 30 | + Base.readchomp(`$g rev-parse --is-bare-repository`) == "false" && |
| 31 | + Base.run(`$g update-index -q --really-refresh`) |
| 32 | + Base.success(`$g $args`) |
| 33 | +end |
| 34 | + |
| 35 | +function version() |
| 36 | + vs = split(readchomp(`version`), ' ')[3] |
| 37 | + ns = split(vs, '.') |
| 38 | + if length(ns) > 3 |
| 39 | + VersionNumber(join(ns[1:3], '.')) |
| 40 | + else |
| 41 | + VersionNumber(join(ns, '.')) |
| 42 | + end |
| 43 | +end |
| 44 | + |
| 45 | +modules(args::Cmd; dir="") = readchomp(`config -f .gitmodules $args`, dir=dir) |
| 46 | +different(verA::AbstractString, verB::AbstractString, path::AbstractString; dir="") = |
| 47 | + !success(`diff-tree --quiet $verA $verB -- $path`, dir=dir) |
| 48 | + |
| 49 | +dirty(; dir="") = !success(`diff-index --quiet HEAD`, dir=dir) |
| 50 | +staged(; dir="") = !success(`diff-index --quiet --cached HEAD`, dir=dir) |
| 51 | +unstaged(; dir="") = !success(`diff-files --quiet`, dir=dir) |
| 52 | +dirty(paths; dir="") = !success(`diff-index --quiet HEAD -- $paths`, dir=dir) |
| 53 | +staged(paths; dir="") = !success(`diff-index --quiet --cached HEAD -- $paths`, dir=dir) |
| 54 | +unstaged(paths; dir="") = !success(`diff-files --quiet -- $paths`, dir=dir) |
| 55 | + |
| 56 | +iscommit(name; dir="") = success(`cat-file commit $name`, dir=dir) |
| 57 | +attached(; dir="") = success(`symbolic-ref -q HEAD`, dir=dir) |
| 58 | +branch(; dir="") = readchomp(`rev-parse --symbolic-full-name --abbrev-ref HEAD`, dir=dir) |
| 59 | +head(; dir="") = readchomp(`rev-parse HEAD`, dir=dir) |
| 60 | + |
| 61 | +function iscommit(sha1s::Vector; dir="") |
| 62 | + indexin(sha1s,split(readchomp(`log --all --format=%H`, dir=dir),"\n")).!=0 |
| 63 | +end |
| 64 | + |
| 65 | +immutable State |
| 66 | + head::ASCIIString |
| 67 | + index::ASCIIString |
| 68 | + work::ASCIIString |
| 69 | +end |
| 70 | + |
| 71 | +function snapshot(; dir="") |
| 72 | + head = readchomp(`rev-parse HEAD`, dir=dir) |
| 73 | + index = readchomp(`write-tree`, dir=dir) |
| 74 | + work = try |
| 75 | + if length(readdir(abspath(dir))) > 1 |
| 76 | + run(`add --all`, dir=dir) |
| 77 | + run(`add .`, dir=dir) |
| 78 | + end |
| 79 | + readchomp(`write-tree`, dir=dir) |
| 80 | + finally |
| 81 | + run(`read-tree $index`, dir=dir) # restore index |
| 82 | + end |
| 83 | + State(head, index, work) |
| 84 | +end |
| 85 | + |
| 86 | +function restore(s::State; dir="") |
| 87 | + run(`reset -q --`, dir=dir) # unstage everything |
| 88 | + run(`read-tree $(s.work)`, dir=dir) # move work tree to index |
| 89 | + run(`checkout-index -fa`, dir=dir) # check the index out to work |
| 90 | + run(`clean -qdf`, dir=dir) # remove everything else |
| 91 | + run(`read-tree $(s.index)`, dir=dir) # restore index |
| 92 | + run(`reset -q --soft $(s.head)`, dir=dir) # restore head |
| 93 | +end |
| 94 | + |
| 95 | +function transact(f::Function; dir="") |
| 96 | + state = snapshot(dir=dir) |
| 97 | + try f() catch |
| 98 | + restore(state, dir=dir) |
| 99 | + rethrow() |
| 100 | + end |
| 101 | +end |
| 102 | + |
| 103 | +function is_ancestor_of(a::AbstractString, b::AbstractString; dir="") |
| 104 | + A = readchomp(`rev-parse $a`, dir=dir) |
| 105 | + readchomp(`merge-base $A $b`, dir=dir) == A |
| 106 | +end |
| 107 | + |
| 108 | +const GITHUB_REGEX = |
| 109 | + r"^(?:git@|git://|https://(?:[\w\.\+\-]+@)?)github.com[:/](([^/].+)/(.+?))(?:\.git)?$"i |
| 110 | + |
| 111 | +function set_remote_url(url::AbstractString; remote::AbstractString="origin", dir="") |
| 112 | + run(`config remote.$remote.url $url`, dir=dir) |
| 113 | + m = match(GITHUB_REGEX,url) |
| 114 | + m === nothing && return |
| 115 | + push = "[email protected]:$(m.captures[1]).git" |
| 116 | + push != url && run(`config remote.$remote.pushurl $push`, dir=dir) |
| 117 | +end |
| 118 | + |
| 119 | +function normalize_url(url::AbstractString) |
| 120 | + m = match(GITHUB_REGEX,url) |
| 121 | + m === nothing ? url : "git://github.com/$(m.captures[1]).git" |
| 122 | +end |
| 123 | + |
| 124 | +end # module |
0 commit comments