Skip to content

Commit

Permalink
Git: dir keyword allows working on repos without cd'ing into them.
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski committed Jun 19, 2013
1 parent 4eaef7b commit 86d9fd4
Showing 1 changed file with 49 additions and 33 deletions.
82 changes: 49 additions & 33 deletions base/git.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,80 @@ module Git
# some utility functions for working with git repos
#

dir() = readchomp(`git rev-parse --git-dir`)
modules(args::Cmd) = readchomp(`git config -f .gitmodules $args`)
dir(d) = cd(dir,d)
dir() = Base.readchomp(`git rev-parse --git-dir`)

function git(d)
isempty(d) && return `git`
work_tree = abspath(d)
git_dir = joinpath(work_tree, dir(work_tree))
`git --work-tree=$work_tree --git-dir=$git_dir`
end

run(args; dir="") = Base.run(`$(git(dir)) $args`)
success(args; dir="") = Base.success(`$(git(dir)) $args`)
readall(args; dir="") = Base.readall(`$(git(dir)) $args`)
readchomp(args; dir="") = Base.readchomp(`$(git(dir)) $args`)

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Jun 20, 2013

Member

I find these kind of obfuscated. How about having git() construct git commands, so you'd write readchomp(git(config remote.origin.url)) where readchomp is the usual one from Base.

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Jun 20, 2013

Member

And git would accept the dir keyword of course.

This comment has been minimized.

Copy link
@StefanKarpinski

StefanKarpinski Jun 20, 2013

Author Member

They're only confusing in here since externally they're used as Git.readchomp(args...), which is the more common situation. In any case, I needed to be able to use these without cding all other the place and wanted to change the API to older code minimally, so I did it this way. We can refactor later when there's only one code base using this.

This comment has been minimized.

Copy link
@StefanKarpinski

StefanKarpinski Jun 20, 2013

Author Member

This Git module is purely an internal API – it's completely ad hoc, not exported, and not really meant to be used anywhere else.


modules(args::Cmd) = readchomp(`config -f .gitmodules $args`)
different(verA::String, verB::String, path::String) =
!success(`git diff --quiet $verA $verB -- $path`)
!success(`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`)
iscommit(name) = success(`git cat-file commit $name`)
dirty(; dir="") = !success(`diff --quiet HEAD`, dir=dir)
staged(; dir="") = !success(`diff --quiet --cached`, dir=dir)
unstaged(; dir="") = !success(`diff --quiet`, dir=dir)
dirty(paths; dir="") = !success(`diff --quiet HEAD -- $paths`, dir=dir)
staged(paths; dir="") = !success(`diff --quiet --cached -- $paths`, dir=dir)
unstaged(paths; dir="") = !success(`diff --quiet -- $paths`, dir=dir)
iscommit(name; dir="") = success(`cat-file commit $name`, dir=dir)

attached() = success(`git symbolic-ref -q HEAD` |> SpawnNullStream())
branch() = readchomp(`git rev-parse --symbolic-full-name --abbrev-ref HEAD`)
head() = readchomp(`git rev-parse HEAD`)
attached(; dir="") = success(`symbolic-ref -q HEAD` |> SpawnNullStream(); dir=dir)
branch(; dir="") = readchomp(`rev-parse --symbolic-full-name --abbrev-ref HEAD`, dir=dir)
head(; dir="") = readchomp(`rev-parse HEAD`, dir=dir)

immutable State
head::ASCIIString
index::ASCIIString
work::ASCIIString
end

function snapshot()
head = readchomp(`git rev-parse HEAD`)
index = readchomp(`git write-tree`)
function snapshot(; dir="")
head = readchomp(`rev-parse HEAD`, dir=dir)
index = readchomp(`write-tree`, dir=dir)
work = try
run(`git add --all`)
run(`git add .`)
readchomp(`git write-tree`)
run(`add --all`, dir=dir)
run(`add .`, dir=dir)
readchomp(`write-tree`, dir=dir)
finally
run(`git read-tree $index`) # restore index
run(`read-tree $index`, dir=dir) # restore index
end
State(head, index, work)
end

function restore(s::State)
run(`git reset -q --`) # unstage everything
run(`git read-tree $(s.work)`) # move work tree to index
run(`git checkout-index -fa`) # check the index out to work
run(`git clean -qdf`) # remove everything else
run(`git read-tree $(s.index)`) # restore index
run(`git reset -q --soft $(s.head)`) # restore head
function restore(s::State; dir="")
run(`reset -q --`, dir=dir) # unstage everything
run(`read-tree $(s.work)`, dir=dir) # move work tree to index
run(`checkout-index -fa`, dir=dir) # check the index out to work
run(`clean -qdf`, dir=dir) # remove everything else
run(`read-tree $(s.index)`, dir=dir) # restore index
run(`reset -q --soft $(s.head)`, dir=dir) # restore head
end

function transact(f::Function)
state = snapshot()
function transact(f::Function; dir="")
state = snapshot(dir=dir)
try f() catch
restore(state)
restore(state, dir=dir)
rethrow()
end
end

function is_ancestor_of(a::String, b::String)
A = readchomp(`git rev-parse $a`)
readchomp(`git merge-base $A $b`) == A
function is_ancestor_of(a::String, b::String; dir="")
A = readchomp(`rev-parse $a`, dir=dir)
readchomp(`merge-base $A $b`, dir=dir) == A
end

## below here to be retired ##

function each_tagged_version()
git_dir = abspath(dir())
@task for line in eachline(`git --git-dir=$git_dir show-ref --tags`)
Expand Down

0 comments on commit 86d9fd4

Please sign in to comment.