MiniGit is a small Ruby gem to talk with Git command line in a straightforward way. You can write, say, git.commit(:amend => true, :reuse_message => true), and have it do the right thing. No fancy model objects for repos, commits, and branches. No magic. Well, a little bit of magic, just to make it simple and comfortable to use.
One of the quirks is capturing Git’s output. As of now, MiniGit either lets git use the original console, or captures its whole stdout and returns it as a string. Without stripping the trailing newline, or any logic. Simple and straightforward – but pretty uncomfortable: to, say, make sure there are no changes to the work tree, you write git.capturing.diff.strip.empty?. It’s a handful, and don’t get me started on splitting the output into lines.
I want it to be simple. I want to be able to get the output as string – stripped or not, as I prefer at the moment. I want to be able to iterate over the lines, and don’t have the lines include the trailing newline. I want to be able to discard the output, or to display it only when there’s an error. And I don’t want to type too much to achieve that. But I have no good concept of how to write it.
A custom magic method (such as
MiniGit#capturing) for each case would clutter the namespace and be hard to maintain. And it would involve a lot of output. The next idea – a custom
MiniGit# method to get a customized instance – this syntax is reserved for
git config integration:
git['user.email'] is too pretty and intuitive to waste it on anything else.
Next thing I thought of was overloading unary operators: -git could discard output (like prefixing Make lines), ~git could capture stdout, etc. Two issues here: one is that there’s still a very limited number of these operators. Still, there’s a limited number of things I may want to do with Git’s output, so this may not even be a problem. Second issue is that method call binds closer than any operator: -git.push would mean -(git.push), and I’d need to add extra parentheses to say (-git).push – ugly! Not to mention it would require users to memorize what each of the symbols means.
The idea I’m thinking of right now may be short enough, but it requires a bit of magic, and I’m not sure if it’s nice & convenient magic, or testrocket flashy & useless magic. If I’d need to use parentheses anyway, I may override binary operators to mimic shell redirection syntax. Instead of magic
capturing method, one could write:
git.log :n => 5
# Goes straight to terminal.
(git > String).log :n => 1
# Returns a string.
(+git > String).log :n => 1
# Returns a string, without newline stripped.
(git > Array).log :n => 5, :oneline => true
# Returns array of stripped lines.
(+git > Array).log :n => 5, :oneline => true
# Returns array of unstripped lines (as if anybody would ever need that).
(git > nil).fetch
# Discards output (redirects to /dev/null)
# Keeps output of last command in an accessor, displays it on failure and lets user examine it,
# but doesn't return it or display it on success.
(git > "path/to/file.patch").diff
# Writes output to file
(!git).log :n => 5
# Forces straight-to-terminal output in case it is a capturing instance.
Each of these expressions would create a new, specialized instance (in most cases cached in the parent instance to avoid allocating too many short lived instances, at cost of sometimes storing a few too many) just the way
#noncapturing. Other modifiers could make it e.g. return a boolean variable to indicate success rather than always raising exception on error and requiring user to
rescue when they need to check status.
Update: a more straightforward option would be a method that would be quicker to type, and accept either a known keyword, or any MiniGit subclass; something like this:
git.to(:string).log :n => 1
git.to(:string, :strip => false).log, :n => 1
git.to(:array).log, :n => 5
git.to(:array, :strip => false).log, :n => 5
git.to(:console).commit :all => true, :patch => true
git.to(:quiet).fetch # shows output on error; any idea for better name?
Write me what you think
What do you think: nice? uselessly convoluted? confusing? stupid? Or maybe you have any better ideas for that? I’ll happy to hear any input. You can also help by upvoting on Hacker News to get more people here.
Just use the poll here – it’s not directly published as a comment, but sent directly to me. I will update the article to include the results after a few days. If you’d like me to be able to reply, include your e-mail – but it’s not required in any way.
Waiting to hear from you!