From 26ca831ad42f6952dbf95ddd1f2ceaee0479a176 Mon Sep 17 00:00:00 2001 From: Gabriel Isenberg Date: Sat, 12 Sep 2015 23:10:16 -0700 Subject: [PATCH] Added support for blame per issue #11 --- spec/fixtures/blame.git/HEAD | 1 + spec/fixtures/blame.git/index | Bin 0 -> 137 bytes .../23/54b25984ae1863618b2a37a6f6d327483e34e4 | Bin 0 -> 50 bytes .../7a/4a0f2a8c1168a2bf7800b0a5f64db64e090b27 | Bin 0 -> 133 bytes .../9e/2fa05995713a934a99c88accdbb8ec941f001b | Bin 0 -> 64 bytes .../a2/e56f1240cf8ee6fb4912b1fcb631a5d8254d46 | 1 + .../a3/59b0418fb6c52dd26d806a937b8903bdbf8531 | Bin 0 -> 50 bytes .../fc/fc9307b3c7864cbb8bbfe457f44d7a33202de4 | Bin 0 -> 163 bytes spec/fixtures/blame.git/refs/heads/master | 1 + spec/git-spec.coffee | 21 ++++++ src/repository.cc | 68 ++++++++++++++++++ src/repository.h | 1 + 12 files changed, 93 insertions(+) create mode 100644 spec/fixtures/blame.git/HEAD create mode 100644 spec/fixtures/blame.git/index create mode 100644 spec/fixtures/blame.git/objects/23/54b25984ae1863618b2a37a6f6d327483e34e4 create mode 100644 spec/fixtures/blame.git/objects/7a/4a0f2a8c1168a2bf7800b0a5f64db64e090b27 create mode 100644 spec/fixtures/blame.git/objects/9e/2fa05995713a934a99c88accdbb8ec941f001b create mode 100644 spec/fixtures/blame.git/objects/a2/e56f1240cf8ee6fb4912b1fcb631a5d8254d46 create mode 100644 spec/fixtures/blame.git/objects/a3/59b0418fb6c52dd26d806a937b8903bdbf8531 create mode 100644 spec/fixtures/blame.git/objects/fc/fc9307b3c7864cbb8bbfe457f44d7a33202de4 create mode 100644 spec/fixtures/blame.git/refs/heads/master diff --git a/spec/fixtures/blame.git/HEAD b/spec/fixtures/blame.git/HEAD new file mode 100644 index 00000000..cb089cd8 --- /dev/null +++ b/spec/fixtures/blame.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/blame.git/index b/spec/fixtures/blame.git/index new file mode 100644 index 0000000000000000000000000000000000000000..8df4e25e029e5635442526a1099b7ef30cba9451 GIT binary patch literal 137 zcmZ?q402{*U|<4b#?Y@^2Z1yLjAmqDU}3Ob!p^|ZxCAKu6(}VF#EFZZ<_kHT?|b&! zQ)uI#ZH7y4sQS7wuqNu2RFr_UGlT@Wx&kRl215k{F6EF-kuB>ak`ueN%$I$;tnOiF b^5kaN9hX?wH9MZ<%Bw!*Tdf)#a(*EIN#iNA literal 0 HcmV?d00001 diff --git a/spec/fixtures/blame.git/objects/23/54b25984ae1863618b2a37a6f6d327483e34e4 b/spec/fixtures/blame.git/objects/23/54b25984ae1863618b2a37a6f6d327483e34e4 new file mode 100644 index 0000000000000000000000000000000000000000..d19be9dac1628d93a2e5f35d5d25b55fcbba526a GIT binary patch literal 50 zcmb}`*qDAAKoXI+r6lt?sa^~OT**ZKt^^$g2U+JLKGc39M@SdwL n`xIKe0ar?JArwcr;*7{ndwxgyzrfa`jN*6rrY80Q{x3eh#+^X! literal 0 HcmV?d00001 diff --git a/spec/fixtures/blame.git/objects/9e/2fa05995713a934a99c88accdbb8ec941f001b b/spec/fixtures/blame.git/objects/9e/2fa05995713a934a99c88accdbb8ec941f001b new file mode 100644 index 0000000000000000000000000000000000000000..b8c27cec15fbbff79263182e4872ab5c24b924d0 GIT binary patch literal 64 zcmV-G0Kflu0ZYosPf{>7W(dj1ELH%b+)9PC%%b8Fh2;F)+{_X^E>93MuOu-u4=7oh WQ<7Pbld6!DnU`9u#{~d^Hx(wNUK_pu literal 0 HcmV?d00001 diff --git a/spec/fixtures/blame.git/objects/a2/e56f1240cf8ee6fb4912b1fcb631a5d8254d46 b/spec/fixtures/blame.git/objects/a2/e56f1240cf8ee6fb4912b1fcb631a5d8254d46 new file mode 100644 index 00000000..b5f49cbf --- /dev/null +++ b/spec/fixtures/blame.git/objects/a2/e56f1240cf8ee6fb4912b1fcb631a5d8254d46 @@ -0,0 +1 @@ +x5Œ± €0 ©3ÅO1= b)–;"Äö@tŧ[‹­˜ÆaÉÒñRotÞL6«U<†Ù_U'ÑS¥$.¦TPD9\™õ‹íœàÏŒ¶ó)vôÿÓ³% \ No newline at end of file diff --git a/spec/fixtures/blame.git/objects/a3/59b0418fb6c52dd26d806a937b8903bdbf8531 b/spec/fixtures/blame.git/objects/a3/59b0418fb6c52dd26d806a937b8903bdbf8531 new file mode 100644 index 0000000000000000000000000000000000000000..d86fab2f9e0a7fabba322cbb37a92e9e243075f8 GIT binary patch literal 50 zcmb|69-(cpK(0Oe#4q zO`{zSt%~f>Q^bA<#A1T8&V-0;J-f+`7Ci+kmW0%kt87hsCWZ+)Bx77)jFS%l5xAcm zjnR?701>_7cYBnouN>Nti&AHOnwKA5=FH35rIOFu5TQXNJLn^L0JYowv&sJj Rb(d7OQGZLF`T#RpOFeG}OThpD literal 0 HcmV?d00001 diff --git a/spec/fixtures/blame.git/refs/heads/master b/spec/fixtures/blame.git/refs/heads/master new file mode 100644 index 00000000..897f2e6b --- /dev/null +++ b/spec/fixtures/blame.git/refs/heads/master @@ -0,0 +1 @@ +fcfc9307b3c7864cbb8bbfe457f44d7a33202de4 diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 88cc5d41..852d910a 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -724,3 +724,24 @@ describe "git", -> it "throws an error if the file doesn't exist", -> expect(-> repo.add('missing.txt')).toThrow() + + describe ".getBlame(path)", -> + beforeEach -> + repoDirectory = temp.mkdirSync('node-git-repo-') + wrench.copyDirSyncRecursive(path.join(__dirname, 'fixtures/blame.git'), path.join(repoDirectory, '.git')) + repo = git.open(repoDirectory) + + it "returns the blame for the path", -> + blame = repo.getBlame('a.txt') + + expect(blame.length).toBe 1 + expect(blame[0].commitId).toBe 'fcfc9307b' + expect(blame[0].startLineNumber).toBe 1 + expect(blame[0].linesInHunk).toBe 3 + expect(blame[0].signature.name).toBe 'Gabriel Isenberg' + expect(blame[0].signature.email).toBe 'gisenberg@gmail.com' + expect(blame[0].signature.when.time).toBe 1442122439 + expect(blame[0].signature.when.offset).toBe -420 + + it "throws an error if the file doesn't exist", -> + expect(-> repo.getBlame('missing.txt')).toThrow() diff --git a/src/repository.cc b/src/repository.cc index 7e9d76ae..6410cf24 100644 --- a/src/repository.cc +++ b/src/repository.cc @@ -61,6 +61,7 @@ void Repository::Init(Local target) { Nan::SetMethod(proto, "getReferences", Repository::GetReferences); Nan::SetMethod(proto, "checkoutRef", Repository::CheckoutReference); Nan::SetMethod(proto, "add", Repository::Add); + Nan::SetMethod(proto, "getBlame", Repository::GetBlame); target->Set(Nan::New("Repository").ToLocalChecked(), newTemplate->GetFunction()); @@ -910,6 +911,73 @@ NAN_METHOD(Repository::Add) { info.GetReturnValue().Set(Nan::New(true)); } +NAN_METHOD(Repository::GetBlame) { + Nan::HandleScope scope; + + git_repository* repository = GetRepository(info); + std::string path(*String::Utf8Value(info[0])); + + git_blame_options blameOpts = GIT_BLAME_OPTIONS_INIT; + git_blame *blame = NULL; + + if (git_blame_file(&blame, repository, path.c_str(), &blameOpts) != GIT_OK) { + const git_error* e = giterr_last(); + if (e != NULL) + return Nan::ThrowError(e->message); + else + return Nan::ThrowError("Unknown error getting blame."); + } + + // git_blob* blob = NULL; + // + // int getBlobResult = GetBlob(info, repository, blob); + // if (getBlobResult != 0) { + // git_blame_free(blame); + // return info.GetReturnValue().Set(Nan::Null()); + // } + + size_t hunkCount = git_blame_get_hunk_count(blame); + Local v8Ranges = Nan::New(hunkCount); + + for (size_t i = 0; i < hunkCount; i++) { + const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, i); + const git_signature *finalSig = hunk->final_signature; + char commitId[10] = {0}; + git_oid_tostr(commitId, sizeof(commitId), &hunk->final_commit_id); + + Local v8Range = Nan::New(); + Local signature = Nan::New(); + Local when = Nan::New(); + + when->Set(Nan::New("time").ToLocalChecked(), + Nan::New(finalSig->when.time)); + when->Set(Nan::New("offset").ToLocalChecked(), + Nan::New(finalSig->when.offset)); + + signature->Set(Nan::New("name").ToLocalChecked(), + Nan::New(finalSig->name).ToLocalChecked()); + signature->Set(Nan::New("email").ToLocalChecked(), + Nan::New(finalSig->email).ToLocalChecked()); + signature->Set(Nan::New("when").ToLocalChecked(), + when); + + v8Range->Set(Nan::New("commitId").ToLocalChecked(), + Nan::New(commitId).ToLocalChecked()); + v8Range->Set(Nan::New("startLineNumber").ToLocalChecked(), + Nan::New(hunk->final_start_line_number)); + v8Range->Set(Nan::New("linesInHunk").ToLocalChecked(), + Nan::New(hunk->lines_in_hunk)); + v8Range->Set(Nan::New("signature").ToLocalChecked(), + signature); + + v8Ranges->Set(i, v8Range); + } + + git_blame_free(blame); + + info.GetReturnValue().Set(v8Ranges); +} + Repository::Repository(Local path) { Nan::HandleScope scope; diff --git a/src/repository.h b/src/repository.h index 9f78f36b..7e0baea0 100644 --- a/src/repository.h +++ b/src/repository.h @@ -59,6 +59,7 @@ class Repository : public Nan::ObjectWrap { static NAN_METHOD(GetReferences); static NAN_METHOD(CheckoutReference); static NAN_METHOD(Add); + static NAN_METHOD(GetBlame); static int StatusCallback(const char *path, unsigned int status, void *payload);