mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
feat(bun:git): Initial implementation of bun:git module with libgit2
- Add libgit2 CMake build target - Register bun:git native module - Implement JSGitRepository class with: - Constructor and static find/init methods - Properties: path, gitDir, isBare, isClean, head, branch - Methods: getCommit, status, add, commit - Implement JSGitCommit class with: - Properties: sha, shortSha, message, summary, author, committer, tree, parents - Methods: parent, isAncestorOf - Implement JSGitBranch class with: - Properties: name, fullName, isRemote, isHead, commit, upstream, ahead, behind - Methods: delete, rename - Implement JSGitSignature class with: - Properties: name, email, date, timezone - Methods: toString Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,7 @@ set(BUN_DEPENDENCIES
|
||||
Cares
|
||||
Highway
|
||||
LibDeflate
|
||||
Libgit2
|
||||
LolHtml
|
||||
Lshpack
|
||||
Mimalloc
|
||||
|
||||
42
cmake/targets/BuildLibgit2.cmake
Normal file
42
cmake/targets/BuildLibgit2.cmake
Normal file
@@ -0,0 +1,42 @@
|
||||
register_repository(
|
||||
NAME
|
||||
libgit2
|
||||
REPOSITORY
|
||||
libgit2/libgit2
|
||||
TAG
|
||||
v1.9.0
|
||||
)
|
||||
|
||||
set(LIBGIT2_CMAKE_ARGS
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DBUILD_TESTS=OFF
|
||||
-DBUILD_CLI=OFF
|
||||
-DUSE_SSH=OFF
|
||||
-DUSE_HTTPS=OFF
|
||||
-DUSE_BUNDLED_ZLIB=OFF
|
||||
-DUSE_ICONV=OFF
|
||||
-DREGEX_BACKEND=builtin
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DCMAKE_INSTALL_LIBDIR=lib
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND LIBGIT2_CMAKE_ARGS
|
||||
-DWIN32_LEAKCHECK=OFF
|
||||
)
|
||||
endif()
|
||||
|
||||
register_cmake_command(
|
||||
TARGET
|
||||
libgit2
|
||||
TARGETS
|
||||
git2
|
||||
ARGS
|
||||
${LIBGIT2_CMAKE_ARGS}
|
||||
LIB_PATH
|
||||
lib
|
||||
LIBRARIES
|
||||
git2
|
||||
INCLUDES
|
||||
include
|
||||
)
|
||||
523
src/bun.js/bindings/JSGit.h
Normal file
523
src/bun.js/bindings/JSGit.h
Normal file
@@ -0,0 +1,523 @@
|
||||
#pragma once
|
||||
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSDestructibleObject.h>
|
||||
#include <JavaScriptCore/JSNonFinalObject.h>
|
||||
#include <JavaScriptCore/InternalFunction.h>
|
||||
#include <git2.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
// Forward declarations
|
||||
class JSGitRepository;
|
||||
class JSGitCommit;
|
||||
class JSGitBranch;
|
||||
class JSGitRemote;
|
||||
class JSGitDiff;
|
||||
class JSGitStatusEntry;
|
||||
class JSGitIndex;
|
||||
class JSGitConfig;
|
||||
class JSGitStash;
|
||||
class JSGitWorktree;
|
||||
class JSGitBlob;
|
||||
class JSGitSignature;
|
||||
|
||||
// Initialize libgit2 (call once at startup)
|
||||
void initializeLibgit2();
|
||||
void shutdownLibgit2();
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepository - Core repository class
|
||||
// ============================================================================
|
||||
|
||||
class JSGitRepository : public JSC::JSDestructibleObject {
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
|
||||
public:
|
||||
JSGitRepository(JSC::VM& vm, JSC::Structure* structure, git_repository* repo)
|
||||
: Base(vm, structure)
|
||||
, m_repo(repo)
|
||||
{
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return subspaceForImpl(vm);
|
||||
}
|
||||
|
||||
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSGitRepository* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, git_repository* repo)
|
||||
{
|
||||
JSGitRepository* object = new (NotNull, JSC::allocateCell<JSGitRepository>(vm)) JSGitRepository(vm, structure, repo);
|
||||
object->finishCreation(vm, globalObject);
|
||||
return object;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
static void destroy(JSCell* thisObject)
|
||||
{
|
||||
static_cast<JSGitRepository*>(thisObject)->~JSGitRepository();
|
||||
}
|
||||
|
||||
~JSGitRepository();
|
||||
|
||||
git_repository* repo() const { return m_repo; }
|
||||
|
||||
private:
|
||||
git_repository* m_repo;
|
||||
};
|
||||
|
||||
class JSGitRepositoryPrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSGitRepositoryPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSGitRepositoryPrototype* ptr = new (NotNull, JSC::allocateCell<JSGitRepositoryPrototype>(vm)) JSGitRepositoryPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSGitRepositoryPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSGitRepositoryPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSGitRepositoryConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
static JSGitRepositoryConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSGitRepositoryPrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSGitRepositoryPrototype* prototype);
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSGitRepositoryConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSGitRepositoryPrototype* prototype);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitCommit - Commit class
|
||||
// ============================================================================
|
||||
|
||||
class JSGitCommit : public JSC::JSDestructibleObject {
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
|
||||
public:
|
||||
JSGitCommit(JSC::VM& vm, JSC::Structure* structure, git_commit* commit, JSGitRepository* repo)
|
||||
: Base(vm, structure)
|
||||
, m_commit(commit)
|
||||
, m_repo(repo)
|
||||
{
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return subspaceForImpl(vm);
|
||||
}
|
||||
|
||||
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSGitCommit* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, git_commit* commit, JSGitRepository* repo)
|
||||
{
|
||||
JSGitCommit* object = new (NotNull, JSC::allocateCell<JSGitCommit>(vm)) JSGitCommit(vm, structure, commit, repo);
|
||||
object->finishCreation(vm, globalObject);
|
||||
return object;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
static void destroy(JSCell* thisObject)
|
||||
{
|
||||
static_cast<JSGitCommit*>(thisObject)->~JSGitCommit();
|
||||
}
|
||||
|
||||
~JSGitCommit();
|
||||
|
||||
git_commit* commit() const { return m_commit; }
|
||||
JSGitRepository* repository() const { return m_repo; }
|
||||
|
||||
private:
|
||||
git_commit* m_commit;
|
||||
JSGitRepository* m_repo;
|
||||
};
|
||||
|
||||
class JSGitCommitPrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSGitCommitPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSGitCommitPrototype* ptr = new (NotNull, JSC::allocateCell<JSGitCommitPrototype>(vm)) JSGitCommitPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSGitCommitPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSGitCommitPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSGitCommitConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
static JSGitCommitConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSGitCommitPrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSGitCommitConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSGitCommitPrototype* prototype);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitBranch - Branch class
|
||||
// ============================================================================
|
||||
|
||||
class JSGitBranch : public JSC::JSDestructibleObject {
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
|
||||
public:
|
||||
JSGitBranch(JSC::VM& vm, JSC::Structure* structure, git_reference* ref, JSGitRepository* repo, bool isRemote)
|
||||
: Base(vm, structure)
|
||||
, m_ref(ref)
|
||||
, m_repo(repo)
|
||||
, m_isRemote(isRemote)
|
||||
{
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return subspaceForImpl(vm);
|
||||
}
|
||||
|
||||
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSGitBranch* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, git_reference* ref, JSGitRepository* repo, bool isRemote)
|
||||
{
|
||||
JSGitBranch* object = new (NotNull, JSC::allocateCell<JSGitBranch>(vm)) JSGitBranch(vm, structure, ref, repo, isRemote);
|
||||
object->finishCreation(vm, globalObject);
|
||||
return object;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
static void destroy(JSCell* thisObject)
|
||||
{
|
||||
static_cast<JSGitBranch*>(thisObject)->~JSGitBranch();
|
||||
}
|
||||
|
||||
~JSGitBranch();
|
||||
|
||||
git_reference* ref() const { return m_ref; }
|
||||
JSGitRepository* repository() const { return m_repo; }
|
||||
bool isRemote() const { return m_isRemote; }
|
||||
|
||||
private:
|
||||
git_reference* m_ref;
|
||||
JSGitRepository* m_repo;
|
||||
bool m_isRemote;
|
||||
};
|
||||
|
||||
class JSGitBranchPrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSGitBranchPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSGitBranchPrototype* ptr = new (NotNull, JSC::allocateCell<JSGitBranchPrototype>(vm)) JSGitBranchPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSGitBranchPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSGitBranchPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSGitBranchConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
static JSGitBranchConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSGitBranchPrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSGitBranchConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSGitBranchPrototype* prototype);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitSignature - Signature class (author/committer info)
|
||||
// ============================================================================
|
||||
|
||||
class JSGitSignature : public JSC::JSNonFinalObject {
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
public:
|
||||
JSGitSignature(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
, m_name()
|
||||
, m_email()
|
||||
, m_time(0)
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSGitSignature, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSGitSignature* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, const git_signature* sig)
|
||||
{
|
||||
JSGitSignature* object = new (NotNull, JSC::allocateCell<JSGitSignature>(vm)) JSGitSignature(vm, structure);
|
||||
object->finishCreation(vm, globalObject, sig);
|
||||
return object;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, const git_signature* sig);
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
const String& email() const { return m_email; }
|
||||
git_time_t time() const { return m_time; }
|
||||
int offset() const { return m_offset; }
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
String m_email;
|
||||
git_time_t m_time;
|
||||
int m_offset;
|
||||
};
|
||||
|
||||
class JSGitSignaturePrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static JSGitSignaturePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSGitSignaturePrototype* ptr = new (NotNull, JSC::allocateCell<JSGitSignaturePrototype>(vm)) JSGitSignaturePrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSGitSignaturePrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
structure->setMayBePrototype(true);
|
||||
return structure;
|
||||
}
|
||||
|
||||
private:
|
||||
JSGitSignaturePrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSGitSignatureConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
|
||||
static JSGitSignatureConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSGitSignaturePrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSGitSignatureConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSGitSignaturePrototype* prototype);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Helper functions for class structure initialization
|
||||
// ============================================================================
|
||||
|
||||
void initJSGitRepositoryClassStructure(JSC::LazyClassStructure::Initializer& init);
|
||||
void initJSGitCommitClassStructure(JSC::LazyClassStructure::Initializer& init);
|
||||
void initJSGitBranchClassStructure(JSC::LazyClassStructure::Initializer& init);
|
||||
void initJSGitSignatureClassStructure(JSC::LazyClassStructure::Initializer& init);
|
||||
|
||||
} // namespace Bun
|
||||
379
src/bun.js/bindings/JSGitBranch.cpp
Normal file
379
src/bun.js/bindings/JSGitBranch.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
#include "root.h"
|
||||
#include "JSGit.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "JavaScriptCore/JSCJSValueInlines.h"
|
||||
#include "JavaScriptCore/ObjectConstructor.h"
|
||||
#include "wtf/text/WTFString.h"
|
||||
#include "helpers.h"
|
||||
#include <git2.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
// ============================================================================
|
||||
// JSGitBranch Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitBranch::s_info = { "Branch"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitBranch) };
|
||||
|
||||
JSGitBranch::~JSGitBranch()
|
||||
{
|
||||
if (m_ref) {
|
||||
git_reference_free(m_ref);
|
||||
m_ref = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JSGitBranch::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSGitBranch::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<JSGitBranch*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
visitor.append(thisObject->m_repo);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSGitBranch);
|
||||
|
||||
JSC::GCClient::IsoSubspace* JSGitBranch::subspaceForImpl(VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<JSGitBranch, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSGitBranch.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSGitBranch = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSGitBranch.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSGitBranch = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
// Getter: name
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_name, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "name"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* name = nullptr;
|
||||
int error = git_branch_name(&name, thisObject->ref());
|
||||
if (error < 0 || !name) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(name)));
|
||||
}
|
||||
|
||||
// Getter: fullName
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_fullName, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "fullName"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* name = git_reference_name(thisObject->ref());
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(name ? name : "")));
|
||||
}
|
||||
|
||||
// Getter: isRemote
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_isRemote, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "isRemote"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsBoolean(thisObject->isRemote()));
|
||||
}
|
||||
|
||||
// Getter: isHead
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_isHead, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "isHead"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsBoolean(git_branch_is_head(thisObject->ref())));
|
||||
}
|
||||
|
||||
// Getter: commit
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_commit, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Branch"_s, "commit"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* oid = git_reference_target(thisObject->ref());
|
||||
if (!oid) {
|
||||
// Symbolic reference, need to resolve
|
||||
git_reference* resolved = nullptr;
|
||||
int error = git_reference_resolve(&resolved, thisObject->ref());
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(err ? err->message : "Failed to resolve branch")));
|
||||
return {};
|
||||
}
|
||||
oid = git_reference_target(resolved);
|
||||
git_reference_free(resolved);
|
||||
}
|
||||
|
||||
git_commit* commit = nullptr;
|
||||
int error = git_commit_lookup(&commit, thisObject->repository()->repo(), oid);
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(err ? err->message : "Failed to get commit")));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
return JSValue::encode(JSGitCommit::create(vm, lexicalGlobalObject, structure, commit, thisObject->repository()));
|
||||
}
|
||||
|
||||
// Getter: upstream
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_upstream, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Branch"_s, "upstream"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference* upstream = nullptr;
|
||||
int error = git_branch_upstream(&upstream, thisObject->ref());
|
||||
if (error < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
const git_error* err = git_error_last();
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(err ? err->message : "Failed to get upstream")));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitBranchStructure();
|
||||
return JSValue::encode(JSGitBranch::create(vm, lexicalGlobalObject, structure, upstream, thisObject->repository(), true));
|
||||
}
|
||||
|
||||
// Getter: ahead
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_ahead, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "ahead"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference* upstream = nullptr;
|
||||
int error = git_branch_upstream(&upstream, thisObject->ref());
|
||||
if (error < 0) {
|
||||
return JSValue::encode(jsNumber(0));
|
||||
}
|
||||
|
||||
size_t ahead = 0, behind = 0;
|
||||
const git_oid* localOid = git_reference_target(thisObject->ref());
|
||||
const git_oid* upstreamOid = git_reference_target(upstream);
|
||||
|
||||
if (localOid && upstreamOid) {
|
||||
git_graph_ahead_behind(&ahead, &behind, thisObject->repository()->repo(), localOid, upstreamOid);
|
||||
}
|
||||
|
||||
git_reference_free(upstream);
|
||||
return JSValue::encode(jsNumber(ahead));
|
||||
}
|
||||
|
||||
// Getter: behind
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitBranchGetter_behind, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "behind"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference* upstream = nullptr;
|
||||
int error = git_branch_upstream(&upstream, thisObject->ref());
|
||||
if (error < 0) {
|
||||
return JSValue::encode(jsNumber(0));
|
||||
}
|
||||
|
||||
size_t ahead = 0, behind = 0;
|
||||
const git_oid* localOid = git_reference_target(thisObject->ref());
|
||||
const git_oid* upstreamOid = git_reference_target(upstream);
|
||||
|
||||
if (localOid && upstreamOid) {
|
||||
git_graph_ahead_behind(&ahead, &behind, thisObject->repository()->repo(), localOid, upstreamOid);
|
||||
}
|
||||
|
||||
git_reference_free(upstream);
|
||||
return JSValue::encode(jsNumber(behind));
|
||||
}
|
||||
|
||||
// Method: delete(force?)
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitBranchProtoFunc_delete, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "delete"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
int error = git_branch_delete(thisObject->ref());
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(globalObject, scope, createError(globalObject, WTF::String::fromUTF8(err ? err->message : "Failed to delete branch")));
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
// Method: rename(newName: string)
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitBranchProtoFunc_rename, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitBranch*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Branch"_s, "rename"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(globalObject, scope, createError(globalObject, "rename requires a newName argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto newName = callFrame->argument(0).toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
git_reference* newRef = nullptr;
|
||||
int error = git_branch_move(&newRef, thisObject->ref(), newName.utf8().data(), 0);
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(globalObject, scope, createError(globalObject, WTF::String::fromUTF8(err ? err->message : "Failed to rename branch")));
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference_free(newRef);
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitBranch Prototype Table
|
||||
// ============================================================================
|
||||
|
||||
static const HashTableValue JSGitBranchPrototypeTableValues[] = {
|
||||
{ "name"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_name, 0 } },
|
||||
{ "fullName"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_fullName, 0 } },
|
||||
{ "isRemote"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_isRemote, 0 } },
|
||||
{ "isHead"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_isHead, 0 } },
|
||||
{ "commit"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_commit, 0 } },
|
||||
{ "upstream"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_upstream, 0 } },
|
||||
{ "ahead"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_ahead, 0 } },
|
||||
{ "behind"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitBranchGetter_behind, 0 } },
|
||||
{ "delete"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitBranchProtoFunc_delete, 0 } },
|
||||
{ "rename"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitBranchProtoFunc_rename, 1 } },
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitBranchPrototype Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitBranchPrototype::s_info = { "Branch"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitBranchPrototype) };
|
||||
|
||||
void JSGitBranchPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSGitBranch::info(), JSGitBranchPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitBranchConstructor Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitBranchConstructor::s_info = { "Branch"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitBranchConstructor) };
|
||||
|
||||
JSGitBranchConstructor* JSGitBranchConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSGitBranchPrototype* prototype)
|
||||
{
|
||||
JSGitBranchConstructor* constructor = new (NotNull, allocateCell<JSGitBranchConstructor>(vm)) JSGitBranchConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
void JSGitBranchConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, JSGitBranchPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 0, "Branch"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitBranchConstructor::construct(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Branch cannot be directly constructed"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitBranchConstructor::call(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Branch cannot be called as a function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Class Structure Initialization
|
||||
// ============================================================================
|
||||
|
||||
void initJSGitBranchClassStructure(LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototype = JSGitBranchPrototype::create(init.vm, init.global, JSGitBranchPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSGitBranch::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSGitBranchConstructor::create(init.vm, init.global, JSGitBranchConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
374
src/bun.js/bindings/JSGitCommit.cpp
Normal file
374
src/bun.js/bindings/JSGitCommit.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "root.h"
|
||||
#include "JSGit.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "JavaScriptCore/JSCJSValueInlines.h"
|
||||
#include "JavaScriptCore/ObjectConstructor.h"
|
||||
#include "wtf/text/WTFString.h"
|
||||
#include "helpers.h"
|
||||
#include <git2.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
// ============================================================================
|
||||
// JSGitCommit Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitCommit::s_info = { "Commit"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitCommit) };
|
||||
|
||||
JSGitCommit::~JSGitCommit()
|
||||
{
|
||||
if (m_commit) {
|
||||
git_commit_free(m_commit);
|
||||
m_commit = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JSGitCommit::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void JSGitCommit::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<JSGitCommit*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
visitor.append(thisObject->m_repo);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(JSGitCommit);
|
||||
|
||||
JSC::GCClient::IsoSubspace* JSGitCommit::subspaceForImpl(VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<JSGitCommit, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSGitCommit.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSGitCommit = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSGitCommit.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSGitCommit = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
// Helper to format OID as hex string
|
||||
static WTF::String oidToString(const git_oid* oid)
|
||||
{
|
||||
char buf[GIT_OID_SHA1_HEXSIZE + 1];
|
||||
git_oid_tostr(buf, sizeof(buf), oid);
|
||||
return WTF::String::fromUTF8(buf);
|
||||
}
|
||||
|
||||
// Getter: sha
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_sha, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "sha"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* oid = git_commit_id(thisObject->commit());
|
||||
return JSValue::encode(jsString(vm, oidToString(oid)));
|
||||
}
|
||||
|
||||
// Getter: shortSha
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_shortSha, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "shortSha"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* oid = git_commit_id(thisObject->commit());
|
||||
char buf[8];
|
||||
git_oid_tostr(buf, sizeof(buf), oid);
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(buf)));
|
||||
}
|
||||
|
||||
// Getter: message
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_message, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "message"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* message = git_commit_message(thisObject->commit());
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(message ? message : "")));
|
||||
}
|
||||
|
||||
// Getter: summary
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_summary, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "summary"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* summary = git_commit_summary(thisObject->commit());
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(summary ? summary : "")));
|
||||
}
|
||||
|
||||
// Getter: author
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_author, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Commit"_s, "author"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_signature* author = git_commit_author(thisObject->commit());
|
||||
auto* structure = globalObject->JSGitSignatureStructure();
|
||||
return JSValue::encode(JSGitSignature::create(vm, lexicalGlobalObject, structure, author));
|
||||
}
|
||||
|
||||
// Getter: committer
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_committer, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Commit"_s, "committer"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_signature* committer = git_commit_committer(thisObject->commit());
|
||||
auto* structure = globalObject->JSGitSignatureStructure();
|
||||
return JSValue::encode(JSGitSignature::create(vm, lexicalGlobalObject, structure, committer));
|
||||
}
|
||||
|
||||
// Getter: tree
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_tree, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "tree"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* treeId = git_commit_tree_id(thisObject->commit());
|
||||
return JSValue::encode(jsString(vm, oidToString(treeId)));
|
||||
}
|
||||
|
||||
// Getter: parents
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitCommitGetter_parents, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Commit"_s, "parents"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
unsigned int parentCount = git_commit_parentcount(thisObject->commit());
|
||||
JSArray* result = constructEmptyArray(lexicalGlobalObject, nullptr, parentCount);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
for (unsigned int i = 0; i < parentCount; i++) {
|
||||
git_commit* parent = nullptr;
|
||||
int error = git_commit_parent(&parent, thisObject->commit(), i);
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(err ? err->message : "Failed to get parent commit")));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
result->putDirectIndex(lexicalGlobalObject, i, JSGitCommit::create(vm, lexicalGlobalObject, structure, parent, thisObject->repository()));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
return JSValue::encode(result);
|
||||
}
|
||||
|
||||
// Method: parent(n?) -> Commit | null
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitCommitProtoFunc_parent, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Commit"_s, "parent"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
unsigned int n = 0;
|
||||
if (callFrame->argumentCount() > 0 && !callFrame->argument(0).isUndefined()) {
|
||||
n = callFrame->argument(0).toUInt32(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
git_commit* parent = nullptr;
|
||||
int error = git_commit_parent(&parent, thisObject->commit(), n);
|
||||
if (error < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
const git_error* err = git_error_last();
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(err ? err->message : "Failed to get parent commit")));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
return JSValue::encode(JSGitCommit::create(vm, lexicalGlobalObject, structure, parent, thisObject->repository()));
|
||||
}
|
||||
|
||||
// Method: isAncestorOf(other: Commit | string) -> boolean
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitCommitProtoFunc_isAncestorOf, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitCommit*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Commit"_s, "isAncestorOf"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(globalObject, scope, createError(globalObject, "isAncestorOf requires a commit argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* ancestorOid = git_commit_id(thisObject->commit());
|
||||
git_oid descendantOid;
|
||||
|
||||
JSValue otherArg = callFrame->argument(0);
|
||||
if (auto* otherCommit = jsDynamicCast<JSGitCommit*>(otherArg)) {
|
||||
git_oid_cpy(&descendantOid, git_commit_id(otherCommit->commit()));
|
||||
} else {
|
||||
auto refString = otherArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
git_object* obj = nullptr;
|
||||
int error = git_revparse_single(&obj, thisObject->repository()->repo(), refString.utf8().data());
|
||||
if (error < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(globalObject, scope, createError(globalObject, WTF::String::fromUTF8(err ? err->message : "Invalid ref")));
|
||||
return {};
|
||||
}
|
||||
git_oid_cpy(&descendantOid, git_object_id(obj));
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
int result = git_graph_descendant_of(thisObject->repository()->repo(), &descendantOid, ancestorOid);
|
||||
if (result < 0) {
|
||||
const git_error* err = git_error_last();
|
||||
throwException(globalObject, scope, createError(globalObject, WTF::String::fromUTF8(err ? err->message : "Failed to check ancestry")));
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsBoolean(result == 1));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitCommit Prototype Table
|
||||
// ============================================================================
|
||||
|
||||
static const HashTableValue JSGitCommitPrototypeTableValues[] = {
|
||||
{ "sha"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_sha, 0 } },
|
||||
{ "shortSha"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_shortSha, 0 } },
|
||||
{ "message"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_message, 0 } },
|
||||
{ "summary"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_summary, 0 } },
|
||||
{ "author"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_author, 0 } },
|
||||
{ "committer"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_committer, 0 } },
|
||||
{ "tree"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_tree, 0 } },
|
||||
{ "parents"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitCommitGetter_parents, 0 } },
|
||||
{ "parent"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitCommitProtoFunc_parent, 0 } },
|
||||
{ "isAncestorOf"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitCommitProtoFunc_isAncestorOf, 1 } },
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitCommitPrototype Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitCommitPrototype::s_info = { "Commit"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitCommitPrototype) };
|
||||
|
||||
void JSGitCommitPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSGitCommit::info(), JSGitCommitPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitCommitConstructor Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitCommitConstructor::s_info = { "Commit"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitCommitConstructor) };
|
||||
|
||||
JSGitCommitConstructor* JSGitCommitConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSGitCommitPrototype* prototype)
|
||||
{
|
||||
JSGitCommitConstructor* constructor = new (NotNull, allocateCell<JSGitCommitConstructor>(vm)) JSGitCommitConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
void JSGitCommitConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, JSGitCommitPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 0, "Commit"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitCommitConstructor::construct(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Commit cannot be directly constructed"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitCommitConstructor::call(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Commit cannot be called as a function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Class Structure Initialization
|
||||
// ============================================================================
|
||||
|
||||
void initJSGitCommitClassStructure(LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototype = JSGitCommitPrototype::create(init.vm, init.global, JSGitCommitPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSGitCommit::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSGitCommitConstructor::create(init.vm, init.global, JSGitCommitConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
730
src/bun.js/bindings/JSGitRepository.cpp
Normal file
730
src/bun.js/bindings/JSGitRepository.cpp
Normal file
@@ -0,0 +1,730 @@
|
||||
#include "root.h"
|
||||
#include "JSGit.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "JavaScriptCore/JSCJSValueInlines.h"
|
||||
#include "JavaScriptCore/ObjectConstructor.h"
|
||||
#include "wtf/text/WTFString.h"
|
||||
#include "helpers.h"
|
||||
#include "BunClientData.h"
|
||||
#include <git2.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
// libgit2 initialization
|
||||
static std::once_flag s_libgit2InitFlag;
|
||||
|
||||
void initializeLibgit2()
|
||||
{
|
||||
std::call_once(s_libgit2InitFlag, []() {
|
||||
git_libgit2_init();
|
||||
});
|
||||
}
|
||||
|
||||
void shutdownLibgit2()
|
||||
{
|
||||
git_libgit2_shutdown();
|
||||
}
|
||||
|
||||
// Helper to throw git errors
|
||||
static void throwGitError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, int errorCode)
|
||||
{
|
||||
const git_error* err = git_error_last();
|
||||
WTF::String message;
|
||||
if (err && err->message) {
|
||||
message = WTF::String::fromUTF8(err->message);
|
||||
} else {
|
||||
message = makeString("Git error: "_s, errorCode);
|
||||
}
|
||||
throwException(globalObject, scope, createError(globalObject, message));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepository Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitRepository::s_info = { "Repository"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitRepository) };
|
||||
|
||||
JSGitRepository::~JSGitRepository()
|
||||
{
|
||||
if (m_repo) {
|
||||
git_repository_free(m_repo);
|
||||
m_repo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void JSGitRepository::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
}
|
||||
|
||||
JSC::GCClient::IsoSubspace* JSGitRepository::subspaceForImpl(VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<JSGitRepository, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForJSGitRepository.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSGitRepository = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForJSGitRepository.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSGitRepository = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepository Prototype Methods and Getters
|
||||
// ============================================================================
|
||||
|
||||
// Getter: path
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_path, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "path"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* path = git_repository_workdir(thisObject->repo());
|
||||
if (!path) {
|
||||
path = git_repository_path(thisObject->repo());
|
||||
}
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(path)));
|
||||
}
|
||||
|
||||
// Getter: gitDir
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_gitDir, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "gitDir"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* path = git_repository_path(thisObject->repo());
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(path)));
|
||||
}
|
||||
|
||||
// Getter: isBare
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_isBare, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "isBare"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsBoolean(git_repository_is_bare(thisObject->repo())));
|
||||
}
|
||||
|
||||
// Getter: isClean
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_isClean, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "isClean"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
|
||||
git_status_list* statusList = nullptr;
|
||||
int error = git_status_list_new(&statusList, thisObject->repo(), &opts);
|
||||
if (error < 0) {
|
||||
throwGitError(globalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t count = git_status_list_entrycount(statusList);
|
||||
git_status_list_free(statusList);
|
||||
|
||||
return JSValue::encode(jsBoolean(count == 0));
|
||||
}
|
||||
|
||||
// Getter: head (returns the HEAD commit)
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_head, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Repository"_s, "head"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference* headRef = nullptr;
|
||||
int error = git_repository_head(&headRef, thisObject->repo());
|
||||
if (error < 0) {
|
||||
if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
const git_oid* oid = git_reference_target(headRef);
|
||||
git_commit* commit = nullptr;
|
||||
error = git_commit_lookup(&commit, thisObject->repo(), oid);
|
||||
git_reference_free(headRef);
|
||||
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
return JSValue::encode(JSGitCommit::create(vm, lexicalGlobalObject, structure, commit, thisObject));
|
||||
}
|
||||
|
||||
// Getter: branch (returns the current branch or null if detached)
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitRepositoryGetter_branch, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Repository"_s, "branch"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_reference* headRef = nullptr;
|
||||
int error = git_repository_head(&headRef, thisObject->repo());
|
||||
if (error < 0) {
|
||||
if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (git_reference_is_branch(headRef)) {
|
||||
auto* structure = globalObject->JSGitBranchStructure();
|
||||
return JSValue::encode(JSGitBranch::create(vm, lexicalGlobalObject, structure, headRef, thisObject, false));
|
||||
}
|
||||
|
||||
git_reference_free(headRef);
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
// Method: getCommit(ref: string) -> Commit | null
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryProtoFunc_getCommit, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Repository"_s, "getCommit"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "getCommit requires a ref argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto refString = callFrame->argument(0).toWTFString(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
git_object* obj = nullptr;
|
||||
int error = git_revparse_single(&obj, thisObject->repo(), refString.utf8().data());
|
||||
if (error < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_commit* commit = nullptr;
|
||||
error = git_commit_lookup(&commit, thisObject->repo(), git_object_id(obj));
|
||||
git_object_free(obj);
|
||||
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
return JSValue::encode(JSGitCommit::create(vm, lexicalGlobalObject, structure, commit, thisObject));
|
||||
}
|
||||
|
||||
// Method: status(options?) -> StatusEntry[]
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryProtoFunc_status, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "status"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||
|
||||
// Parse options if provided
|
||||
if (callFrame->argumentCount() > 0 && !callFrame->argument(0).isUndefinedOrNull()) {
|
||||
JSObject* options = callFrame->argument(0).toObject(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSValue includeUntracked = options->get(globalObject, Identifier::fromString(vm, "includeUntracked"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!includeUntracked.isUndefined() && !includeUntracked.toBoolean(globalObject)) {
|
||||
opts.flags &= ~(GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS);
|
||||
}
|
||||
|
||||
JSValue includeIgnored = options->get(globalObject, Identifier::fromString(vm, "includeIgnored"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!includeIgnored.isUndefined() && includeIgnored.toBoolean(globalObject)) {
|
||||
opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
|
||||
}
|
||||
}
|
||||
|
||||
git_status_list* statusList = nullptr;
|
||||
int error = git_status_list_new(&statusList, thisObject->repo(), &opts);
|
||||
if (error < 0) {
|
||||
throwGitError(globalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t count = git_status_list_entrycount(statusList);
|
||||
JSArray* result = constructEmptyArray(globalObject, nullptr, count);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const git_status_entry* entry = git_status_byindex(statusList, i);
|
||||
JSObject* entryObj = constructEmptyObject(globalObject);
|
||||
|
||||
const char* path = entry->head_to_index ? entry->head_to_index->new_file.path
|
||||
: entry->index_to_workdir ? entry->index_to_workdir->new_file.path
|
||||
: nullptr;
|
||||
if (path) {
|
||||
entryObj->putDirect(vm, Identifier::fromString(vm, "path"_s), jsString(vm, WTF::String::fromUTF8(path)));
|
||||
}
|
||||
|
||||
// Index status
|
||||
WTF::String indexStatus = "unmodified"_s;
|
||||
if (entry->status & GIT_STATUS_INDEX_NEW) indexStatus = "added"_s;
|
||||
else if (entry->status & GIT_STATUS_INDEX_MODIFIED) indexStatus = "modified"_s;
|
||||
else if (entry->status & GIT_STATUS_INDEX_DELETED) indexStatus = "deleted"_s;
|
||||
else if (entry->status & GIT_STATUS_INDEX_RENAMED) indexStatus = "renamed"_s;
|
||||
else if (entry->status & GIT_STATUS_INDEX_TYPECHANGE) indexStatus = "typechange"_s;
|
||||
entryObj->putDirect(vm, Identifier::fromString(vm, "indexStatus"_s), jsString(vm, indexStatus));
|
||||
|
||||
// Worktree status
|
||||
WTF::String wtStatus = "unmodified"_s;
|
||||
if (entry->status & GIT_STATUS_WT_NEW) wtStatus = "untracked"_s;
|
||||
else if (entry->status & GIT_STATUS_WT_MODIFIED) wtStatus = "modified"_s;
|
||||
else if (entry->status & GIT_STATUS_WT_DELETED) wtStatus = "deleted"_s;
|
||||
else if (entry->status & GIT_STATUS_WT_RENAMED) wtStatus = "renamed"_s;
|
||||
else if (entry->status & GIT_STATUS_WT_TYPECHANGE) wtStatus = "typechange"_s;
|
||||
else if (entry->status & GIT_STATUS_IGNORED) wtStatus = "ignored"_s;
|
||||
else if (entry->status & GIT_STATUS_CONFLICTED) wtStatus = "unmerged"_s;
|
||||
entryObj->putDirect(vm, Identifier::fromString(vm, "workTreeStatus"_s), jsString(vm, wtStatus));
|
||||
|
||||
// Original path for renames
|
||||
const char* origPath = entry->head_to_index ? entry->head_to_index->old_file.path
|
||||
: entry->index_to_workdir ? entry->index_to_workdir->old_file.path
|
||||
: nullptr;
|
||||
if (origPath && path && strcmp(origPath, path) != 0) {
|
||||
entryObj->putDirect(vm, Identifier::fromString(vm, "origPath"_s), jsString(vm, WTF::String::fromUTF8(origPath)));
|
||||
} else {
|
||||
entryObj->putDirect(vm, Identifier::fromString(vm, "origPath"_s), jsNull());
|
||||
}
|
||||
|
||||
result->putDirectIndex(globalObject, i, entryObj);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
git_status_list_free(statusList);
|
||||
return JSValue::encode(result);
|
||||
}
|
||||
|
||||
// Method: add(paths: string | string[])
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryProtoFunc_add, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Repository"_s, "add"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(globalObject, scope, createError(globalObject, "add requires a path argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
git_index* index = nullptr;
|
||||
int error = git_repository_index(&index, thisObject->repo());
|
||||
if (error < 0) {
|
||||
throwGitError(globalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSValue pathsArg = callFrame->argument(0);
|
||||
if (pathsArg.isString()) {
|
||||
auto pathStr = pathsArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
error = git_index_add_bypath(index, pathStr.utf8().data());
|
||||
} else if (isArray(globalObject, pathsArg)) {
|
||||
JSArray* paths = jsCast<JSArray*>(pathsArg);
|
||||
uint32_t length = paths->length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
JSValue pathValue = paths->get(globalObject, i);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
auto pathStr = pathValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
error = git_index_add_bypath(index, pathStr.utf8().data());
|
||||
if (error < 0) break;
|
||||
}
|
||||
} else {
|
||||
git_index_free(index);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "paths must be a string or array of strings"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (error < 0) {
|
||||
git_index_free(index);
|
||||
throwGitError(globalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
error = git_index_write(index);
|
||||
git_index_free(index);
|
||||
|
||||
if (error < 0) {
|
||||
throwGitError(globalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
// Method: commit(message: string, options?) -> Commit
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryProtoFunc_commit, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitRepository*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*lexicalGlobalObject, scope, "Repository"_s, "commit"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "commit requires a message argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto message = callFrame->argument(0).toWTFString(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Get the index
|
||||
git_index* index = nullptr;
|
||||
int error = git_repository_index(&index, thisObject->repo());
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Write the index as a tree
|
||||
git_oid treeId;
|
||||
error = git_index_write_tree(&treeId, index);
|
||||
git_index_free(index);
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Lookup the tree
|
||||
git_tree* tree = nullptr;
|
||||
error = git_tree_lookup(&tree, thisObject->repo(), &treeId);
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the default signature
|
||||
git_signature* sig = nullptr;
|
||||
error = git_signature_default(&sig, thisObject->repo());
|
||||
if (error < 0) {
|
||||
git_tree_free(tree);
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the parent commit (HEAD)
|
||||
git_commit* parent = nullptr;
|
||||
git_reference* headRef = nullptr;
|
||||
error = git_repository_head(&headRef, thisObject->repo());
|
||||
if (error == 0) {
|
||||
const git_oid* parentId = git_reference_target(headRef);
|
||||
error = git_commit_lookup(&parent, thisObject->repo(), parentId);
|
||||
git_reference_free(headRef);
|
||||
if (error < 0) {
|
||||
git_signature_free(sig);
|
||||
git_tree_free(tree);
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
} else if (error != GIT_EUNBORNBRANCH && error != GIT_ENOTFOUND) {
|
||||
git_signature_free(sig);
|
||||
git_tree_free(tree);
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create the commit
|
||||
git_oid commitId;
|
||||
const git_commit* parents[] = { parent };
|
||||
size_t parentCount = parent ? 1 : 0;
|
||||
|
||||
error = git_commit_create(
|
||||
&commitId,
|
||||
thisObject->repo(),
|
||||
"HEAD",
|
||||
sig,
|
||||
sig,
|
||||
nullptr,
|
||||
message.utf8().data(),
|
||||
tree,
|
||||
parentCount,
|
||||
parents
|
||||
);
|
||||
|
||||
git_signature_free(sig);
|
||||
git_tree_free(tree);
|
||||
if (parent) git_commit_free(parent);
|
||||
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Return the new commit
|
||||
git_commit* newCommit = nullptr;
|
||||
error = git_commit_lookup(&newCommit, thisObject->repo(), &commitId);
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitCommitStructure();
|
||||
return JSValue::encode(JSGitCommit::create(vm, lexicalGlobalObject, structure, newCommit, thisObject));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepository Prototype Table
|
||||
// ============================================================================
|
||||
|
||||
static const HashTableValue JSGitRepositoryPrototypeTableValues[] = {
|
||||
{ "path"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_path, 0 } },
|
||||
{ "gitDir"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_gitDir, 0 } },
|
||||
{ "isBare"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_isBare, 0 } },
|
||||
{ "isClean"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_isClean, 0 } },
|
||||
{ "head"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_head, 0 } },
|
||||
{ "branch"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitRepositoryGetter_branch, 0 } },
|
||||
{ "getCommit"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryProtoFunc_getCommit, 1 } },
|
||||
{ "status"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryProtoFunc_status, 0 } },
|
||||
{ "add"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryProtoFunc_add, 1 } },
|
||||
{ "commit"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryProtoFunc_commit, 1 } },
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepositoryPrototype Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitRepositoryPrototype::s_info = { "Repository"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitRepositoryPrototype) };
|
||||
|
||||
void JSGitRepositoryPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSGitRepository::info(), JSGitRepositoryPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitRepositoryConstructor Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitRepositoryConstructor::s_info = { "Repository"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitRepositoryConstructor) };
|
||||
|
||||
JSGitRepositoryConstructor* JSGitRepositoryConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSGitRepositoryPrototype* prototype)
|
||||
{
|
||||
JSGitRepositoryConstructor* constructor = new (NotNull, allocateCell<JSGitRepositoryConstructor>(vm)) JSGitRepositoryConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
void JSGitRepositoryConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, JSGitRepositoryPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 1, "Repository"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
// Constructor: new Repository(path?)
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitRepositoryConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
initializeLibgit2();
|
||||
|
||||
WTF::String pathStr = "."_s;
|
||||
if (callFrame->argumentCount() > 0 && !callFrame->argument(0).isUndefinedOrNull()) {
|
||||
pathStr = callFrame->argument(0).toWTFString(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
// Discover the repository
|
||||
git_buf repoPath = GIT_BUF_INIT;
|
||||
int error = git_repository_discover(&repoPath, pathStr.utf8().data(), 0, nullptr);
|
||||
if (error < 0) {
|
||||
git_buf_dispose(&repoPath);
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Not a git repository"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
git_repository* repo = nullptr;
|
||||
error = git_repository_open(&repo, repoPath.ptr);
|
||||
git_buf_dispose(&repoPath);
|
||||
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitRepositoryStructure();
|
||||
return JSValue::encode(JSGitRepository::create(vm, lexicalGlobalObject, structure, repo));
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitRepositoryConstructor::call(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Repository constructor cannot be called as a function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Static method: Repository.find(startPath?) -> Repository | null
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryConstructorFunc_find, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
initializeLibgit2();
|
||||
|
||||
WTF::String pathStr = "."_s;
|
||||
if (callFrame->argumentCount() > 0 && !callFrame->argument(0).isUndefinedOrNull()) {
|
||||
pathStr = callFrame->argument(0).toWTFString(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
git_buf repoPath = GIT_BUF_INIT;
|
||||
int error = git_repository_discover(&repoPath, pathStr.utf8().data(), 0, nullptr);
|
||||
if (error < 0) {
|
||||
git_buf_dispose(&repoPath);
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
git_repository* repo = nullptr;
|
||||
error = git_repository_open(&repo, repoPath.ptr);
|
||||
git_buf_dispose(&repoPath);
|
||||
|
||||
if (error < 0) {
|
||||
return JSValue::encode(jsNull());
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitRepositoryStructure();
|
||||
return JSValue::encode(JSGitRepository::create(vm, lexicalGlobalObject, structure, repo));
|
||||
}
|
||||
|
||||
// Static method: Repository.init(path, options?) -> Repository
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitRepositoryConstructorFunc_init, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
initializeLibgit2();
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "init requires a path argument"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto pathStr = callFrame->argument(0).toWTFString(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
bool isBare = false;
|
||||
if (callFrame->argumentCount() > 1 && !callFrame->argument(1).isUndefinedOrNull()) {
|
||||
JSObject* options = callFrame->argument(1).toObject(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSValue bareValue = options->get(lexicalGlobalObject, Identifier::fromString(vm, "bare"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
isBare = bareValue.toBoolean(lexicalGlobalObject);
|
||||
}
|
||||
|
||||
git_repository* repo = nullptr;
|
||||
int error = git_repository_init(&repo, pathStr.utf8().data(), isBare ? 1 : 0);
|
||||
if (error < 0) {
|
||||
throwGitError(lexicalGlobalObject, scope, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* structure = globalObject->JSGitRepositoryStructure();
|
||||
return JSValue::encode(JSGitRepository::create(vm, lexicalGlobalObject, structure, repo));
|
||||
}
|
||||
|
||||
static const HashTableValue JSGitRepositoryConstructorTableValues[] = {
|
||||
{ "find"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryConstructorFunc_find, 0 } },
|
||||
{ "init"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitRepositoryConstructorFunc_init, 1 } },
|
||||
};
|
||||
|
||||
void JSGitRepositoryConstructor::initializeProperties(VM& vm, JSGlobalObject* globalObject, JSGitRepositoryPrototype* prototype)
|
||||
{
|
||||
reifyStaticProperties(vm, info(), JSGitRepositoryConstructorTableValues, *this);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Class Structure Initialization
|
||||
// ============================================================================
|
||||
|
||||
void initJSGitRepositoryClassStructure(LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototype = JSGitRepositoryPrototype::create(init.vm, init.global, JSGitRepositoryPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSGitRepository::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSGitRepositoryConstructor::create(init.vm, init.global, JSGitRepositoryConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
constructor->initializeProperties(init.vm, init.global, prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
190
src/bun.js/bindings/JSGitSignature.cpp
Normal file
190
src/bun.js/bindings/JSGitSignature.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
#include "root.h"
|
||||
#include "JSGit.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "JavaScriptCore/JSCJSValueInlines.h"
|
||||
#include "JavaScriptCore/ObjectConstructor.h"
|
||||
#include "JavaScriptCore/DateInstance.h"
|
||||
#include "wtf/text/WTFString.h"
|
||||
#include "helpers.h"
|
||||
#include <git2.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
// ============================================================================
|
||||
// JSGitSignature Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitSignature::s_info = { "Signature"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitSignature) };
|
||||
|
||||
void JSGitSignature::finishCreation(VM& vm, JSGlobalObject* globalObject, const git_signature* sig)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
if (sig) {
|
||||
m_name = WTF::String::fromUTF8(sig->name ? sig->name : "");
|
||||
m_email = WTF::String::fromUTF8(sig->email ? sig->email : "");
|
||||
m_time = sig->when.time;
|
||||
m_offset = sig->when.offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Getter: name
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitSignatureGetter_name, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitSignature*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Signature"_s, "name"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsString(vm, thisObject->name()));
|
||||
}
|
||||
|
||||
// Getter: email
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitSignatureGetter_email, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitSignature*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Signature"_s, "email"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(jsString(vm, thisObject->email()));
|
||||
}
|
||||
|
||||
// Getter: date
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitSignatureGetter_date, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitSignature*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Signature"_s, "date"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Convert git_time_t (seconds since epoch) to JavaScript Date (milliseconds)
|
||||
double ms = static_cast<double>(thisObject->time()) * 1000.0;
|
||||
return JSValue::encode(DateInstance::create(vm, globalObject->dateStructure(), ms));
|
||||
}
|
||||
|
||||
// Getter: timezone
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsGitSignatureGetter_timezone, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitSignature*>(JSValue::decode(thisValue));
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Signature"_s, "timezone"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
int offset = thisObject->offset();
|
||||
int hours = offset / 60;
|
||||
int minutes = offset % 60;
|
||||
if (minutes < 0) minutes = -minutes;
|
||||
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%+03d:%02d", hours, minutes);
|
||||
return JSValue::encode(jsString(vm, WTF::String::fromUTF8(buf)));
|
||||
}
|
||||
|
||||
// Method: toString() -> "Name <email>"
|
||||
JSC_DEFINE_HOST_FUNCTION(jsGitSignatureProtoFunc_toString, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSGitSignature*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
throwThisTypeError(*globalObject, scope, "Signature"_s, "toString"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
WTF::String result = makeString(thisObject->name(), " <"_s, thisObject->email(), ">"_s);
|
||||
return JSValue::encode(jsString(vm, result));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitSignature Prototype Table
|
||||
// ============================================================================
|
||||
|
||||
static const HashTableValue JSGitSignaturePrototypeTableValues[] = {
|
||||
{ "name"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitSignatureGetter_name, 0 } },
|
||||
{ "email"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitSignatureGetter_email, 0 } },
|
||||
{ "date"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitSignatureGetter_date, 0 } },
|
||||
{ "timezone"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsGitSignatureGetter_timezone, 0 } },
|
||||
{ "toString"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsGitSignatureProtoFunc_toString, 0 } },
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSGitSignaturePrototype Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitSignaturePrototype::s_info = { "Signature"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitSignaturePrototype) };
|
||||
|
||||
void JSGitSignaturePrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSGitSignature::info(), JSGitSignaturePrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSGitSignatureConstructor Implementation
|
||||
// ============================================================================
|
||||
|
||||
const ClassInfo JSGitSignatureConstructor::s_info = { "Signature"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSGitSignatureConstructor) };
|
||||
|
||||
JSGitSignatureConstructor* JSGitSignatureConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSGitSignaturePrototype* prototype)
|
||||
{
|
||||
JSGitSignatureConstructor* constructor = new (NotNull, allocateCell<JSGitSignatureConstructor>(vm)) JSGitSignatureConstructor(vm, structure);
|
||||
constructor->finishCreation(vm, globalObject, prototype);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
void JSGitSignatureConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, JSGitSignaturePrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 0, "Signature"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitSignatureConstructor::construct(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Signature cannot be directly constructed"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSGitSignatureConstructor::call(JSGlobalObject* globalObject, CallFrame*)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Signature cannot be called as a function"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Class Structure Initialization
|
||||
// ============================================================================
|
||||
|
||||
void initJSGitSignatureClassStructure(LazyClassStructure::Initializer& init)
|
||||
{
|
||||
auto* prototype = JSGitSignaturePrototype::create(init.vm, init.global, JSGitSignaturePrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSGitSignature::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSGitSignatureConstructor::create(init.vm, init.global, JSGitSignatureConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
49
src/bun.js/modules/BunGitModule.h
Normal file
49
src/bun.js/modules/BunGitModule.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "root.h"
|
||||
#include "_NativeModule.h"
|
||||
|
||||
namespace Zig {
|
||||
using namespace WebCore;
|
||||
using namespace JSC;
|
||||
|
||||
// Forward declarations for the git classes
|
||||
JSC::JSValue createGitRepositoryConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitCommitConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitBranchConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitRemoteConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitDiffConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitStatusEntryConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitIndexConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitConfigConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitStashConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitWorktreeConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitBlobConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitSignatureConstructor(JSC::JSGlobalObject* globalObject);
|
||||
JSC::JSValue createGitErrorConstructor(JSC::JSGlobalObject* globalObject);
|
||||
|
||||
DEFINE_NATIVE_MODULE(BunGit)
|
||||
{
|
||||
INIT_NATIVE_MODULE(13);
|
||||
|
||||
auto* zigGlobalObject = jsCast<Zig::GlobalObject*>(globalObject);
|
||||
|
||||
// Main classes
|
||||
put(JSC::Identifier::fromString(vm, "Repository"_s), zigGlobalObject->JSGitRepositoryConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Commit"_s), zigGlobalObject->JSGitCommitConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Branch"_s), zigGlobalObject->JSGitBranchConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Remote"_s), zigGlobalObject->JSGitRemoteConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Diff"_s), zigGlobalObject->JSGitDiffConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "StatusEntry"_s), zigGlobalObject->JSGitStatusEntryConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Index"_s), zigGlobalObject->JSGitIndexConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Config"_s), zigGlobalObject->JSGitConfigConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Stash"_s), zigGlobalObject->JSGitStashConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Worktree"_s), zigGlobalObject->JSGitWorktreeConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Blob"_s), zigGlobalObject->JSGitBlobConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "Signature"_s), zigGlobalObject->JSGitSignatureConstructor());
|
||||
put(JSC::Identifier::fromString(vm, "GitError"_s), zigGlobalObject->JSGitErrorConstructor());
|
||||
|
||||
RETURN_NATIVE_MODULE();
|
||||
}
|
||||
|
||||
} // namespace Zig
|
||||
@@ -28,6 +28,7 @@
|
||||
macro("bun:test"_s, BunTest) \
|
||||
macro("bun:jsc"_s, BunJSC) \
|
||||
macro("bun:app"_s, BunApp) \
|
||||
macro("bun:git"_s, BunGit) \
|
||||
macro("node:buffer"_s, NodeBuffer) \
|
||||
macro("node:constants"_s, NodeConstants) \
|
||||
macro("node:string_decoder"_s, NodeStringDecoder) \
|
||||
|
||||
Reference in New Issue
Block a user