Files
atom/native/v8_extensions/git.mm
Kevin Sawicki 6f5e29d48e Throw exception when repository fails to open
Removes need to check if the repository is valid before
each native API call and also removes calling into the
native-side if no repository can be opened for the project
page.
2013-01-08 21:15:41 -08:00

255 lines
7.5 KiB
Plaintext

#import "git.h"
#import "include/git2.h"
#import <Cocoa/Cocoa.h>
namespace v8_extensions {
class GitRepository : public CefBase {
private:
git_repository *repo;
public:
GitRepository(const char *pathInRepo) {
if (git_repository_open_ext(&repo, pathInRepo, 0, NULL) != GIT_OK) {
repo = NULL;
}
}
~GitRepository() {
git_repository_free(repo);
}
BOOL exists() {
return repo != NULL;
}
CefRefPtr<CefV8Value> GetPath() {
return CefV8Value::CreateString(git_repository_path(repo));
}
CefRefPtr<CefV8Value> GetHead() {
git_reference *head;
if (git_repository_head(&head, repo) == GIT_OK) {
if (git_repository_head_detached(repo) == 1) {
const git_oid* sha = git_reference_target(head);
if (sha) {
char oid[GIT_OID_HEXSZ + 1];
git_oid_tostr(oid, GIT_OID_HEXSZ + 1, sha);
git_reference_free(head);
return CefV8Value::CreateString(oid);
}
}
CefRefPtr<CefV8Value> result = CefV8Value::CreateString(git_reference_name(head));
git_reference_free(head);
return result;
}
return CefV8Value::CreateNull();
}
CefRefPtr<CefV8Value> IsIgnored(const char *path) {
int ignored;
if (git_ignore_path_is_ignored(&ignored, repo, path) == GIT_OK) {
return CefV8Value::CreateBool(ignored == 1);
}
else {
return CefV8Value::CreateBool(false);
}
}
CefRefPtr<CefV8Value> GetStatus(const char *path) {
unsigned int status = 0;
if (git_status_file(&status, repo, path) == GIT_OK) {
return CefV8Value::CreateInt(status);
}
else {
return CefV8Value::CreateInt(0);
}
}
CefRefPtr<CefV8Value> CheckoutHead(const char *path) {
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_checkout_opts options = GIT_CHECKOUT_OPTS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.paths = paths;
int result = git_checkout_head(repo, &options);
free(copiedPath);
return CefV8Value::CreateBool(result == GIT_OK);
}
CefRefPtr<CefV8Value> GetDiffStats(const char *path) {
git_reference *head;
if (git_repository_head(&head, repo) != GIT_OK) {
return CefV8Value::CreateNull();
}
const git_oid* sha = git_reference_target(head);
git_commit *commit;
int commitStatus = git_commit_lookup(&commit, repo, sha);
git_reference_free(head);
if (commitStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
git_tree *tree;
int treeStatus = git_commit_tree(&tree, commit);
git_commit_free(commit);
if (treeStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
char *copiedPath = (char *)malloc(sizeof(char) * (strlen(path) + 1));
strcpy(copiedPath, path);
git_diff_options options = GIT_DIFF_OPTIONS_INIT;
git_strarray paths;
paths.count = 1;
paths.strings = &copiedPath;
options.pathspec = paths;
options.context_lines = 1;
options.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH;
git_diff_list *diffs;
int diffStatus = git_diff_tree_to_workdir(&diffs, repo, tree, &options);
free(copiedPath);
if (diffStatus != GIT_OK || git_diff_num_deltas(diffs) != 1) {
return CefV8Value::CreateNull();
}
git_diff_patch *patch;
int patchStatus = git_diff_get_patch(&patch, NULL, diffs, 0);
git_diff_list_free(diffs);
if (patchStatus != GIT_OK) {
return CefV8Value::CreateNull();
}
int added = 0;
int deleted = 0;
int hunks = git_diff_patch_num_hunks(patch);
for (int i = 0; i < hunks; i++) {
int lines = git_diff_patch_num_lines_in_hunk(patch, i);
for (int j = 0; j < lines; j++) {
char lineType;
if (git_diff_patch_get_line_in_hunk(&lineType, NULL, NULL, NULL, NULL, patch, i, j) == GIT_OK) {
switch (lineType) {
case GIT_DIFF_LINE_ADDITION:
added++;
break;
case GIT_DIFF_LINE_DELETION:
deleted++;
break;
}
}
}
}
git_diff_patch_free(patch);
CefRefPtr<CefV8Value> result = CefV8Value::CreateObject(NULL);
result->SetValue("added", CefV8Value::CreateInt(added), V8_PROPERTY_ATTRIBUTE_NONE);
result->SetValue("deleted", CefV8Value::CreateInt(deleted), V8_PROPERTY_ATTRIBUTE_NONE);
return result;
}
CefRefPtr<CefV8Value> IsSubmodule(const char *path) {
BOOL isSubmodule = false;
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
const git_index_entry *entry = git_index_get_bypath(index, path, 0);
isSubmodule = entry != NULL && (entry->mode & S_IFMT) == GIT_FILEMODE_COMMIT;
git_index_free(index);
}
return CefV8Value::CreateBool(isSubmodule);
}
void RefreshIndex() {
git_index* index;
if (git_repository_index(&index, repo) == GIT_OK) {
git_index_read(index);
git_index_free(index);
}
}
IMPLEMENT_REFCOUNTING(GitRepository);
};
Git::Git() : CefV8Handler() {
NSString *filePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"v8_extensions/git.js"];
NSString *extensionCode = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
CefRegisterExtension("v8/git", [extensionCode UTF8String], this);
}
bool Git::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
if (name == "getRepository") {
GitRepository *repository = new GitRepository(arguments[0]->GetStringValue().ToString().c_str());
if (repository->exists()) {
CefRefPtr<CefBase> userData = repository;
retval = CefV8Value::CreateObject(NULL);
retval->SetUserData(userData);
} else {
retval = CefV8Value::CreateNull();
}
return true;
}
if (name == "getHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetHead();
return true;
}
if (name == "getPath") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetPath();
return true;
}
if (name == "isIgnored") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsIgnored(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getStatus") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetStatus(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "checkoutHead") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->CheckoutHead(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "getDiffStats") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->GetDiffStats(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "isSubmodule") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
retval = userData->IsSubmodule(arguments[0]->GetStringValue().ToString().c_str());
return true;
}
if (name == "refreshIndex") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->RefreshIndex();
return true;
}
return false;
}
}