Files
atom/native/v8_extensions/git.mm
Nathan Sobo f22fedebcf Inject new instances of native objects into every JS context
This prevents concurrent access to the same state from different
worker threads. We needed to treat windowState specially because we
explicitly want it to last beyond the life-span of a single context.
So we store it as a static variable in `native.mm` and synchronize
access with a static `NSLock`. Good enough for now.
2013-01-24 17:22:50 -08:00

282 lines
8.6 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() {
Destroy();
}
void Destroy() {
if (Exists()) {
git_repository_free(repo);
repo = NULL;
}
}
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() {
}
void Git::CreateContextBinding(CefRefPtr<CefV8Context> context) {
const char* methodNames[] = {
"getRepository", "getHead", "getPath", "isIgnored", "getStatus", "checkoutHead",
"getDiffStats", "isSubmodule", "refreshIndex", "destroy"
};
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
int arrayLength = sizeof(methodNames) / sizeof(const char *);
for (int i = 0; i < arrayLength; i++) {
const char *functionName = methodNames[i];
CefRefPtr<CefV8Value> function = CefV8Value::CreateFunction(functionName, this);
nativeObject->SetValue(functionName, function, V8_PROPERTY_ATTRIBUTE_NONE);
}
CefRefPtr<CefV8Value> global = context->GetGlobal();
global->SetValue("$git", nativeObject, V8_PROPERTY_ATTRIBUTE_NONE);
}
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;
}
if (name == "destroy") {
GitRepository *userData = (GitRepository *)object->GetUserData().get();
userData->Destroy();
return true;
}
return false;
}
}