├── .gitignore ├── cmake ├── InstallConfig.cmake ├── Version.cmake └── InstallRules.cmake ├── include └── git2cpp │ ├── repo_fwd.h │ ├── id_to_str.h │ ├── initializer.h │ ├── odb_object.h │ ├── odb.h │ ├── blob.h │ ├── buffer.h │ ├── blame.h │ ├── signature.h │ ├── pathspec.h │ ├── annotated_commit.h │ ├── tag.h │ ├── reference.h │ ├── config.h │ ├── revspec.h │ ├── object.h │ ├── internal │ ├── format.h │ └── optional.h │ ├── str_array.h │ ├── tagged_mask.h │ ├── revwalker.h │ ├── remote.h │ ├── index.h │ ├── commit.h │ ├── status.h │ ├── submodule.h │ ├── diff.h │ ├── tree.h │ ├── repo.h │ └── error.h ├── .gitmodules ├── src ├── buffer.cpp ├── initializer.cpp ├── annotated_commit.cpp ├── blob.cpp ├── id_to_str.cpp ├── odb_object.cpp ├── blame.cpp ├── signature.cpp ├── config.cpp ├── odb.cpp ├── revspec.cpp ├── reference.cpp ├── submodule.cpp ├── tag.cpp ├── object.cpp ├── revwalker.cpp ├── commit.cpp ├── remote.cpp ├── tree.cpp ├── status.cpp ├── index.cpp ├── diff.cpp └── repo.cpp ├── TODO.md ├── examples ├── CMakeLists.txt ├── showindex.cpp ├── rev-parse.cpp ├── branch.cpp ├── commit-graph-generator.cpp ├── rev-list.cpp ├── add.cpp ├── ls-files.cpp ├── clone.cpp ├── cat-file.cpp ├── remote.cpp ├── blame.cpp ├── init.cpp ├── checkout.cpp ├── log.cpp ├── diff.cpp └── status.cpp ├── README.md ├── appveyor.yml ├── .clang-format ├── test.sh └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | build 4 | *.user 5 | cmake-build*/ 6 | .idea/ 7 | -------------------------------------------------------------------------------- /cmake/InstallConfig.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/git2cppTargets.cmake") 2 | -------------------------------------------------------------------------------- /include/git2cpp/repo_fwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace git 4 | { 5 | struct Repository; 6 | } 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/libgit2"] 2 | path = libs/libgit2 3 | url = https://github.com/libgit2/libgit2.git 4 | -------------------------------------------------------------------------------- /src/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/buffer.h" 2 | 3 | namespace git 4 | { 5 | Buffer::~Buffer() 6 | { 7 | git_buf_dispose(&buf_); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | libgit2cpp TODO 2 | =============== 3 | 4 | * standardize command-line processing for all examples 5 | * most importantly --git-dir 6 | * unit tests 7 | * embed test repo 8 | * remove dep on libgit2/tests/resources/testrepo.git 9 | -------------------------------------------------------------------------------- /src/initializer.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/initializer.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | Initializer::Initializer() 8 | { 9 | git_libgit2_init(); 10 | } 11 | 12 | Initializer::~Initializer() 13 | { 14 | git_libgit2_shutdown(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /include/git2cpp/id_to_str.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | std::string id_to_str(git_oid const & oid); 10 | 11 | std::string id_to_str(git_oid const & oid, size_t digits_num); 12 | 13 | git_oid str_to_id(const char * str); 14 | } 15 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE LIBGIT2CPP_EXAMPLE_SOURCES *.cpp) 2 | foreach(EXAMPLE_SOURCE ${LIBGIT2CPP_EXAMPLE_SOURCES}) 3 | get_filename_component(EXAMPLE_NAME ${EXAMPLE_SOURCE} NAME_WE) 4 | add_executable(${EXAMPLE_NAME}-cpp ${EXAMPLE_SOURCE}) 5 | target_link_libraries(${EXAMPLE_NAME}-cpp ${package}) 6 | endforeach() 7 | -------------------------------------------------------------------------------- /include/git2cpp/initializer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace git 4 | { 5 | struct Initializer 6 | { 7 | Initializer(); 8 | ~Initializer(); 9 | 10 | Initializer(Initializer const &) = delete; 11 | Initializer & operator=(Initializer const &) = delete; 12 | }; 13 | } 14 | 15 | #define auto_git_initializer \ 16 | ::git::Initializer git_initializer; \ 17 | (void)git_initializer 18 | -------------------------------------------------------------------------------- /include/git2cpp/odb_object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace git 7 | { 8 | struct OdbObject 9 | { 10 | explicit OdbObject(git_odb_object * obj) 11 | : obj_(obj) 12 | {} 13 | 14 | git_object_t type() const; 15 | unsigned char const * data() const; 16 | size_t size() const; 17 | 18 | private: 19 | struct Destroy { void operator() (git_odb_object *) const; }; 20 | std::unique_ptr obj_; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/annotated_commit.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/annotated_commit.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | git_oid const& AnnotatedCommit::commit_id() const 8 | { 9 | return *git_annotated_commit_id(ptr()); 10 | } 11 | 12 | char const* AnnotatedCommit::commit_ref() const 13 | { 14 | return git_annotated_commit_ref(ptr()); 15 | } 16 | 17 | void AnnotatedCommit::Destroy::operator()(git_annotated_commit* commit) const 18 | { 19 | git_annotated_commit_free(commit); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/blob.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/blob.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | Blob::Blob(git_blob * blob) 8 | : blob_(blob) 9 | { 10 | } 11 | 12 | std::size_t Blob::size() const 13 | { 14 | return static_cast(git_blob_rawsize(ptr())); 15 | } 16 | 17 | const void * Blob::content() const 18 | { 19 | return git_blob_rawcontent(ptr()); 20 | } 21 | 22 | void Blob::Destroy::operator()(git_blob * blob) const 23 | { 24 | git_blob_free(blob); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /include/git2cpp/odb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "odb_object.h" 4 | 5 | #include 6 | 7 | struct git_odb; 8 | 9 | namespace git 10 | { 11 | struct Odb 12 | { 13 | OdbObject read(git_oid const & oid) const; 14 | git_oid write(const void * data, size_t len, git_object_t type); 15 | 16 | private: 17 | friend struct Repository; 18 | explicit Odb(git_repository * repo); 19 | 20 | private: 21 | struct Destroy { void operator() (git_odb* odb) const; }; 22 | std::unique_ptr odb_; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /include/git2cpp/blob.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct git_blob; 7 | 8 | namespace git 9 | { 10 | struct Blob 11 | { 12 | explicit Blob(git_blob *); 13 | 14 | std::size_t size() const; 15 | const void * content() const; 16 | 17 | private: 18 | friend struct Repository; 19 | 20 | git_blob const * ptr() const { return blob_.get(); } 21 | 22 | private: 23 | struct Destroy { void operator() (git_blob *) const; }; 24 | 25 | std::unique_ptr blob_; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /include/git2cpp/buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | struct Buffer 8 | { 9 | Buffer(Buffer const &) = delete; 10 | Buffer & operator=(Buffer const &) = delete; 11 | 12 | Buffer(Buffer &&) noexcept; 13 | 14 | explicit Buffer(git_buf buf) 15 | : buf_(buf) 16 | {} 17 | 18 | ~Buffer(); 19 | 20 | explicit operator bool() const { return ptr() != nullptr; } 21 | 22 | const char * ptr() const { return buf_.ptr; } 23 | 24 | private: 25 | git_buf buf_; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /include/git2cpp/blame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct git_blame; 6 | struct git_blame_hunk; 7 | 8 | namespace git 9 | { 10 | struct Blame 11 | { 12 | uint32_t hunk_count() const; 13 | 14 | const git_blame_hunk * hunk_byindex(uint32_t index) const; 15 | 16 | const git_blame_hunk * hunk_byline(size_t lineno) const; 17 | 18 | explicit Blame(git_blame * blame) 19 | : blame_(blame) 20 | {} 21 | 22 | private: 23 | struct Destroy { void operator() (git_blame *) const; }; 24 | std::unique_ptr blame_; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/id_to_str.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/id_to_str.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | std::string id_to_str(git_oid const & oid) 8 | { 9 | return id_to_str(oid, GIT_OID_SHA1_HEXSIZE); 10 | } 11 | 12 | std::string id_to_str(git_oid const & oid, size_t digits_num) 13 | { 14 | char buf[GIT_OID_SHA1_HEXSIZE + 1]; 15 | git_oid_tostr(buf, sizeof(buf), &oid); 16 | return std::string(buf, buf + digits_num); 17 | } 18 | 19 | git_oid str_to_id(const char * str) 20 | { 21 | git_oid res; 22 | git_oid_fromstr(&res, str); 23 | return res; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/odb_object.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/odb_object.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | void OdbObject::Destroy::operator()(git_odb_object* obj) const 8 | { 9 | git_odb_object_free(obj); 10 | } 11 | 12 | git_object_t OdbObject::type() const 13 | { 14 | return git_odb_object_type(obj_.get()); 15 | } 16 | 17 | unsigned char const * OdbObject::data() const 18 | { 19 | return reinterpret_cast(git_odb_object_data(obj_.get())); 20 | } 21 | 22 | size_t OdbObject::size() const 23 | { 24 | return git_odb_object_size(obj_.get()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /include/git2cpp/signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct git_signature; 8 | struct git_repository; 9 | 10 | namespace git 11 | { 12 | struct Signature 13 | { 14 | Signature(const char * name, const char * email, git_time_t time, int offset); 15 | 16 | private: 17 | friend struct Repository; 18 | explicit Signature(git_repository * repo); 19 | 20 | git_signature const * ptr() const { return sig_.get(); } 21 | 22 | private: 23 | struct Destroy { void operator() (git_signature*) const; }; 24 | std::unique_ptr sig_; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/blame.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/blame.h" 2 | #include "git2/blame.h" 3 | 4 | namespace git 5 | { 6 | uint32_t Blame::hunk_count() const 7 | { 8 | return git_blame_get_hunk_count(blame_.get()); 9 | } 10 | 11 | const git_blame_hunk* Blame::hunk_byindex(uint32_t index) const 12 | { 13 | return git_blame_get_hunk_byindex(blame_.get(), index); 14 | } 15 | 16 | const git_blame_hunk* Blame::hunk_byline(size_t lineno) const 17 | { 18 | return git_blame_get_hunk_byline(blame_.get(), lineno); 19 | } 20 | 21 | void Blame::Destroy::operator() (git_blame * blame) const 22 | { 23 | git_blame_free(blame); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /include/git2cpp/pathspec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "error.h" 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | struct Pathspec 10 | { 11 | explicit Pathspec(git_strarray const & pathspec) 12 | { 13 | if (git_pathspec_new(&ps_, &pathspec)) 14 | throw pathspec_new_error(); 15 | } 16 | 17 | git_pathspec * ptr() const { return ps_; } 18 | 19 | ~Pathspec() 20 | { 21 | git_pathspec_free(ps_); 22 | } 23 | 24 | Pathspec(Pathspec const &) = delete; 25 | Pathspec & operator=(Pathspec const &) = delete; 26 | 27 | private: 28 | git_pathspec * ps_; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /include/git2cpp/annotated_commit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct git_annotated_commit; 6 | struct git_oid; 7 | 8 | namespace git 9 | { 10 | struct AnnotatedCommit 11 | { 12 | AnnotatedCommit(git_annotated_commit * commit) 13 | : commit_(commit) 14 | {} 15 | 16 | git_oid const & commit_id() const; 17 | char const * commit_ref() const; 18 | 19 | private: 20 | friend struct Repository; 21 | git_annotated_commit * ptr() const { return commit_.get(); } 22 | 23 | private: 24 | struct Destroy { void operator() (git_annotated_commit*) const; }; 25 | std::unique_ptr commit_; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /include/git2cpp/tag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct git_tag; 7 | struct git_oid; 8 | 9 | namespace git 10 | { 11 | struct Repository; 12 | struct Object; 13 | 14 | struct Tag 15 | { 16 | explicit Tag(git_tag *); 17 | 18 | Object target(Repository const &) const; 19 | 20 | git_oid const & target_id() const; 21 | git_object_t target_type() const; 22 | 23 | const char * name() const; 24 | const char * message() const; 25 | git_signature const * tagger() const; 26 | 27 | private: 28 | struct Destroy { void operator() (git_tag*) const; }; 29 | std::unique_ptr tag_; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libgit2cpp 2 | ========== 3 | 4 | [![AppVeryor Build Status](https://ci.appveyor.com/api/projects/status/ps6e0s4nfnw5afh4/branch/master?svg=true)](https://ci.appveyor.com/project/AndreyG/libgit2cpp/branch/master) 5 | 6 | C++ wrapper for libgit2. It contains libgit2 as submodule (libs/libgit2). 7 | 8 | Building libgit2cpp - Using CMake 9 | ================================= 10 | 11 | $ mkdir build && cd build 12 | $ cmake .. 13 | $ make 14 | 15 | Supporting CMake options: `USE_BOOST`, `BUNDLE_LIBGIT2`, `BUILD_LIBGIT2CPP_EXAMPLES`. 16 | 17 | Testing 18 | ======= 19 | 20 | $ cd build 21 | $ ./test.sh [path_to_libgit2_testrepo] 22 | 23 | e.g. 24 | 25 | $ mkdir build && cd build 26 | $ cmake .. 27 | $ make 28 | $ ./test.sh .. ../libs/libgit2/tests/resources/testrepo.git 29 | -------------------------------------------------------------------------------- /src/signature.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "git2cpp/error.h" 4 | #include "git2cpp/signature.h" 5 | 6 | #include 7 | 8 | namespace git 9 | { 10 | Signature::Signature(git_repository * repo) 11 | { 12 | git_signature * sig; 13 | if (git_signature_default(&sig, repo) != GIT_OK) 14 | throw signature_create_error(); 15 | sig_.reset(sig); 16 | } 17 | 18 | Signature::Signature(const char * name, const char * email, git_time_t time, int offset) 19 | { 20 | git_signature * sig; 21 | if (git_signature_new(&sig, name, email, time, offset) != GIT_OK) 22 | throw signature_create_error(); 23 | sig_.reset(sig); 24 | } 25 | 26 | void Signature::Destroy::operator()(git_signature* sig) const 27 | { 28 | git_signature_free(sig); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | environment: 8 | matrix: 9 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 10 | GENERATOR: "Visual Studio 15 2017" 11 | ARCH: 32 12 | BOOST: 1_65_1 13 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 14 | GENERATOR: "Visual Studio 15 2017 Win64" 15 | ARCH: 64 16 | BOOST: 1_65_1 17 | 18 | clone_script: 19 | - cmd: git clone --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% 20 | - cmd: cd %APPVEYOR_BUILD_FOLDER% 21 | - cmd: git checkout -q %APPVEYOR_REPO_COMMIT% 22 | - cmd: git submodule update --init 23 | 24 | build_script: 25 | - ps: | 26 | mkdir build 27 | cd build 28 | cmake -D BUILD_CLAR=OFF -D BUILD_EXAMPLES=OFF -D USE_BOOST=ON -D BOOST_ROOT="C:\Libraries\boost_$env:BOOST" .. -G"$env:GENERATOR" 29 | cmake --build . --config Debug 30 | -------------------------------------------------------------------------------- /include/git2cpp/reference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct git_reference; 7 | struct git_oid; 8 | 9 | namespace git 10 | { 11 | struct Reference 12 | { 13 | Reference() 14 | : ref_(nullptr) 15 | {} 16 | 17 | explicit Reference(git_reference * ref); 18 | 19 | explicit operator bool() const { return ref_ != nullptr; } 20 | 21 | const char * name() const; 22 | git_reference_t type() const; 23 | git_oid const & target() const; 24 | const char * symbolic_target() const; 25 | 26 | bool has_same_target_as(Reference const & other) const; 27 | 28 | private: 29 | friend struct Repository; 30 | git_reference * ptr() const { return ref_.get(); } 31 | 32 | private: 33 | struct Destroy { void operator() (git_reference*) const; }; 34 | std::unique_ptr ref_; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/config.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/config.h" 2 | #include "git2cpp/error.h" 3 | 4 | #include 5 | 6 | namespace git 7 | { 8 | Config::Config(std::string const & filename) 9 | { 10 | git_config * cfg; 11 | if (git_config_open_ondisk(&cfg, filename.c_str())) 12 | throw config_open_error(); 13 | cfg_.reset(cfg); 14 | } 15 | 16 | void Config::Destroy::operator()(git_config* cfg) const 17 | { 18 | git_config_free(cfg); 19 | } 20 | 21 | std::string Config::operator[](const char * key) const 22 | { 23 | const char * res; 24 | if (git_config_get_string(&res, cfg_.get(), key)) 25 | throw no_such_key_error(key); 26 | return res; 27 | } 28 | 29 | int Config::get_int(const char * key) const 30 | { 31 | int res; 32 | if (git_config_get_int32(&res, cfg_.get(), key)) 33 | throw no_such_key_error(key); 34 | return res; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/odb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "git2cpp/error.h" 5 | #include "git2cpp/odb.h" 6 | 7 | namespace git 8 | { 9 | Odb::Odb(git_repository * repo) 10 | { 11 | git_odb * odb; 12 | if (git_repository_odb(&odb, repo)) 13 | throw odb_open_error(); 14 | odb_.reset(odb); 15 | } 16 | 17 | void Odb::Destroy::operator()(git_odb* odb) const 18 | { 19 | git_odb_free(odb); 20 | } 21 | 22 | OdbObject Odb::read(git_oid const & oid) const 23 | { 24 | git_odb_object * obj; 25 | if (git_odb_read(&obj, odb_.get(), &oid)) 26 | throw odb_read_error(oid); 27 | return OdbObject(obj); 28 | } 29 | 30 | git_oid Odb::write(const void * data, size_t len, git_object_t type) 31 | { 32 | git_oid res; 33 | if (git_odb_write(&res, odb_.get(), data, len, type)) 34 | throw odb_write_error(); 35 | return res; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | ColumnLimit: 0 4 | BreakBeforeBraces: Custom 5 | BraceWrapping: 6 | AfterNamespace: true 7 | AfterClass: true 8 | AfterStruct: true 9 | AfterEnum: true 10 | AfterFunction: true 11 | AfterControlStatement: true 12 | BeforeElse: true 13 | BeforeCatch: true 14 | SplitEmptyFunction: false 15 | SplitEmptyRecord: false 16 | NamespaceIndentation: All 17 | AccessModifierOffset: -4 18 | IndentWidth: 4 19 | TabWidth: 4 20 | UseTab: Never 21 | PointerAlignment: Middle 22 | BreakConstructorInitializers: BeforeComma 23 | BreakBeforeTernaryOperators: true 24 | CompactNamespaces: true 25 | FixNamespaceComments: false 26 | IncludeBlocks: Preserve 27 | IncludeCategories: 28 | - Regex: '^"git2cpp/' 29 | Priority: 2 30 | - Regex: '^' 33 | Priority: 4 34 | - Regex: '.*' 35 | Priority: 1 36 | ... 37 | 38 | -------------------------------------------------------------------------------- /include/git2cpp/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct git_config; 8 | 9 | namespace git 10 | { 11 | struct Config 12 | { 13 | explicit Config(std::string const & filename); 14 | 15 | struct no_such_key_error : std::exception 16 | { 17 | explicit no_such_key_error(std::string key) 18 | : key_(std::move(key)) 19 | {} 20 | 21 | const char * what() const noexcept override 22 | { 23 | return "no such key in config"; 24 | } 25 | 26 | std::string const & key() const { return key_; } 27 | 28 | private: 29 | std::string key_; 30 | }; 31 | 32 | std::string operator[](const char * key) const; 33 | 34 | int get_int(const char * key) const; 35 | 36 | private: 37 | struct Destroy { void operator() (git_config*) const; }; 38 | std::unique_ptr cfg_; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /include/git2cpp/revspec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "object.h" 4 | 5 | extern "C" { 6 | #include 7 | } 8 | 9 | namespace git 10 | { 11 | struct Repository; 12 | 13 | struct Revspec 14 | { 15 | struct Range 16 | { 17 | Object from, to; 18 | 19 | Range(git_object * single, Repository const & repo) 20 | : from(single, repo) 21 | {} 22 | 23 | explicit Range(git_revspec const & revspec, Repository const & repo) 24 | : from(revspec.from, repo) 25 | , to(revspec.to, repo) 26 | {} 27 | }; 28 | 29 | Object const * single() const; 30 | Range const * range() const; 31 | 32 | Object * single(); 33 | 34 | unsigned int flags() const; 35 | 36 | Revspec(git_object * single, Repository const &); 37 | Revspec(git_revspec const &, Repository const &); 38 | 39 | private: 40 | unsigned int flags_ = 0; 41 | Range revspec_; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/revspec.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/revspec.h" 2 | 3 | namespace git 4 | { 5 | Revspec::Revspec(git_object * single, Repository const & repo) 6 | : flags_(GIT_REVSPEC_SINGLE) 7 | , revspec_(single, repo) 8 | { 9 | } 10 | 11 | Revspec::Revspec(git_revspec const & revspec, Repository const & repo) 12 | : flags_(revspec.flags) 13 | , revspec_((revspec.flags & GIT_REVSPEC_SINGLE) 14 | ? Range(revspec.from, repo) 15 | : Range(revspec, repo)) 16 | { 17 | } 18 | 19 | Object * Revspec::single() 20 | { 21 | if (flags_ & GIT_REVSPEC_SINGLE) 22 | return &revspec_.from; 23 | else 24 | return nullptr; 25 | } 26 | 27 | Revspec::Range const * Revspec::range() const 28 | { 29 | if (flags_ & GIT_REVSPEC_SINGLE) 30 | return nullptr; 31 | else 32 | return &revspec_; 33 | } 34 | 35 | unsigned int Revspec::flags() const 36 | { 37 | return flags_; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/reference.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/reference.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace git 9 | { 10 | void Reference::Destroy::operator()(git_reference* ref) const 11 | { 12 | git_reference_free(ref); 13 | } 14 | 15 | Reference::Reference(git_reference * ref) 16 | : ref_(ref) 17 | { 18 | } 19 | 20 | const char * Reference::name() const 21 | { 22 | return git_reference_name(ptr()); 23 | } 24 | 25 | git_reference_t Reference::type() const 26 | { 27 | return git_reference_type(ptr()); 28 | } 29 | 30 | git_oid const & Reference::target() const 31 | { 32 | assert(type() == GIT_REFERENCE_DIRECT); 33 | return *git_reference_target(ptr()); 34 | } 35 | 36 | const char * Reference::symbolic_target() const 37 | { 38 | return git_reference_symbolic_target(ptr()); 39 | } 40 | 41 | bool Reference::has_same_target_as(Reference const & other) const 42 | { 43 | return git_oid_equal(&target(), &other.target()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /include/git2cpp/object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct git_object; 7 | struct git_oid; 8 | struct git_tree; 9 | struct git_tag; 10 | 11 | namespace git 12 | { 13 | struct Repository; 14 | 15 | struct Blob; 16 | struct Commit; 17 | struct Tree; 18 | struct Tag; 19 | 20 | struct Object 21 | { 22 | Object() = default; 23 | Object(git_object * obj, Repository const &); 24 | 25 | explicit operator bool() const { return obj_.operator bool(); } 26 | 27 | git_object_t type() const; 28 | git_oid const & id() const; 29 | 30 | Commit to_commit() /*&&*/; 31 | Tree to_tree() /*&&*/; 32 | Blob to_blob() /*&&*/; 33 | Tag to_tag() /*&&*/; 34 | 35 | private: 36 | git_blob * as_blob(); 37 | git_commit * as_commit(); 38 | git_tree * as_tree(); 39 | git_tag * as_tag(); 40 | 41 | private: 42 | struct Destroy { void operator() (git_object*) const; }; 43 | std::unique_ptr obj_; 44 | Repository const * repo_ = nullptr; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /src/submodule.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/submodule.h" 2 | 3 | #include 4 | 5 | #include "git2cpp/error.h" 6 | 7 | namespace git 8 | { 9 | Submodule::Submodule(git_submodule * sm, git_repository * repo) 10 | : sm_(sm) 11 | , repo_(repo) 12 | { 13 | } 14 | 15 | void Submodule::Destroy::operator()(git_submodule* sm) const 16 | { 17 | git_submodule_free(sm); 18 | } 19 | 20 | Submodule::status Submodule::get_status() const 21 | { 22 | unsigned int res; 23 | if (auto err = git_submodule_status(&res, repo_, git_submodule_name(sm_.get()), git_submodule_ignore(sm_.get()))) 24 | throw error_t("git_submodule_status failed with error code " + std::to_string(err)); 25 | else 26 | return static_cast(res); 27 | } 28 | 29 | namespace 30 | { 31 | unsigned int to_raw(Submodule::status s) 32 | { 33 | return static_cast(s); 34 | } 35 | } 36 | 37 | bool contains(Submodule::status set, Submodule::status element) 38 | { 39 | return to_raw(set) & to_raw(element); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/tag.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/tag.h" 2 | #include "git2cpp/object.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace git 9 | { 10 | Tag::Tag(git_tag * tag) 11 | : tag_(tag) 12 | { 13 | } 14 | 15 | void Tag::Destroy::operator()(git_tag* tag) const 16 | { 17 | git_tag_free(tag); 18 | } 19 | 20 | Object Tag::target(Repository const & repo) const 21 | { 22 | git_object * obj; 23 | git_tag_target(&obj, tag_.get()); 24 | return Object(obj, repo); 25 | } 26 | 27 | git_oid const & Tag::target_id() const 28 | { 29 | return *git_tag_target_id(tag_.get()); 30 | } 31 | 32 | git_object_t Tag::target_type() const 33 | { 34 | return git_tag_target_type(tag_.get()); 35 | } 36 | 37 | const char * Tag::name() const 38 | { 39 | return git_tag_name(tag_.get()); 40 | } 41 | 42 | const char * Tag::message() const 43 | { 44 | return git_tag_message(tag_.get()); 45 | } 46 | 47 | git_signature const * Tag::tagger() const 48 | { 49 | return git_tag_tagger(tag_.get()); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /include/git2cpp/internal/format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef USE_BOOST 7 | #include 8 | #endif 9 | 10 | namespace git { 11 | namespace internal 12 | { 13 | 14 | #ifndef USE_BOOST 15 | template 16 | std::string format(const char * fmt, Args&& ... args) 17 | { 18 | auto size = std::snprintf(nullptr, 0, fmt, args...); 19 | std::string result(size, 0); 20 | std::snprintf(&result[0], size + 1, fmt, args...); 21 | return result; 22 | } 23 | #else 24 | inline boost::format & format_apply(boost::format & fmt) 25 | { 26 | return fmt; 27 | } 28 | 29 | template 30 | boost::format & format_apply(boost::format & fmt, T && data, Args&& ... args) 31 | { 32 | return format_apply(fmt % data, std::forward(args)...); 33 | } 34 | 35 | template 36 | std::string format(const char * fmt_str, Args&& ... args) 37 | { 38 | boost::format fmt(fmt_str); 39 | return str(format_apply(fmt, std::forward(args)...)); 40 | } 41 | #endif 42 | 43 | }} 44 | -------------------------------------------------------------------------------- /include/git2cpp/str_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | struct StrArray 10 | { 11 | explicit StrArray(git_strarray const & str_array) 12 | : str_array_(str_array) 13 | {} 14 | 15 | StrArray(StrArray const &) = delete; 16 | StrArray& operator =(StrArray const &) = delete; 17 | 18 | StrArray(StrArray && other) noexcept 19 | : str_array_(other.str_array_) 20 | { 21 | std::memset(&other.str_array_, 0, sizeof(git_strarray)); 22 | } 23 | 24 | StrArray& operator =(StrArray && other) noexcept 25 | { 26 | std::swap(str_array_, other.str_array_); 27 | return *this; 28 | } 29 | 30 | size_t count() const { return str_array_.count; } 31 | 32 | const char * operator[](size_t i) const 33 | { 34 | return str_array_.strings[i]; 35 | } 36 | 37 | ~StrArray() 38 | { 39 | git_strarray_dispose(&str_array_); 40 | } 41 | 42 | private: 43 | git_strarray str_array_; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /include/git2cpp/tagged_mask.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace git 4 | { 5 | template 6 | struct tagged_mask_t 7 | { 8 | typedef tagged_mask_t self_t; 9 | 10 | explicit tagged_mask_t(RawType value = RawType()) 11 | : value_(value) 12 | {} 13 | 14 | RawType value() const { return value_; } 15 | 16 | explicit operator bool() const { return value_; } 17 | 18 | self_t & operator|=(self_t other) 19 | { 20 | value_ |= other.value_; 21 | return *this; 22 | } 23 | 24 | friend self_t operator~(self_t x) { return self_t(~x.value_); } 25 | 26 | friend self_t operator|(self_t x, self_t y) { return self_t(x.value_ | y.value_); } 27 | friend self_t operator&(self_t x, self_t y) { return self_t(x.value_ & y.value_); } 28 | friend self_t operator^(self_t x, self_t y) { return self_t(x.value_ ^ y.value_); } 29 | 30 | friend bool operator==(self_t x, self_t y) { return x.value_ == y.value_; } 31 | friend bool operator!=(self_t x, self_t y) { return x.value_ != y.value_; } 32 | 33 | private: 34 | RawType value_; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /cmake/Version.cmake: -------------------------------------------------------------------------------- 1 | set(VERSION "0.0.0" CACHE STRING "libgit2cpp Version") 2 | set(COMMIT_HASH "") 3 | set(COMMIT_COUNT 0) 4 | 5 | find_package(Git) 6 | 7 | if (Git_FOUND AND VERSION STREQUAL "0.0.0") 8 | message(STATUS "No version defined, fetching one from git") 9 | 10 | execute_process( 11 | COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD 12 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 13 | OUTPUT_VARIABLE COMMIT_COUNT 14 | OUTPUT_STRIP_TRAILING_WHITESPACE 15 | ERROR_QUIET 16 | ) 17 | 18 | execute_process( 19 | COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 20 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 21 | OUTPUT_VARIABLE COMMIT_HASH 22 | OUTPUT_STRIP_TRAILING_WHITESPACE 23 | ERROR_QUIET 24 | ) 25 | 26 | set(VERSION "r${COMMIT_COUNT}.${COMMIT_HASH}") 27 | endif () 28 | 29 | message(STATUS "Version: ${VERSION}") 30 | string(REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9])?)(.*)" "\\1" VERSION_FOR_CMAKE "${VERSION}") 31 | message(STATUS "Version for CMake: ${VERSION_FOR_CMAKE}") 32 | 33 | #configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/version.h) 34 | -------------------------------------------------------------------------------- /include/git2cpp/revwalker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commit.h" 4 | #include "tagged_mask.h" 5 | #include 6 | 7 | namespace git 8 | { 9 | struct Repository; 10 | 11 | namespace revwalker 12 | { 13 | namespace sorting 14 | { 15 | typedef tagged_mask_t type; 16 | 17 | extern const type none; 18 | extern const type topological; 19 | extern const type time; 20 | extern const type reverse; 21 | } 22 | } 23 | 24 | struct RevWalker 25 | { 26 | RevWalker(git_revwalk * walker, Repository const & repo) 27 | : walker_(walker) 28 | , repo_(&repo) 29 | {} 30 | 31 | void sort(revwalker::sorting::type); 32 | void simplify_first_parent(); 33 | 34 | void push_head() const; 35 | void hide(git_oid const &) const; 36 | void push(git_oid const &) const; 37 | 38 | Commit next() const; 39 | bool next(char * id_buffer) const; 40 | 41 | private: 42 | struct Destroy { void operator() (git_revwalk*) const; }; 43 | std::unique_ptr walker_; 44 | Repository const * repo_; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /include/git2cpp/remote.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct git_remote; 6 | struct git_oid; 7 | struct git_indexer_progress; 8 | struct git_credential; 9 | 10 | namespace git 11 | { 12 | struct Remote 13 | { 14 | const char * url() const; 15 | const char * pushurl() const; 16 | 17 | struct FetchCallbacks 18 | { 19 | protected: 20 | ~FetchCallbacks() = default; 21 | 22 | public: 23 | virtual void update_tips(char const * refname, git_oid const & a, git_oid const & b) {} 24 | virtual void sideband_progress(char const * str, int len) {} 25 | virtual void transfer_progress(git_indexer_progress const &) {} 26 | 27 | virtual git_credential* acquire_cred(const char * url, const char * username_from_url, unsigned int allowed_types) = 0; 28 | }; 29 | 30 | void fetch(FetchCallbacks &, char const * reflog_message = nullptr); 31 | 32 | private: 33 | friend struct Repository; 34 | 35 | struct Destroy { void operator() (git_remote*) const; }; 36 | 37 | explicit Remote(git_remote * remote) 38 | : remote_(remote) 39 | {} 40 | 41 | std::unique_ptr remote_; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /include/git2cpp/index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct git_index; 10 | struct git_repository; 11 | struct git_strarray; 12 | struct git_index_entry; 13 | 14 | namespace git 15 | { 16 | struct Index 17 | { 18 | explicit Index(git_repository * repo); 19 | explicit Index(const char * dir); 20 | 21 | size_t entrycount() const; 22 | git_index_entry const * operator[](size_t i) const; 23 | 24 | git_index_entry const * get_by_path(const char *path, int stage) const; 25 | 26 | typedef std::function matched_path_callback_t; 27 | 28 | void update_all(git_strarray const & pathspec, matched_path_callback_t cb); 29 | void add_all(git_strarray const & pathspec, matched_path_callback_t cb, unsigned int flags = 0); 30 | void clear(); 31 | 32 | void add_path(const char *); 33 | void add_path(std::string const &); 34 | 35 | void remove_path(const char *); 36 | void remove_path(std::string const &); 37 | 38 | git_oid write_tree() const; 39 | 40 | void write() const; 41 | 42 | private: 43 | struct Destroy { void operator() (git_index*) const; }; 44 | std::unique_ptr index_; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #trace 4 | # set -x 5 | #verbose outputs each input line as is read 6 | # set -v 7 | 8 | if [ $# -eq 0 ] ; then 9 | echo "usage: test.sh read_only_repo [writable_repo]" 1>&2 10 | return 1 11 | fi 12 | 13 | REPO=$1 14 | 15 | if [ ! -d $REPO ]; then 16 | echo "repo $REPO not a directory" 1>&2 17 | exit 1 18 | fi 19 | 20 | CWD=${PWD} 21 | 22 | function test() 23 | { 24 | local test_name="$1" 25 | echo -e "**** test $test_name *********************************\n\n" 26 | local bin="$CWD/examples/$test_name" 27 | 28 | shift 29 | 30 | $bin "$@" 31 | 32 | local exit_status 33 | 34 | exit_status=$? 35 | 36 | echo -e "\nexit status $exit_status \n\n" 37 | } 38 | 39 | # read-only tests (repo shouldn't be bare) 40 | pushd $REPO 41 | 42 | test branch-cpp 43 | test diff-cpp 44 | test commit-graph-generator-cpp . "$CWD/commit-graph.dot" 45 | test log-cpp 46 | test rev-list-cpp --topo-order HEAD 47 | test rev-parse-cpp HEAD 48 | test showindex-cpp 49 | test status-cpp 50 | 51 | popd 52 | 53 | # write test (use libgit2/tests/resources/testrepo.git) 54 | 55 | RW_REPO=$2 56 | 57 | if [ -z $RW_REPO ]; then 58 | echo "no writable repo specified" 59 | exit 0 60 | fi 61 | 62 | pushd $RW_REPO 63 | 64 | test cat-file-cpp -t a8233120f6ad708f843d861ce2b7228ec4e3dec6 65 | test cat-file-cpp -p a8233120f6ad708f843d861ce2b7228ec4e3dec6 66 | test general-cpp . 67 | 68 | popd 69 | -------------------------------------------------------------------------------- /include/git2cpp/commit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "repo_fwd.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | struct git_commit; 10 | struct git_oid; 11 | struct git_repository; 12 | 13 | namespace git 14 | { 15 | struct Tree; 16 | 17 | struct Commit 18 | { 19 | size_t parents_num() const; 20 | Commit parent(size_t i) const; 21 | git_oid const & parent_id(size_t i) const; 22 | 23 | Tree tree() const; 24 | 25 | explicit operator bool() const { return commit_ != nullptr; } 26 | 27 | git_oid const & id() const; 28 | git_oid const & tree_id() const; 29 | 30 | git_signature const * author() const; 31 | git_signature const * commiter() const; 32 | 33 | const char * message() const; 34 | const char * summary() const; 35 | git_time_t time() const; 36 | 37 | git_oid merge_base(size_t p1, size_t p2) const; 38 | 39 | Repository const & repo() const { return *repo_; } 40 | 41 | Commit(git_commit *, Repository const &); 42 | Commit() = default; 43 | 44 | private: 45 | friend struct Repository; 46 | 47 | git_commit const * ptr() const { return commit_.get(); } 48 | 49 | private: 50 | struct Destroy { void operator() (git_commit*) const; }; 51 | std::unique_ptr commit_; 52 | Repository const * repo_ = nullptr; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /include/git2cpp/status.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | struct Status 10 | { 11 | struct Options 12 | { 13 | enum class Show 14 | { 15 | IndexOnly, 16 | WorkdirOnly, 17 | IndexAndWorkdir 18 | }; 19 | 20 | enum class Sort 21 | { 22 | CaseSensitively, 23 | CaseInsensitively, 24 | }; 25 | 26 | Options(Show = Show::IndexAndWorkdir, Sort = Sort::CaseSensitively); 27 | 28 | Options & include_untracked(); 29 | Options & exclude_untracked(); 30 | Options & renames_head_to_index(); 31 | Options & include_ignored(); 32 | Options & recurse_untracked_dirs(); 33 | Options & exclude_submodules(); 34 | 35 | void set_pathspec(char ** ptr, size_t size); 36 | 37 | git_status_options const * raw() const { return &opts_; } 38 | 39 | private: 40 | git_status_options opts_; 41 | }; 42 | 43 | size_t entrycount() const; 44 | git_status_entry const & operator[](size_t i) const; 45 | 46 | private: 47 | friend struct Repository; 48 | Status(git_repository * repo, Options const & opts); 49 | 50 | private: 51 | struct Destroy { void operator() (git_status_list*) const; }; 52 | std::unique_ptr status_; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /cmake/InstallRules.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "") 2 | 3 | include(CMakePackageConfigHelpers) 4 | include(GNUInstallDirs) 5 | 6 | install( 7 | DIRECTORY include/ 8 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 9 | COMPONENT git2cpp_Development 10 | ) 11 | 12 | install( 13 | TARGETS ${package} 14 | EXPORT git2cppTargets 15 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 16 | ) 17 | 18 | if (NOT DEFINED VERSION) 19 | set(VERSION "1.0.0") 20 | endif () 21 | 22 | write_basic_package_version_file( 23 | "${package}ConfigVersion.cmake" 24 | VERSION ${VERSION} 25 | COMPATIBILITY SameMajorVersion 26 | ARCH_INDEPENDENT 27 | ) 28 | 29 | set( 30 | git2cpp_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" 31 | CACHE PATH "CMake package config location relative to the install prefix" 32 | ) 33 | 34 | mark_as_advanced(git2cpp_INSTALL_CMAKEDIR) 35 | 36 | install( 37 | FILES cmake/InstallConfig.cmake 38 | DESTINATION "${git2cpp_INSTALL_CMAKEDIR}" 39 | RENAME "${package}Config.cmake" 40 | COMPONENT git2cpp_Development 41 | ) 42 | 43 | install( 44 | FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" 45 | DESTINATION "${git2cpp_INSTALL_CMAKEDIR}" 46 | COMPONENT git2cpp_Development 47 | ) 48 | 49 | install( 50 | EXPORT git2cppTargets 51 | NAMESPACE git2cpp:: 52 | DESTINATION "${git2cpp_INSTALL_CMAKEDIR}" 53 | COMPONENT git2cpp_Development 54 | ) 55 | 56 | if (PROJECT_IS_TOP_LEVEL) 57 | include(CPack) 58 | endif () 59 | -------------------------------------------------------------------------------- /include/git2cpp/submodule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct git_submodule; 7 | struct git_repository; 8 | 9 | namespace git 10 | { 11 | struct Submodule 12 | { 13 | enum class status : unsigned int 14 | { 15 | in_head = GIT_SUBMODULE_STATUS_IN_HEAD, 16 | in_index = GIT_SUBMODULE_STATUS_IN_INDEX, 17 | in_config = GIT_SUBMODULE_STATUS_IN_CONFIG, 18 | in_wd = GIT_SUBMODULE_STATUS_IN_WD, 19 | index_added = GIT_SUBMODULE_STATUS_INDEX_ADDED, 20 | index_deleted = GIT_SUBMODULE_STATUS_INDEX_DELETED, 21 | index_modified = GIT_SUBMODULE_STATUS_INDEX_MODIFIED, 22 | wd_uninitialized = GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, 23 | wd_added = GIT_SUBMODULE_STATUS_WD_ADDED, 24 | wd_deleted = GIT_SUBMODULE_STATUS_WD_DELETED, 25 | wd_modified = GIT_SUBMODULE_STATUS_WD_MODIFIED, 26 | wd_index_modified = GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, 27 | wd_wd_modified = GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, 28 | wd_untracked = GIT_SUBMODULE_STATUS_WD_UNTRACKED, 29 | }; 30 | 31 | friend bool contains(status set, status element); 32 | 33 | status get_status() const; 34 | 35 | private: 36 | friend struct Repository; 37 | Submodule(git_submodule *, git_repository *); 38 | 39 | private: 40 | struct Destroy { void operator() (git_submodule*) const; }; 41 | std::unique_ptr sm_; 42 | git_repository * repo_; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /examples/showindex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "git2cpp/initializer.h" 7 | #include "git2cpp/repo.h" 8 | 9 | using namespace git; 10 | 11 | int main(int argc, char ** argv) 12 | { 13 | const char * dir = "."; 14 | 15 | Initializer threads_initalizer; 16 | 17 | if (argc > 1) 18 | dir = argv[1]; 19 | if (!dir || argc > 2) 20 | { 21 | fprintf(stderr, "usage: showindex []\n"); 22 | return 1; 23 | } 24 | 25 | size_t dirlen = strlen(dir); 26 | Index index = (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) 27 | ? Index(dir) 28 | : Repository(dir).index(); 29 | 30 | size_t ecount = index.entrycount(); 31 | if (!ecount) 32 | printf("Empty index\n"); 33 | 34 | for (size_t i = 0; i < ecount; ++i) 35 | { 36 | auto e = index[i]; 37 | 38 | char out[41]; 39 | out[40] = '\0'; 40 | git_oid_fmt(out, &e->id); 41 | 42 | printf("File Path: %s\n", e->path); 43 | printf(" Stage: %d\n", git_index_entry_stage(e)); 44 | printf(" Blob SHA: %s\n", out); 45 | printf("File Mode: %07o\n", e->mode); 46 | printf("File Size: %d bytes\n", (int)e->file_size); 47 | printf("Dev/Inode: %d/%d\n", (int)e->dev, (int)e->ino); 48 | printf(" UID/GID: %d/%d\n", (int)e->uid, (int)e->gid); 49 | printf(" ctime: %d\n", (int)e->ctime.seconds); 50 | printf(" mtime: %d\n", (int)e->mtime.seconds); 51 | printf("\n"); 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /src/object.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/object.h" 2 | #include "git2cpp/blob.h" 3 | #include "git2cpp/commit.h" 4 | #include "git2cpp/tree.h" 5 | #include "git2cpp/tag.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace git 12 | { 13 | Object::Object(git_object * obj, Repository const & repo) 14 | : obj_(obj) 15 | , repo_(&repo) 16 | { 17 | } 18 | 19 | void Object::Destroy::operator()(git_object* obj) const 20 | { 21 | git_object_free(obj); 22 | } 23 | 24 | git_object_t Object::type() const 25 | { 26 | return git_object_type(obj_.get()); 27 | } 28 | 29 | git_oid const & Object::id() const 30 | { 31 | return *git_object_id(obj_.get()); 32 | } 33 | 34 | #define DEFINE_METHOD_AS(type_name, enum_element) \ 35 | git_##type_name * Object::as_##type_name() \ 36 | { \ 37 | assert(type() == GIT_OBJECT_##enum_element); \ 38 | return reinterpret_cast(obj_.get()); \ 39 | } 40 | 41 | DEFINE_METHOD_AS(blob, BLOB) 42 | DEFINE_METHOD_AS(commit, COMMIT) 43 | DEFINE_METHOD_AS(tree, TREE) 44 | DEFINE_METHOD_AS(tag, TAG) 45 | 46 | #undef DEFINE_METHOD_AS 47 | 48 | Tree Object::to_tree() /*&&*/ 49 | { 50 | Tree res(as_tree(), *repo_); 51 | obj_ = nullptr; 52 | return res; 53 | } 54 | 55 | Commit Object::to_commit() /*&&*/ 56 | { 57 | Commit res(as_commit(), *repo_); 58 | obj_ = nullptr; 59 | return res; 60 | } 61 | 62 | Blob Object::to_blob() /*&&*/ 63 | { 64 | Blob res(as_blob()); 65 | obj_ = nullptr; 66 | return res; 67 | } 68 | 69 | Tag Object::to_tag() /*&&*/ 70 | { 71 | Tag res(as_tag()); 72 | obj_ = nullptr; 73 | return res; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | 3 | project(libgit2cpp LANGUAGES C CXX) 4 | set(package git2cpp) 5 | 6 | option(USE_BOOST "Enable use of Boost header libraries" OFF) 7 | option(BUNDLE_LIBGIT2 "Use bundled libgit2" ${MSVC}) 8 | option(BUILD_LIBGIT2CPP_EXAMPLES "Build libgit2cpp examples" ON) 9 | 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 11 | include(Version) 12 | 13 | if (USE_BOOST) 14 | find_package(Boost REQUIRED) 15 | add_definitions(-DUSE_BOOST=1) 16 | include_directories(${BOOST_INCLUDEDIR}) 17 | endif () 18 | 19 | if (BUNDLE_LIBGIT2) 20 | add_subdirectory(libs/libgit2) 21 | endif () 22 | 23 | file(GLOB_RECURSE LIBGIT2CPP_SOURCES 24 | src/*.cpp 25 | include/git2cpp/*.h 26 | ) 27 | 28 | add_library(${package} STATIC ${LIBGIT2CPP_SOURCES}) 29 | 30 | set_target_properties(${package} PROPERTIES 31 | CXX_STANDARD 17 32 | CXX_STANDARD_REQUIRED ON 33 | INTERFACE_COMPILE_FEATURES cxx_std_17 34 | ) 35 | 36 | if (MSVC AND ($MSVC_VERSION VERSION_GREATER 1900)) 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") 38 | endif () 39 | 40 | target_include_directories(${package} 41 | PRIVATE src 42 | PUBLIC $ 43 | ) 44 | 45 | if (USE_BOOST) 46 | target_include_directories(${package} PUBLIC ${Boost_INCLUDE_DIRS}) 47 | endif () 48 | 49 | if (BUILD_LIBGIT2CPP_EXAMPLES) 50 | add_subdirectory(examples) 51 | file(COPY test.sh DESTINATION . FILE_PERMISSIONS ${EXE_PERM}) 52 | endif () 53 | 54 | if (BUNDLE_LIBGIT2) 55 | target_include_directories(${package} PUBLIC libs/libgit2/include) 56 | target_link_libraries(${package} libgit2package) 57 | else () 58 | find_package(PkgConfig REQUIRED) 59 | pkg_search_module(LibGit2 REQUIRED IMPORTED_TARGET libgit2) 60 | target_link_libraries(${package} PkgConfig::LibGit2) 61 | endif () 62 | 63 | include(InstallRules) 64 | -------------------------------------------------------------------------------- /src/revwalker.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/revwalker.h" 2 | #include "git2cpp/error.h" 3 | #include "git2cpp/repo.h" 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | namespace revwalker 10 | { 11 | namespace sorting 12 | { 13 | const type none(GIT_SORT_NONE); 14 | const type topological(GIT_SORT_TOPOLOGICAL); 15 | const type time(GIT_SORT_TIME); 16 | const type reverse(GIT_SORT_REVERSE); 17 | } 18 | } 19 | 20 | void RevWalker::Destroy::operator()(git_revwalk* walker) const 21 | { 22 | git_revwalk_free(walker); 23 | } 24 | 25 | void RevWalker::sort(revwalker::sorting::type s) 26 | { 27 | git_revwalk_sorting(walker_.get(), s.value()); 28 | } 29 | 30 | void RevWalker::simplify_first_parent() 31 | { 32 | git_revwalk_simplify_first_parent(walker_.get()); 33 | } 34 | 35 | void RevWalker::push_head() const 36 | { 37 | if (git_revwalk_push_head(walker_.get())) 38 | throw invalid_head_error(); 39 | } 40 | 41 | void RevWalker::hide(git_oid const & obj) const 42 | { 43 | if (git_revwalk_hide(walker_.get(), &obj)) 44 | throw non_commit_object_error(obj); 45 | } 46 | 47 | void RevWalker::push(git_oid const & obj) const 48 | { 49 | if (git_revwalk_push(walker_.get(), &obj)) 50 | throw non_commit_object_error(obj); 51 | } 52 | 53 | Commit RevWalker::next() const 54 | { 55 | git_oid oid; 56 | if (git_revwalk_next(&oid, walker_.get()) == 0) 57 | return repo_->commit_lookup(oid); 58 | else 59 | return Commit(); 60 | } 61 | 62 | bool RevWalker::next(char * id_buffer) const 63 | { 64 | git_oid oid; 65 | bool valid = (git_revwalk_next(&oid, walker_.get()) == 0); 66 | if (valid) 67 | git_oid_fmt(id_buffer, &oid); 68 | return valid; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/rev-parse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "git2cpp/initializer.h" 9 | #include "git2cpp/internal/optional.h" 10 | #include "git2cpp/repo.h" 11 | 12 | using namespace git; 13 | 14 | static void usage(const char * message, const char * arg) 15 | { 16 | if (message && arg) 17 | fprintf(stderr, "%s: %s\n", message, arg); 18 | else if (message) 19 | fprintf(stderr, "%s\n", message); 20 | fprintf(stderr, "usage: rev-parse [ --option ] ...\n"); 21 | exit(1); 22 | } 23 | 24 | struct parse_state 25 | { 26 | internal::optional repo; 27 | const char * repodir = "."; 28 | }; 29 | 30 | void parse_revision(parse_state & ps, const char * revstr) 31 | { 32 | if (!ps.repo) 33 | internal::emplace(ps.repo, ps.repodir); 34 | 35 | Revspec rs = ps.repo->revparse(revstr); 36 | 37 | if (auto commit = rs.single()) 38 | { 39 | std::cout << id_to_str(commit->id()) << std::endl; 40 | } 41 | else 42 | { 43 | auto const & range = *rs.range(); 44 | std::cout << id_to_str(range.to.id()) << std::endl; 45 | 46 | if ((rs.flags() & GIT_REVSPEC_MERGE_BASE) != 0) 47 | { 48 | git_oid base = ps.repo->merge_base(range); 49 | std::cout << id_to_str(base) << std::endl; 50 | } 51 | 52 | std::cout << "^" << id_to_str(range.from.id()) << std::endl; 53 | } 54 | } 55 | 56 | int main(int argc, char * argv[]) 57 | { 58 | auto_git_initializer; 59 | 60 | parse_state ps; 61 | 62 | for (int i = 1; i < argc; ++i) 63 | { 64 | const char * a = argv[i]; 65 | 66 | if (a[0] != '-') 67 | parse_revision(ps, a); 68 | else if (!strncmp(a, "--git-dir=", strlen("--git-dir="))) 69 | ps.repodir = a + strlen("--git-dir="); 70 | else 71 | usage("Cannot handle argument", a); 72 | } 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /include/git2cpp/diff.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "buffer.h" 4 | #include "tagged_mask.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace git 12 | { 13 | namespace diff 14 | { 15 | namespace stats 16 | { 17 | namespace format 18 | { 19 | typedef tagged_mask_t type; 20 | 21 | extern const type none; 22 | extern const type full; 23 | extern const type _short; 24 | extern const type number; 25 | extern const type include_summary; 26 | } 27 | } 28 | 29 | enum class format 30 | { 31 | patch, 32 | patch_header, 33 | raw, 34 | name_only, 35 | name_status 36 | }; 37 | } 38 | 39 | struct Diff 40 | { 41 | struct Stats 42 | { 43 | Buffer to_buf(diff::stats::format::type, size_t width) const; 44 | 45 | private: 46 | friend struct Diff; 47 | 48 | explicit Stats(git_diff_stats * stats) 49 | : stats_(stats) 50 | {} 51 | 52 | private: 53 | struct Destroy { void operator() (git_diff_stats*) const; }; 54 | std::unique_ptr stats_; 55 | }; 56 | 57 | size_t deltas_num() const; 58 | 59 | void find_similar(git_diff_find_options &); 60 | 61 | Stats stats() const; 62 | 63 | typedef std::function 64 | print_callback_t; 65 | 66 | void print(diff::format, print_callback_t print_callback) const; 67 | 68 | explicit Diff(git_diff * diff) 69 | : diff_(diff) 70 | {} 71 | 72 | private: 73 | struct Destroy { void operator() (git_diff *) const; }; 74 | std::unique_ptr diff_; 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /include/git2cpp/tree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pathspec.h" 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | struct Repository; 10 | 11 | struct Tree 12 | { 13 | struct BorrowedEntry 14 | { 15 | const char * name() const; 16 | git_oid const & id() const; 17 | git_object_t type() const; 18 | git_filemode_t filemode() const; 19 | 20 | private: 21 | friend struct Tree; 22 | friend struct Repository; 23 | 24 | explicit BorrowedEntry(git_tree_entry const * entry) 25 | : entry_(entry) 26 | {} 27 | 28 | git_tree_entry const * ptr() const { return entry_; } 29 | 30 | private: 31 | git_tree_entry const * entry_; 32 | }; 33 | 34 | struct OwnedEntry 35 | { 36 | Tree to_tree() /*&&*/; 37 | 38 | private: 39 | friend struct Tree; 40 | friend struct Repository; 41 | 42 | OwnedEntry(git_tree_entry * entry, Repository const & repo); 43 | 44 | git_tree_entry const * ptr() const { return entry_.get(); } 45 | 46 | private: 47 | struct Destroy { void operator() (git_tree_entry*) const; }; 48 | std::unique_ptr entry_; 49 | Repository const * repo_; 50 | }; 51 | 52 | git_tree const * ptr() const { return tree_.get(); } 53 | git_tree * ptr() { return tree_.get(); } 54 | 55 | int pathspec_match(uint32_t flags, Pathspec const & ps); 56 | 57 | size_t entrycount() const; 58 | 59 | BorrowedEntry operator[](size_t) const; 60 | 61 | BorrowedEntry operator[](std::string const & filename) const; 62 | 63 | OwnedEntry find(const char * path) const; 64 | 65 | Tree(git_tree *, Repository const &); 66 | 67 | Tree() = default; 68 | 69 | explicit operator bool() const { return tree_ != nullptr; } 70 | 71 | private: 72 | struct Destroy { void operator() (git_tree*) const; }; 73 | std::unique_ptr tree_; 74 | Repository const * repo_ = nullptr; 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /src/commit.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/commit.h" 2 | #include "git2cpp/error.h" 3 | #include "git2cpp/repo.h" 4 | #include "git2cpp/tree.h" 5 | 6 | #include 7 | 8 | namespace git 9 | { 10 | Commit::Commit(git_commit * commit, Repository const & repo) 11 | : commit_(commit) 12 | , repo_(&repo) 13 | { 14 | } 15 | 16 | void Commit::Destroy::operator()(git_commit* commit) const { 17 | git_commit_free(commit); 18 | } 19 | 20 | Commit Commit::parent(size_t i) const 21 | { 22 | git_commit * parent; 23 | if (git_commit_parent(&parent, ptr(), static_cast(i))) 24 | throw commit_parent_error(id()); 25 | return Commit(parent, *repo_); 26 | } 27 | 28 | Tree Commit::tree() const 29 | { 30 | git_tree * tree; 31 | if (git_commit_tree(&tree, ptr())) 32 | throw commit_tree_error(id()); 33 | return Tree(tree, *repo_); 34 | } 35 | 36 | size_t Commit::parents_num() const 37 | { 38 | return git_commit_parentcount(ptr()); 39 | } 40 | 41 | git_oid const & Commit::id() const 42 | { 43 | return *git_commit_id(ptr()); 44 | } 45 | 46 | git_oid const & Commit::tree_id() const 47 | { 48 | return *git_commit_tree_id(ptr()); 49 | } 50 | 51 | git_oid const & Commit::parent_id(size_t i) const 52 | { 53 | return *git_commit_parent_id(ptr(), static_cast(i)); 54 | } 55 | 56 | git_signature const * Commit::author() const 57 | { 58 | return git_commit_author(ptr()); 59 | } 60 | 61 | git_signature const * Commit::commiter() const 62 | { 63 | return git_commit_committer(ptr()); 64 | } 65 | 66 | const char * Commit::message() const 67 | { 68 | return git_commit_message(ptr()); 69 | } 70 | 71 | const char * Commit::summary() const 72 | { 73 | return git_commit_summary(commit_.get()); 74 | } 75 | 76 | git_time_t Commit::time() const 77 | { 78 | return git_commit_time(ptr()); 79 | } 80 | 81 | git_oid Commit::merge_base(size_t p1, size_t p2) const 82 | { 83 | return repo_->merge_base(parent_id(p1), parent_id(p2)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/branch.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/initializer.h" 2 | #include "git2cpp/repo.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace 8 | { 9 | using namespace git; 10 | 11 | int create_branch(Repository & repo, const char * name, bool force) 12 | { 13 | try 14 | { 15 | auto head = repo.head(); 16 | auto branch = repo.create_branch(name, repo.commit_lookup(head.target()), force); 17 | assert(branch); 18 | assert(std::strcmp(branch.name(), name)); 19 | std::cout << "branch " << name << " has been created" << std::endl; 20 | return EXIT_SUCCESS; 21 | } 22 | catch (branch_create_error const & e) 23 | { 24 | switch (e.reason) 25 | { 26 | case branch_create_error::already_exists:; 27 | std::cerr << "a branch named '" << name << "' already exists" << std::endl; 28 | break; 29 | case branch_create_error::invalid_spec: 30 | std::cerr << "'" << name << "' is not a valid branch name" << std::endl; 31 | break; 32 | case branch_create_error::unknown: 33 | break; 34 | } 35 | return EXIT_FAILURE; 36 | } 37 | } 38 | 39 | void print_help() 40 | { 41 | std::cerr << "invalid command line arguments, expected are:" << std::endl 42 | << "[[-f] branch_name]" << std::endl; 43 | } 44 | } 45 | 46 | int main(int argc, char* argv[]) 47 | { 48 | Initializer threads_initializer; 49 | 50 | Repository repo("."); 51 | switch (argc) 52 | { 53 | case 1: 54 | { 55 | auto branches = repo.branches(git::branch_type::ALL); 56 | for (auto const & b : branches) 57 | std::cout << b.name() << std::endl; 58 | return EXIT_SUCCESS; 59 | } 60 | case 2: 61 | return create_branch(repo, argv[1], false); 62 | case 3: 63 | if (std::strcmp(argv[1], "-f") != 0) 64 | { 65 | print_help(); 66 | return EXIT_FAILURE; 67 | } 68 | return create_branch(repo, argv[2], true); 69 | default: 70 | print_help(); 71 | return EXIT_FAILURE; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/remote.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/remote.h" 2 | 3 | #include 4 | 5 | namespace git 6 | { 7 | const char * Remote::url() const 8 | { 9 | return git_remote_url(remote_.get()); 10 | } 11 | 12 | const char * Remote::pushurl() const 13 | { 14 | return git_remote_pushurl(remote_.get()); 15 | } 16 | 17 | git_fetch_options fetch_options_from_callbacks(Remote::FetchCallbacks & callbacks) 18 | { 19 | using FetchCallbacks = Remote::FetchCallbacks; 20 | 21 | git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; 22 | opts.callbacks.payload = &callbacks; 23 | 24 | opts.callbacks.update_tips = [] (char const * refname, git_oid const * a, git_oid const * b, void * data) 25 | { 26 | auto callbacks = static_cast(data); 27 | callbacks->update_tips(refname, *a, *b); 28 | return 0; 29 | }; 30 | opts.callbacks.sideband_progress = [] (char const * str, int len, void * data) 31 | { 32 | auto callbacks = static_cast(data); 33 | callbacks->sideband_progress(str, len); 34 | return 0; 35 | }; 36 | opts.callbacks.transfer_progress = [] (git_indexer_progress const * stats, void * data) 37 | { 38 | auto callbacks = static_cast(data); 39 | callbacks->transfer_progress(*stats); 40 | return 0; 41 | }; 42 | opts.callbacks.credentials = [] (git_credential ** out, char const *url, char const * user_from_url, unsigned int allowed_types, void * data) 43 | { 44 | auto callbacks = static_cast(data); 45 | auto cred = callbacks->acquire_cred(url, user_from_url, allowed_types); 46 | if (!cred) 47 | return -1; 48 | *out = cred; 49 | return 0; 50 | }; 51 | return opts; 52 | } 53 | 54 | void Remote::fetch(FetchCallbacks & callbacks, char const * reflog_message) 55 | { 56 | const auto opts = fetch_options_from_callbacks(callbacks); 57 | git_remote_fetch(remote_.get(), nullptr, &opts, reflog_message); 58 | } 59 | 60 | void Remote::Destroy::operator()(git_remote * remote) const 61 | { 62 | git_remote_free(remote); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/commit-graph-generator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace 7 | { 8 | struct visitor_t 9 | { 10 | void operator()(git::RevWalker & walker) 11 | { 12 | walker.sort(git::revwalker::sorting::topological); 13 | walker.simplify_first_parent(); 14 | 15 | while (git::Commit commit = walker.next()) 16 | { 17 | output_commit(commit); 18 | 19 | for (size_t i = 0; i != commit.parents_num(); ++i) 20 | { 21 | output_hash(commit.id()) << " -> "; 22 | output_hash(commit.parent_id(i)) << "\n"; 23 | } 24 | 25 | for (size_t i = 1; i < commit.parents_num(); ++i) 26 | { 27 | auto branch_walker = repo_.rev_walker(); 28 | branch_walker.push(commit.parent_id(i)); 29 | branch_walker.hide(commit.merge_base(0, i)); 30 | (*this)(branch_walker); 31 | } 32 | } 33 | } 34 | 35 | visitor_t(git::Repository const & repo, std::ostream & out) 36 | : repo_(repo) 37 | , out_(out) 38 | { 39 | } 40 | 41 | private: 42 | std::ostream & output_hash(git_oid const & id) const 43 | { 44 | out_ << "C" << git::id_to_str(id, 6); 45 | return out_; 46 | } 47 | 48 | void output_commit(git::Commit const & commit) const 49 | { 50 | output_hash(commit.id()) << " [label=\"" << commit.summary() << "\"];" 51 | << "\n"; 52 | } 53 | 54 | private: 55 | git::Repository const & repo_; 56 | std::ostream & out_; 57 | }; 58 | 59 | void visit(git::Repository const & repo, std::ostream & out) 60 | { 61 | visitor_t visitor(repo, out); 62 | 63 | git::RevWalker walker = repo.rev_walker(); 64 | walker.push_head(); 65 | visitor(walker); 66 | } 67 | } 68 | 69 | int main(int argc, char * argv[]) 70 | { 71 | auto_git_initializer; 72 | git::Repository repo(argc >= 2 ? argv[1] : "."); 73 | std::ofstream out(argc >= 3 ? argv[2] : "commit-graph.dot"); 74 | 75 | out << "digraph {\n"; 76 | visit(repo, out); 77 | out << "}\n"; 78 | } 79 | -------------------------------------------------------------------------------- /src/tree.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/tree.h" 2 | #include "git2cpp/error.h" 3 | #include "git2cpp/repo.h" 4 | 5 | #include 6 | 7 | namespace git 8 | { 9 | Tree::Tree(git_tree * tree, Repository const & repo) 10 | : tree_(tree) 11 | , repo_(&repo) 12 | { 13 | } 14 | 15 | void Tree::Destroy::operator()(git_tree* tree) const 16 | { 17 | git_tree_free(tree); 18 | } 19 | 20 | int Tree::pathspec_match(uint32_t flags, Pathspec const & ps) 21 | { 22 | return git_pathspec_match_tree(nullptr, ptr(), flags, ps.ptr()); 23 | } 24 | 25 | size_t Tree::entrycount() const 26 | { 27 | return git_tree_entrycount(ptr()); 28 | } 29 | 30 | Tree::BorrowedEntry Tree::operator[](size_t i) const 31 | { 32 | return BorrowedEntry(git_tree_entry_byindex(ptr(), i)); 33 | } 34 | 35 | Tree::BorrowedEntry Tree::operator[](std::string const & filename) const 36 | { 37 | if (auto entry = git_tree_entry_byname(ptr(), filename.c_str())) 38 | return BorrowedEntry(entry); 39 | else 40 | throw file_not_found_error(filename.c_str()); 41 | } 42 | 43 | Tree::OwnedEntry Tree::find(const char * path) const 44 | { 45 | git_tree_entry * res; 46 | const auto status = git_tree_entry_bypath(&res, ptr(), path); 47 | switch (status) 48 | { 49 | case GIT_OK: 50 | return OwnedEntry(res, *repo_); 51 | case GIT_ENOTFOUND: 52 | throw file_not_found_error(path); 53 | default: 54 | throw error_t(internal::format("unknown error inside function: 'git_tree_entry_bypath': %d", status)); 55 | } 56 | } 57 | 58 | Tree::OwnedEntry::OwnedEntry(git_tree_entry * entry, Repository const & repo) 59 | : entry_(entry) 60 | , repo_(&repo) 61 | { 62 | } 63 | 64 | void Tree::OwnedEntry::Destroy::operator()(git_tree_entry* entry) const 65 | { 66 | git_tree_entry_free(entry); 67 | } 68 | 69 | Tree Tree::OwnedEntry::to_tree() /* && */ 70 | { 71 | auto const & repo = *repo_; 72 | return repo.entry_to_object(std::move(*this)).to_tree(); 73 | } 74 | 75 | const char * Tree::BorrowedEntry::name() const 76 | { 77 | return git_tree_entry_name(entry_); 78 | } 79 | 80 | git_oid const & Tree::BorrowedEntry::id() const 81 | { 82 | return *git_tree_entry_id(entry_); 83 | } 84 | 85 | git_object_t Tree::BorrowedEntry::type() const 86 | { 87 | return git_tree_entry_type(entry_); 88 | } 89 | 90 | git_filemode_t Tree::BorrowedEntry::filemode() const 91 | { 92 | return git_tree_entry_filemode(entry_); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/status.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/status.h" 2 | #include "git2cpp/error.h" 3 | 4 | namespace git 5 | { 6 | size_t Status::entrycount() const 7 | { 8 | return git_status_list_entrycount(status_.get()); 9 | } 10 | 11 | git_status_entry const & Status::operator[](size_t i) const 12 | { 13 | if (auto res = git_status_byindex(status_.get(), i)) 14 | return *res; 15 | else 16 | throw error_t("status entry index out of bounds: " + std::to_string(i)); 17 | } 18 | 19 | namespace 20 | { 21 | git_status_show_t convert(Status::Options::Show show) 22 | { 23 | switch (show) 24 | { 25 | case Status::Options::Show::IndexOnly: 26 | return GIT_STATUS_SHOW_INDEX_ONLY; 27 | case Status::Options::Show::WorkdirOnly: 28 | return GIT_STATUS_SHOW_WORKDIR_ONLY; 29 | default: 30 | return GIT_STATUS_SHOW_INDEX_AND_WORKDIR; 31 | } 32 | } 33 | } 34 | 35 | Status::Options::Options(Show show, Sort sort) 36 | : opts_(GIT_STATUS_OPTIONS_INIT) 37 | { 38 | opts_.show = convert(show); 39 | 40 | switch (sort) 41 | { 42 | case Sort::CaseSensitively: 43 | opts_.flags |= GIT_STATUS_OPT_SORT_CASE_SENSITIVELY; 44 | break; 45 | case Sort::CaseInsensitively: 46 | opts_.flags |= GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY; 47 | break; 48 | } 49 | } 50 | 51 | void Status::Options::set_pathspec(char ** ptr, size_t size) 52 | { 53 | opts_.pathspec.strings = ptr; 54 | opts_.pathspec.count = size; 55 | } 56 | 57 | Status::Options & Status::Options::include_untracked() 58 | { 59 | opts_.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; 60 | return *this; 61 | } 62 | 63 | Status::Options & Status::Options::exclude_untracked() 64 | { 65 | opts_.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED; 66 | return *this; 67 | } 68 | 69 | Status::Options & Status::Options::recurse_untracked_dirs() 70 | { 71 | opts_.flags |= GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; 72 | return *this; 73 | } 74 | 75 | Status::Options & Status::Options::exclude_submodules() 76 | { 77 | opts_.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES; 78 | return *this; 79 | } 80 | 81 | Status::Options & Status::Options::include_ignored() 82 | { 83 | opts_.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED; 84 | return *this; 85 | } 86 | 87 | Status::Options & Status::Options::renames_head_to_index() 88 | { 89 | opts_.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX; 90 | return *this; 91 | } 92 | 93 | Status::Status(git_repository * repo, Options const & opts) 94 | { 95 | git_status_list * status; 96 | if (git_status_list_new(&status, repo, opts.raw())) 97 | throw get_status_error(); 98 | status_.reset(status); 99 | } 100 | 101 | void Status::Destroy::operator()(git_status_list* status) const 102 | { 103 | git_status_list_free(status); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/rev-list.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/initializer.h" 2 | #include "git2cpp/repo.h" 3 | #include "git2cpp/revwalker.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using namespace git; 13 | 14 | void push_commit(RevWalker const & walk, git_oid const & oid, int hide) 15 | { 16 | if (hide) 17 | return walk.hide(oid); 18 | else 19 | return walk.push(oid); 20 | } 21 | 22 | void push_spec(Repository const & repo, RevWalker const & walk, const char * spec, int hide) 23 | { 24 | push_commit(walk, revparse_single(repo, spec).id(), hide); 25 | } 26 | 27 | void push_range(Repository const & repo, RevWalker const & walk, const char * range, int hide) 28 | { 29 | auto revspec = repo.revparse(range); 30 | 31 | if (revspec.flags() & GIT_REVSPEC_MERGE_BASE) 32 | { 33 | /* TODO: support "..." */ 34 | throw std::runtime_error("unsupported operation"); 35 | } 36 | 37 | auto const & r = *revspec.range(); 38 | 39 | push_commit(walk, r.to.id(), !hide); 40 | push_commit(walk, r.from.id(), hide); 41 | } 42 | 43 | void revwalk_parseopts(Repository const & repo, RevWalker & walk, int nopts, char ** opts) 44 | { 45 | namespace sort = revwalker::sorting; 46 | auto sorting = sort::none; 47 | 48 | int hide = 0; 49 | for (int i = 0; i < nopts; i++) 50 | { 51 | if (!strcmp(opts[i], "--topo-order")) 52 | { 53 | sorting = sort::topological | (sorting & sort::reverse); 54 | walk.sort(sorting); 55 | } 56 | else if (!strcmp(opts[i], "--date-order")) 57 | { 58 | sorting = sort::time | (sorting & sort::reverse); 59 | walk.sort(sorting); 60 | } 61 | else if (!strcmp(opts[i], "--reverse")) 62 | { 63 | if ((sorting & sort::reverse) != sort::none) 64 | sorting = sorting & ~sort::reverse; 65 | walk.sort(sorting); 66 | } 67 | else if (!strcmp(opts[i], "--not")) 68 | { 69 | hide = !hide; 70 | } 71 | else if (opts[i][0] == '^') 72 | { 73 | push_spec(repo, walk, opts[i] + 1, !hide); 74 | } 75 | else if (strstr(opts[i], "..")) 76 | { 77 | push_range(repo, walk, opts[i], hide); 78 | } 79 | else 80 | { 81 | push_spec(repo, walk, opts[i], hide); 82 | } 83 | } 84 | } 85 | 86 | int main(int argc, char ** argv) 87 | { 88 | auto_git_initializer; 89 | 90 | try 91 | { 92 | Repository repo("."); 93 | auto walker = repo.rev_walker(); 94 | 95 | revwalk_parseopts(repo, walker, argc - 1, argv + 1); 96 | 97 | char buf[41]; 98 | 99 | while (walker.next(buf)) 100 | { 101 | buf[40] = '\0'; 102 | printf("%s\n", buf); 103 | } 104 | 105 | return 0; 106 | } 107 | catch (std::exception const & e) 108 | { 109 | std::cerr << e.what() << std::endl; 110 | if (auto err = git_error_last()) 111 | { 112 | if (err->message) 113 | std::cerr << "libgit2 last error: " << err->message << std::endl; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /examples/add.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "git2cpp/initializer.h" 7 | #include "git2cpp/repo.h" 8 | 9 | #include 10 | 11 | enum print_options 12 | { 13 | SKIP = 1, 14 | VERBOSE = 2, 15 | UPDATE = 4, 16 | }; 17 | 18 | void init_array(git_strarray & arr, int argc, char ** argv) 19 | { 20 | arr.count = argc; 21 | arr.strings = reinterpret_cast(malloc(sizeof(char *) * argc)); 22 | std::copy_n(argv, argc, arr.strings); 23 | } 24 | 25 | int print_matched_cb(const char * path, const char * /*matched_pathspec*/, 26 | git::Repository const & repo, print_options options) 27 | { 28 | git_status_t status = repo.file_status(path); 29 | 30 | int ret; 31 | if (status & GIT_STATUS_WT_MODIFIED || 32 | status & GIT_STATUS_WT_NEW) 33 | { 34 | printf("add '%s'\n", path); 35 | ret = 0; 36 | } 37 | else 38 | { 39 | ret = 1; 40 | } 41 | 42 | if (options & SKIP) 43 | { 44 | ret = 1; 45 | } 46 | 47 | return ret; 48 | } 49 | 50 | void print_usage(void) 51 | { 52 | fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n"); 53 | fprintf(stderr, "\t-n, --dry-run dry run\n"); 54 | fprintf(stderr, "\t-v, --verbose be verbose\n"); 55 | fprintf(stderr, "\t-u, --update update tracked files\n"); 56 | } 57 | 58 | int main(int argc, char ** argv) 59 | { 60 | using namespace std::placeholders; 61 | 62 | int i = 1, options = 0; 63 | for (; i < argc; ++i) 64 | { 65 | if (argv[i][0] != '-') 66 | { 67 | break; 68 | } 69 | else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) 70 | { 71 | options |= VERBOSE; 72 | } 73 | else if (!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) 74 | { 75 | options |= SKIP; 76 | } 77 | else if (!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) 78 | { 79 | options |= UPDATE; 80 | } 81 | else if (!strcmp(argv[i], "-h")) 82 | { 83 | print_usage(); 84 | break; 85 | } 86 | else if (!strcmp(argv[i], "--")) 87 | { 88 | i++; 89 | break; 90 | } 91 | else 92 | { 93 | fprintf(stderr, "Unsupported option %s.\n", argv[i]); 94 | print_usage(); 95 | return 1; 96 | } 97 | } 98 | 99 | if (argc <= i) 100 | { 101 | print_usage(); 102 | return EXIT_FAILURE; 103 | } 104 | 105 | git_strarray arr; 106 | init_array(arr, argc - i, argv + i); 107 | 108 | auto_git_initializer; 109 | 110 | git::Repository repo("."); 111 | 112 | git::Index::matched_path_callback_t cb; 113 | if (options & VERBOSE || options & SKIP) 114 | cb = std::bind(&print_matched_cb, _1, _2, std::cref(repo), static_cast(options)); 115 | 116 | git::Index index = repo.index(); 117 | if (options & UPDATE) 118 | index.update_all(arr, cb); 119 | else 120 | index.add_all(arr, cb); 121 | 122 | index.write(); 123 | free(arr.strings); 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /src/index.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "git2cpp/error.h" 7 | #include "git2cpp/index.h" 8 | 9 | namespace git 10 | { 11 | Index::Index(git_repository * repo) 12 | { 13 | git_index * index; 14 | if (git_repository_index(&index, repo) < 0) 15 | throw index_open_error(); 16 | index_.reset(index); 17 | } 18 | 19 | Index::Index(const char * dir) 20 | { 21 | git_index * index; 22 | if (git_index_open(&index, dir)) 23 | throw index_open_error(); 24 | index_.reset(index); 25 | git_index_read(index, true); 26 | } 27 | 28 | void Index::Destroy::operator()(git_index* index) const 29 | { 30 | git_index_free(index); 31 | } 32 | 33 | size_t Index::entrycount() const 34 | { 35 | return git_index_entrycount(index_.get()); 36 | } 37 | 38 | git_index_entry const * Index::operator[](size_t i) const 39 | { 40 | return git_index_get_byindex(index_.get(), i); 41 | } 42 | 43 | git_index_entry const* Index::get_by_path(const char* path, int stage) const 44 | { 45 | return git_index_get_bypath(index_.get(), path, stage); 46 | } 47 | 48 | namespace 49 | { 50 | int apply_callback(const char * path, const char * matched_pathspec, void * payload) 51 | { 52 | auto cb = reinterpret_cast(payload); 53 | return (*cb)(path, matched_pathspec); 54 | } 55 | } 56 | 57 | void Index::update_all(git_strarray const & pathspec, matched_path_callback_t cb) 58 | { 59 | int res = cb 60 | ? git_index_update_all(index_.get(), &pathspec, &apply_callback, &cb) 61 | : git_index_update_all(index_.get(), &pathspec, nullptr, nullptr); 62 | assert(res == 0); 63 | } 64 | 65 | void Index::add_all(git_strarray const & pathspec, matched_path_callback_t cb, unsigned int flags) 66 | { 67 | int res = cb 68 | ? git_index_add_all(index_.get(), &pathspec, flags, &apply_callback, &cb) 69 | : git_index_add_all(index_.get(), &pathspec, flags, nullptr, nullptr); 70 | assert(res == 0); 71 | } 72 | 73 | void Index::clear() 74 | { 75 | int res = git_index_clear(index_.get()); 76 | assert(res == 0); 77 | } 78 | 79 | void Index::add_path(const char * path) 80 | { 81 | int res = git_index_add_bypath(index_.get(), path); 82 | assert(res == 0); 83 | } 84 | 85 | void Index::add_path(std::string const & path) 86 | { 87 | add_path(path.c_str()); 88 | } 89 | 90 | void Index::remove_path(const char * path) 91 | { 92 | int res = git_index_remove_bypath(index_.get(), path); 93 | assert(res == 0); 94 | } 95 | 96 | void Index::remove_path(std::string const & path) 97 | { 98 | remove_path(path.c_str()); 99 | } 100 | 101 | void Index::write() const 102 | { 103 | if (git_index_write(index_.get())) 104 | throw index_write_error(); 105 | } 106 | 107 | git_oid Index::write_tree() const 108 | { 109 | git_oid res; 110 | if (git_index_write_tree(&res, index_.get()) < 0) 111 | throw index_write_tree_error(); 112 | return res; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/diff.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/diff.h" 2 | #include "git2cpp/error.h" 3 | 4 | #include 5 | 6 | #ifdef USE_BOOST 7 | #include 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | namespace git 14 | { 15 | namespace diff 16 | { 17 | namespace stats 18 | { 19 | namespace format 20 | { 21 | const type none(GIT_DIFF_STATS_NONE); 22 | const type full(GIT_DIFF_STATS_FULL); 23 | const type _short(GIT_DIFF_STATS_SHORT); 24 | const type number(GIT_DIFF_STATS_NUMBER); 25 | const type include_summary(GIT_DIFF_STATS_INCLUDE_SUMMARY); 26 | } 27 | } 28 | 29 | #ifdef USE_BOOST 30 | template 31 | using map_container = boost::container::flat_map; 32 | #else 33 | struct EnumHash 34 | { 35 | template 36 | std::size_t operator()(T t) const 37 | { 38 | return static_cast(t); 39 | } 40 | }; 41 | 42 | template 43 | using map_container = std::unordered_map; 44 | #endif 45 | 46 | git_diff_format_t convert(format f) 47 | { 48 | static const map_container converter = { 49 | {format::patch, GIT_DIFF_FORMAT_PATCH}, 50 | {format::patch_header, GIT_DIFF_FORMAT_PATCH_HEADER}, 51 | {format::raw, GIT_DIFF_FORMAT_RAW}, 52 | {format::name_only, GIT_DIFF_FORMAT_NAME_ONLY}, 53 | {format::name_status, GIT_DIFF_FORMAT_NAME_STATUS}}; 54 | return converter.at(f); 55 | } 56 | } 57 | 58 | namespace 59 | { 60 | int apply_callback(git_diff_delta const * delta, git_diff_hunk const * hunk, git_diff_line const * line, void * payload) 61 | { 62 | assert(delta); 63 | assert(line); 64 | auto cb = reinterpret_cast(payload); 65 | (*cb)(*delta, *hunk, *line); 66 | return 0; 67 | } 68 | } 69 | 70 | void Diff::Destroy::operator() (git_diff* diff) const { git_diff_free(diff); } 71 | 72 | void Diff::find_similar(git_diff_find_options & findopts) 73 | { 74 | git_diff_find_similar(diff_.get(), &findopts); 75 | } 76 | 77 | size_t Diff::deltas_num() const 78 | { 79 | return git_diff_num_deltas(diff_.get()); 80 | } 81 | 82 | void Diff::print(diff::format f, print_callback_t print_callback) const 83 | { 84 | git_diff_print(diff_.get(), convert(f), &apply_callback, &print_callback); 85 | } 86 | 87 | Diff::Stats Diff::stats() const 88 | { 89 | git_diff_stats * stats; 90 | if (git_diff_get_stats(&stats, diff_.get())) 91 | throw error_t("git_diff_get_stats fail"); 92 | else 93 | return Stats(stats); 94 | } 95 | 96 | void Diff::Stats::Destroy::operator()(git_diff_stats* stats) const 97 | { 98 | git_diff_stats_free(stats); 99 | } 100 | 101 | Buffer Diff::Stats::to_buf(diff::stats::format::type format, size_t width) const 102 | { 103 | git_buf buf = GIT_BUF_INIT; 104 | if (git_diff_stats_to_buf(&buf, stats_.get(), git_diff_stats_format_t(format.value()), width)) 105 | throw error_t("git_diff_stats_to_buf fail"); 106 | else 107 | return Buffer(buf); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/ls-files.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * libgit2 "ls-files" example - shows how to view all files currently in the index 3 | * 4 | * Written by the libgit2 contributors 5 | * 6 | * To the extent possible under law, the author(s) have dedicated all copyright 7 | * and related and neighboring rights to this software to the public domain 8 | * worldwide. This software is distributed without any warranty. 9 | * 10 | * You should have received a copy of the CC0 Public Domain Dedication along 11 | * with this software. If not, see 12 | * . 13 | */ 14 | 15 | /** 16 | * This example demonstrates the libgit2 index APIs to roughly 17 | * simulate the output of `git ls-files`. 18 | * `git ls-files` has many options and this currently does not show them. 19 | * 20 | * `git ls-files` base command shows all paths in the index at that time. 21 | * This includes staged and committed files, but unstaged files will not display. 22 | * 23 | * This currently supports the default behavior and the `--error-unmatch` option. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "git2/index.h" 31 | 32 | typedef struct { 33 | int error_unmatch; 34 | char *files[1024]; 35 | size_t file_count; 36 | } ls_options; 37 | 38 | static void usage(const char *message, const char *arg) 39 | { 40 | if (message && arg) 41 | fprintf(stderr, "%s: %s\n", message, arg); 42 | else if (message) 43 | fprintf(stderr, "%s\n", message); 44 | fprintf(stderr, "usage: ls-files [--error-unmatch] [--] [...]\n"); 45 | exit(1); 46 | } 47 | 48 | static int parse_options(ls_options *opts, int argc, char *argv[]) 49 | { 50 | int parsing_files = 0; 51 | int i; 52 | 53 | memset(opts, 0, sizeof(ls_options)); 54 | 55 | if (argc < 2) 56 | return 0; 57 | 58 | for (i = 1; i < argc; ++i) { 59 | char *a = argv[i]; 60 | 61 | /* if it doesn't start with a '-' or is after the '--' then it is a file */ 62 | if (a[0] != '-' || parsing_files) { 63 | parsing_files = 1; 64 | 65 | /* watch for overflows (just in case) */ 66 | if (opts->file_count == 1024) { 67 | fprintf(stderr, "ls-files can only support 1024 files at this time.\n"); 68 | return -1; 69 | } 70 | 71 | opts->files[opts->file_count++] = a; 72 | } else if (!strcmp(a, "--")) { 73 | parsing_files = 1; 74 | } else if (!strcmp(a, "--error-unmatch")) { 75 | opts->error_unmatch = 1; 76 | } else { 77 | usage("Unsupported argument", a); 78 | return -1; 79 | } 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | static int print_paths(ls_options *opts, git::Index & index) 86 | { 87 | /* if there are no files explicitly listed by the user print all entries in the index */ 88 | if (opts->file_count == 0) { 89 | size_t const entry_count = index.entrycount(); 90 | 91 | for (size_t i = 0; i < entry_count; i++) { 92 | auto entry = index[i]; 93 | puts(entry->path); 94 | } 95 | return 0; 96 | } 97 | 98 | /* loop through the files found in the args and print them if they exist */ 99 | for (size_t i = 0; i < opts->file_count; ++i) { 100 | const char *path = opts->files[i]; 101 | 102 | if (index.get_by_path(path, GIT_INDEX_STAGE_NORMAL)) { 103 | puts(path); 104 | } else if (opts->error_unmatch) { 105 | fprintf(stderr, "error: pathspec '%s' did not match any file(s) known to git.\n", path); 106 | fprintf(stderr, "Did you forget to 'git add'?\n"); 107 | return -1; 108 | } 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | int main(int argc, char *argv[]) 115 | { 116 | ls_options opts; 117 | 118 | auto error = parse_options(&opts, argc, argv); 119 | if (error < 0) 120 | return error; 121 | 122 | auto_git_initializer; 123 | 124 | git::Repository repo("."); 125 | auto index = repo.index(); 126 | 127 | return print_paths(&opts, index); 128 | } 129 | -------------------------------------------------------------------------------- /examples/clone.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/initializer.h" 2 | #include "git2cpp/remote.h" 3 | #include "git2cpp/repo.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | /* Define the printf format specifer to use for size_t output */ 10 | #if defined(_MSC_VER) || defined(__MINGW32__) 11 | # define PRIuZ "Iu" 12 | # define PRIxZ "Ix" 13 | # define PRIdZ "Id" 14 | #else 15 | # define PRIuZ "zu" 16 | # define PRIxZ "zx" 17 | # define PRIdZ "zd" 18 | #endif 19 | 20 | namespace 21 | { 22 | struct progress_data 23 | { 24 | git_indexer_progress fetch_progress; 25 | size_t completed_steps; 26 | size_t total_steps; 27 | const char * path; 28 | 29 | void print() 30 | { 31 | int network_percent = fetch_progress.total_objects > 0 ? (100 * fetch_progress.received_objects) / fetch_progress.total_objects : 0; 32 | int index_percent = fetch_progress.total_objects > 0 ? (100 * fetch_progress.indexed_objects) / fetch_progress.total_objects : 0; 33 | 34 | int checkout_percent = total_steps > 0 35 | ? (int)((100 * completed_steps) / total_steps) 36 | : 0; 37 | size_t kbytes = fetch_progress.received_bytes / 1024; 38 | 39 | if (fetch_progress.total_objects && 40 | fetch_progress.received_objects == fetch_progress.total_objects) 41 | { 42 | printf("Resolving deltas %u/%u\r", 43 | fetch_progress.indexed_deltas, 44 | fetch_progress.total_deltas); 45 | } 46 | else 47 | { 48 | printf("net %3d%% (%4" PRIuZ " kb, %5u/%5u) / idx %3d%% (%5u/%5u) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ")%s\n", 49 | network_percent, kbytes, 50 | fetch_progress.received_objects, fetch_progress.total_objects, 51 | index_percent, fetch_progress.indexed_objects, fetch_progress.total_objects, 52 | checkout_percent, 53 | completed_steps, total_steps, 54 | path); 55 | } 56 | } 57 | }; 58 | 59 | void checkout_progress(const char * path, size_t cur, size_t tot, void * payload) 60 | { 61 | progress_data * pd = static_cast(payload); 62 | pd->completed_steps = cur; 63 | pd->total_steps = tot; 64 | pd->path = path; 65 | pd->print(); 66 | } 67 | 68 | struct FetchCallbacks final : git::Remote::FetchCallbacks 69 | { 70 | FetchCallbacks(progress_data & pd) 71 | : pd_(pd) 72 | { 73 | } 74 | 75 | void sideband_progress(char const * str, int len) override 76 | { 77 | printf("remote: %.*s", len, str); 78 | fflush(stdout); 79 | } 80 | 81 | void transfer_progress(git_indexer_progress const & progress) override 82 | { 83 | pd_.fetch_progress = progress; 84 | pd_.print(); 85 | } 86 | 87 | git_credential * acquire_cred(const char * url, const char * username_from_url, unsigned allowed_types) override 88 | { 89 | return nullptr; 90 | } 91 | 92 | private: 93 | progress_data & pd_; 94 | }; 95 | } 96 | 97 | int main(int argc, char ** argv) 98 | { 99 | /* Validate args */ 100 | if (argc != 3) 101 | { 102 | printf("USAGE: %s \n", argv[0]); 103 | return EXIT_FAILURE; 104 | } 105 | 106 | auto_git_initializer; 107 | 108 | progress_data pd = {{0}}; 109 | FetchCallbacks fetch_callbacks(pd); 110 | git_checkout_options checkout_opts = {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE}; 111 | /* Set up options */ 112 | checkout_opts.progress_cb = checkout_progress; 113 | checkout_opts.progress_payload = &pd; 114 | const char * url = argv[1]; 115 | const char * path = argv[2]; 116 | /* Do the clone */ 117 | try 118 | { 119 | git::Repository::clone(url, path, checkout_opts, fetch_callbacks); 120 | } 121 | catch (git::repository_clone_error err) 122 | { 123 | printf("\n"); 124 | if (auto detailed_info = std::get_if(&err.data)) 125 | printf("ERROR %d: %s\n", detailed_info->klass, detailed_info->message); 126 | else 127 | printf("ERROR %d: no detailed info\n", std::get(err.data)); 128 | return EXIT_FAILURE; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /examples/cat-file.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/id_to_str.h" 2 | #include "git2cpp/initializer.h" 3 | #include "git2cpp/repo.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace { 14 | 15 | [[noreturn]] void usage(const char * message, const char * arg) 16 | { 17 | if (message && arg) 18 | fprintf(stderr, "%s: %s\n", message, arg); 19 | else if (message) 20 | fprintf(stderr, "%s\n", message); 21 | fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [] \n"); 22 | exit(1); 23 | } 24 | 25 | bool check_str_param(const char * arg, const char * pattern, const char ** val) 26 | { 27 | size_t len = strlen(pattern); 28 | if (strncmp(arg, pattern, len)) 29 | return false; 30 | *val = arg + len; 31 | return true; 32 | } 33 | 34 | void print_signature(const char * header, const git_signature * sig) 35 | { 36 | if (!sig) 37 | return; 38 | 39 | int offset = sig->when.offset; 40 | char sign; 41 | if (offset < 0) 42 | { 43 | sign = '-'; 44 | offset = -offset; 45 | } 46 | else 47 | { 48 | sign = '+'; 49 | } 50 | 51 | const int hours = offset / 60; 52 | const int minutes = offset % 60; 53 | 54 | printf("%s %s <%s> %ld %c%02d%02d\n", 55 | header, sig->name, sig->email, (long)sig->when.time, 56 | sign, hours, minutes); 57 | } 58 | 59 | void show_blob(git::Blob const & blob) 60 | { 61 | /* ? Does this need crlf filtering? */ 62 | fwrite(blob.content(), blob.size(), 1, stdout); 63 | } 64 | 65 | void show_tree(git::Tree const & tree) 66 | { 67 | char oidstr[GIT_OID_SHA1_HEXSIZE + 1]; 68 | 69 | for (size_t i = 0, n = tree.entrycount(); i < n; ++i) 70 | { 71 | auto te = tree[i]; 72 | 73 | git_oid_tostr(oidstr, sizeof(oidstr), &te.id()); 74 | 75 | printf("%06o %s %s\t%s\n", 76 | te.filemode(), 77 | git_object_type2string(te.type()), 78 | oidstr, te.name()); 79 | } 80 | } 81 | 82 | void show_commit(git::Commit const & commit) 83 | { 84 | char oidstr[GIT_OID_SHA1_HEXSIZE + 1]; 85 | 86 | git_oid_tostr(oidstr, sizeof(oidstr), &commit.tree_id()); 87 | printf("tree %s\n", oidstr); 88 | 89 | for (size_t i = 0, n = commit.parents_num(); i != n; ++i) 90 | { 91 | git_oid_tostr(oidstr, sizeof(oidstr), &commit.parent_id(i)); 92 | printf("parent %s\n", oidstr); 93 | } 94 | 95 | print_signature("author", commit.author()); 96 | print_signature("committer", commit.commiter()); 97 | 98 | if (auto message = commit.message()) 99 | printf("\n%s\n", message); 100 | } 101 | 102 | void show_tag(git::Tag const & tag) 103 | { 104 | std::cout << "object " << git::id_to_str(tag.target_id()) << "\n" 105 | << "\ntype " << git_object_type2string(tag.target_type()) 106 | << "\ntag " << tag.name() 107 | << std::endl; 108 | print_signature("tagger", tag.tagger()); 109 | 110 | if (auto message = tag.message()) 111 | printf("\n%s\n", message); 112 | } 113 | 114 | enum class Action 115 | { 116 | NONE = 0, 117 | SHOW_TYPE = 1, 118 | SHOW_SIZE = 2, 119 | SHOW_NONE = 3, 120 | SHOW_PRETTY = 4 121 | }; 122 | 123 | } 124 | 125 | int main(int argc, char * argv[]) 126 | { 127 | const char *dir = ".", *rev = nullptr; 128 | int i, verbose = 0; 129 | Action action = Action::NONE; 130 | char oidstr[GIT_OID_SHA1_HEXSIZE + 1]; 131 | 132 | for (i = 1; i < argc; ++i) 133 | { 134 | char * a = argv[i]; 135 | 136 | if (a[0] != '-') 137 | { 138 | if (rev) 139 | usage("Only one rev should be provided", nullptr); 140 | else 141 | rev = a; 142 | } 143 | else if (!strcmp(a, "-t")) 144 | action = Action::SHOW_TYPE; 145 | else if (!strcmp(a, "-s")) 146 | action = Action::SHOW_SIZE; 147 | else if (!strcmp(a, "-e")) 148 | action = Action::SHOW_NONE; 149 | else if (!strcmp(a, "-p")) 150 | action = Action::SHOW_PRETTY; 151 | else if (!strcmp(a, "-q")) 152 | verbose = 0; 153 | else if (!strcmp(a, "-v")) 154 | verbose = 1; 155 | else if (!strcmp(a, "--help") || !strcmp(a, "-h")) 156 | usage(nullptr, nullptr); 157 | else if (!check_str_param(a, "--git-dir=", &dir)) 158 | usage("Unknown option", a); 159 | } 160 | 161 | if (action == Action::NONE || !rev) 162 | usage(nullptr, nullptr); 163 | 164 | git::Initializer threads_initializer; 165 | 166 | git::Repository repo(dir); 167 | git::Object obj = revparse_single(repo, rev); 168 | 169 | if (verbose) 170 | { 171 | std::cout 172 | << git_object_type2string(obj.type()) 173 | << " " 174 | << git::id_to_str(obj.id()) 175 | << "\n--\n"; 176 | } 177 | 178 | switch (action) 179 | { 180 | case Action::SHOW_TYPE: 181 | printf("%s\n", git_object_type2string(obj.type())); 182 | break; 183 | case Action::SHOW_SIZE: 184 | { 185 | git::Odb odb = repo.odb(); 186 | git::OdbObject odbobj = odb.read(obj.id()); 187 | 188 | printf("%zu\n", odbobj.size()); 189 | break; 190 | } 191 | case Action::SHOW_NONE: 192 | /* just want return result */ 193 | break; 194 | case Action::SHOW_PRETTY: 195 | 196 | switch (obj.type()) 197 | { 198 | case GIT_OBJ_BLOB: 199 | show_blob(obj.to_blob()); 200 | break; 201 | case GIT_OBJ_COMMIT: 202 | show_commit(obj.to_commit()); 203 | break; 204 | case GIT_OBJ_TREE: 205 | show_tree(obj.to_tree()); 206 | break; 207 | case GIT_OBJ_TAG: 208 | show_tag(obj.to_tag()); 209 | break; 210 | default: 211 | printf("unknown %s\n", oidstr); 212 | break; 213 | } 214 | break; 215 | default: 216 | assert("unexpected action" && static_cast(action)); 217 | } 218 | 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /include/git2cpp/repo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "repo_fwd.h" 4 | 5 | #include "blame.h" 6 | #include "blob.h" 7 | #include "commit.h" 8 | #include "diff.h" 9 | #include "index.h" 10 | #include "odb.h" 11 | #include "reference.h" 12 | #include "remote.h" 13 | #include "revspec.h" 14 | #include "revwalker.h" 15 | #include "signature.h" 16 | #include "status.h" 17 | #include "str_array.h" 18 | #include "submodule.h" 19 | #include "tag.h" 20 | #include "tree.h" 21 | 22 | #include "internal/optional.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | struct git_blame_options; 29 | 30 | namespace git 31 | { 32 | struct non_existing_branch_error 33 | {}; 34 | struct missing_head_error 35 | {}; 36 | 37 | struct repository_clone_error 38 | { 39 | struct detailed_info 40 | { 41 | char const* message; 42 | int klass; 43 | }; 44 | std::variant data; 45 | }; 46 | 47 | enum class branch_type 48 | { 49 | LOCAL, 50 | REMOTE, 51 | ALL 52 | }; 53 | 54 | struct FileDiffHandler 55 | { 56 | virtual void line(git_diff_line const &) = 0; 57 | 58 | protected: 59 | ~FileDiffHandler() = default; 60 | }; 61 | 62 | struct AnnotatedCommit; 63 | 64 | struct Repository 65 | { 66 | Commit commit_lookup(git_oid const & oid) const; 67 | Tree tree_lookup(git_oid const & oid) const; 68 | Tag tag_lookup(git_oid const & oid) const; 69 | Blob blob_lookup(git_oid const & oid) const; 70 | 71 | git_oid merge_base(Revspec::Range const & range) const; 72 | git_oid merge_base(git_oid const &, git_oid const &) const; 73 | 74 | Revspec revparse(const char * spec) const; 75 | Revspec revparse_single(const char * spec) const; 76 | 77 | RevWalker rev_walker() const; 78 | 79 | git_status_t file_status(const char * filepath) const; 80 | 81 | Object entry_to_object(Tree::OwnedEntry) const; 82 | Object entry_to_object(Tree::BorrowedEntry) const; 83 | 84 | Index index() const; 85 | Odb odb() const; 86 | 87 | Diff diff(Tree &, Tree &, git_diff_options const &) const; 88 | Diff diff_to_index(Tree &, git_diff_options const &) const; 89 | Diff diff_to_workdir(Tree &, git_diff_options const &) const; 90 | Diff diff_to_workdir_with_index(Tree &, git_diff_options const &) const; 91 | Diff diff_index_to_workdir(git_diff_options const &) const; 92 | 93 | Signature signature() const; 94 | 95 | Status status(Status::Options const &) const; 96 | 97 | git_repository_state_t state() const; 98 | 99 | StrArray reference_list() const; 100 | 101 | std::vector branches(branch_type, git_reference_t ref_kind = GIT_REFERENCE_ALL) const; 102 | 103 | Reference create_branch(const char * name, Commit const & target, bool force); 104 | 105 | /// @return can be empty 106 | Reference dwim(const char * shorthand) const; 107 | 108 | AnnotatedCommit annotated_commit_from_ref(Reference const &) const; 109 | AnnotatedCommit annotated_commit_lookup(git_oid const &) const; 110 | 111 | bool is_bare() const; 112 | 113 | Reference head() const; 114 | Reference ref(const char * name) const; 115 | 116 | Submodule submodule_lookup(const char * name) const; 117 | 118 | const char * path() const; 119 | const char * workdir() const; 120 | 121 | git_oid create_commit(const char * update_ref, 122 | Signature const & author, 123 | Signature const & commiter, 124 | const char * message, 125 | Tree const & tree, 126 | const char * message_encoding = nullptr); 127 | 128 | git_oid create_commit(const char * update_ref, 129 | Signature const & author, 130 | Signature const & commiter, 131 | const char * message, 132 | Tree const & tree, 133 | Commit const & parent, 134 | const char * message_encoding = nullptr); 135 | 136 | git_oid amend_commit(Commit const & commit_to_amend, const char * update_ref, const char * message, Tree const & tree); 137 | 138 | void reset_default(Commit const &, git_strarray const & pathspecs); 139 | 140 | void file_diff(std::string const & old_path, git_oid const & old_id, 141 | std::string const & new_path, git_oid const & new_id, FileDiffHandler &) const; 142 | 143 | StrArray remotes() const; 144 | Remote remote(const char * name) const; 145 | Remote create_remote(const char * name, const char * url); 146 | void delete_remote(const char * name); 147 | internal::optional rename_remote(const char * old_name, const char * new_name); 148 | void set_url (const char * name, const char * url); 149 | void set_pushurl(const char * name, const char * url); 150 | 151 | /// @return raw error code 152 | int checkout_tree(Commit const &, git_checkout_options const &); 153 | int checkout_head(git_checkout_options const &); 154 | 155 | /// @return raw error code 156 | int set_head(char const* ref); 157 | int set_head_detached(git_oid const&); 158 | int set_head_detached(AnnotatedCommit const&); 159 | 160 | Blame blame_file(const char * path, git_blame_options const &); 161 | 162 | explicit Repository(const char * dir); 163 | explicit Repository(std::string const & dir); 164 | 165 | struct init_tag 166 | {}; 167 | static const init_tag init; 168 | Repository(const char * dir, init_tag); 169 | Repository(const char * dir, init_tag, git_repository_init_options opts); 170 | Repository(std::string const & dir, init_tag); 171 | 172 | static Repository clone(const char * url, const char* path, git_checkout_options const &, Remote::FetchCallbacks &); 173 | 174 | static internal::optional discover(const char * start_path); 175 | 176 | private: 177 | struct Destroy { void operator() (git_repository *) const; }; 178 | std::unique_ptr repo_; 179 | 180 | explicit Repository(git_repository*); 181 | }; 182 | 183 | Object revparse_single(Repository const & repo, const char * spec); 184 | } 185 | -------------------------------------------------------------------------------- /examples/remote.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * libgit2 "remote" example - shows how to modify remotes for a repo 3 | * 4 | * Written by the libgit2 contributors 5 | * 6 | * To the extent possible under law, the author(s) have dedicated all copyright 7 | * and related and neighboring rights to this software to the public domain 8 | * worldwide. This software is distributed without any warranty. 9 | * 10 | * You should have received a copy of the CC0 Public Domain Dedication along 11 | * with this software. If not, see 12 | * . 13 | */ 14 | 15 | #include "git2cpp/repo.h" 16 | #include "git2cpp/initializer.h" 17 | 18 | #include "git2/errors.h" 19 | 20 | #include 21 | 22 | /** 23 | * This is a sample program that is similar to "git remote". See the 24 | * documentation for that (try "git help remote") to understand what this 25 | * program is emulating. 26 | * 27 | * This demonstrates using the libgit2 APIs to modify remotes of a repository. 28 | */ 29 | 30 | namespace { 31 | 32 | [[noreturn]] void usage(const char *msg, const char *arg = nullptr) 33 | { 34 | fputs("usage: remote add \n", stderr); 35 | fputs(" remote remove \n", stderr); 36 | fputs(" remote rename \n", stderr); 37 | fputs(" remote set-url [--push] \n", stderr); 38 | fputs(" remote show [-v|--verbose]\n", stderr); 39 | 40 | if (msg && !arg) 41 | fprintf(stderr, "\n%s\n", msg); 42 | else if (msg && arg) 43 | fprintf(stderr, "\n%s: %s\n", msg, arg); 44 | exit(1); 45 | } 46 | 47 | [[noreturn]] void report_error(const char *message) 48 | { 49 | const git_error *lg2err = git_error_last(); 50 | const char *lg2msg = "", *lg2spacer = ""; 51 | 52 | if (lg2err && lg2err->message) 53 | { 54 | lg2msg = lg2err->message; 55 | lg2spacer = " - "; 56 | } 57 | 58 | fprintf(stderr, "%s %s%s\n", message, lg2spacer, lg2msg); 59 | exit(1); 60 | } 61 | 62 | enum class Subcmd { 63 | add, 64 | remove, 65 | rename, 66 | seturl, 67 | show, 68 | }; 69 | 70 | struct Options { 71 | Subcmd cmd; 72 | 73 | /* for command-specific args */ 74 | int argc; 75 | char **argv; 76 | 77 | Options(int argc, char **argv) 78 | : argc(argc - 2) /* executable and subcommand are removed */ 79 | , argv(argv + 2) 80 | { 81 | char *arg = argv[1]; 82 | if (argc < 2) 83 | usage("no command specified"); 84 | 85 | if (!strcmp(arg, "add")) { 86 | cmd = Subcmd::add; 87 | } else if (!strcmp(arg, "remove")) { 88 | cmd = Subcmd::remove; 89 | } else if (!strcmp(arg, "rename")) { 90 | cmd = Subcmd::rename; 91 | } else if (!strcmp(arg, "set-url")) { 92 | cmd = Subcmd::seturl; 93 | } else if (!strcmp(arg, "show")) { 94 | cmd = Subcmd::show; 95 | } else { 96 | usage("command is not valid", arg); 97 | } 98 | } 99 | }; 100 | 101 | using git::Repository; 102 | 103 | void cmd_add(Repository & repo, Options const & o) 104 | { 105 | if (o.argc != 2) 106 | usage("you need to specify a name and URL"); 107 | 108 | auto name = o.argv[0], url = o.argv[1]; 109 | repo.create_remote(name, url); 110 | } 111 | 112 | void cmd_remove(Repository & repo, Options const & o) 113 | { 114 | if (o.argc != 1) 115 | usage("you need to specify a name"); 116 | 117 | auto name = o.argv[0]; 118 | repo.delete_remote(name); 119 | } 120 | 121 | void cmd_rename(Repository & repo, Options const & o) 122 | { 123 | if (o.argc != 2) 124 | usage("you need to specify old and new remote name"); 125 | 126 | auto old_name = o.argv[0]; 127 | auto new_name = o.argv[1]; 128 | if (auto problems = repo.rename_remote(old_name, new_name)) 129 | { 130 | for (size_t i = 0, n = problems->count(); i != n; ++i) 131 | puts((*problems)[i]); 132 | } 133 | } 134 | 135 | void cmd_seturl(Repository & repo, Options const & o) 136 | { 137 | bool push = false; 138 | char const * name = nullptr, *url = nullptr; 139 | 140 | for (auto i = 0; i != o.argc; ++i) 141 | { 142 | char const * arg = o.argv[i]; 143 | 144 | if (!strcmp(arg, "--push")) { 145 | push = 1; 146 | } else if (arg[0] != '-' && !name) { 147 | name = arg; 148 | } else if (arg[0] != '-' && !url) { 149 | url = arg; 150 | } else { 151 | usage("invalid argument to set-url", arg); 152 | } 153 | } 154 | 155 | if (!name || !url) 156 | usage("you need to specify remote and the new URL"); 157 | 158 | if (push) 159 | repo.set_pushurl(name, url); 160 | else 161 | repo.set_url(name, url); 162 | } 163 | 164 | void cmd_show(Repository const & repo, Options const & o) 165 | { 166 | bool verbose = false; 167 | 168 | for (int i = 0; i != o.argc; ++i) 169 | { 170 | auto arg = o.argv[i]; 171 | 172 | if (!strcmp(arg, "-v") || 173 | !strcmp(arg, "--verbose")) 174 | { 175 | verbose = true; 176 | } 177 | } 178 | 179 | auto remotes = repo.remotes(); 180 | 181 | for (size_t i = 0, n = remotes.count(); i != n; ++i) 182 | { 183 | auto name = remotes[i]; 184 | if (!verbose) { 185 | puts(name); 186 | continue; 187 | } 188 | 189 | auto remote = repo.remote(name); 190 | 191 | auto fetch = remote.url(); 192 | if (fetch) 193 | printf("%s\t%s (fetch)\n", name, fetch); 194 | auto push = remote.pushurl(); 195 | /* use fetch URL if no distinct push URL has been set */ 196 | push = push ? push : fetch; 197 | if (push) 198 | printf("%s\t%s (push)\n", name, push); 199 | } 200 | } 201 | 202 | } 203 | 204 | int main(int argc, char *argv[]) 205 | { 206 | Options opt(argc, argv); 207 | 208 | auto_git_initializer; 209 | 210 | auto path_to_repo = git::Repository::discover("."); 211 | if (!path_to_repo) 212 | report_error("Could not find repository"); 213 | 214 | Repository repo(*path_to_repo); 215 | 216 | switch (opt.cmd) 217 | { 218 | case Subcmd::add: 219 | cmd_add(repo, opt); 220 | break; 221 | case Subcmd::remove: 222 | cmd_remove(repo, opt); 223 | break; 224 | case Subcmd::rename: 225 | cmd_rename(repo, opt); 226 | break; 227 | case Subcmd::seturl: 228 | cmd_seturl(repo, opt); 229 | break; 230 | case Subcmd::show: 231 | cmd_show(repo, opt); 232 | break; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /examples/blame.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * libgit2 "blame" example - shows how to use the blame API 3 | * 4 | * Written by the libgit2 contributors 5 | * 6 | * To the extent possible under law, the author(s) have dedicated all copyright 7 | * and related and neighboring rights to this software to the public domain 8 | * worldwide. This software is distributed without any warranty. 9 | * 10 | * You should have received a copy of the CC0 Public Domain Dedication along 11 | * with this software. If not, see 12 | * . 13 | */ 14 | 15 | #include "git2cpp/repo.h" 16 | #include "git2cpp/initializer.h" 17 | #include "git2/blame.h" 18 | 19 | #ifdef _MSC_VER 20 | #define snprintf sprintf_s 21 | #define strcasecmp strcmpi 22 | #endif 23 | 24 | /** 25 | * This example demonstrates how to invoke the libgit2 blame API to roughly 26 | * simulate the output of `git blame` and a few of its command line arguments. 27 | */ 28 | 29 | struct opts { 30 | const char * path; 31 | const char * commitspec = nullptr; 32 | int start_line; 33 | int end_line; 34 | bool C = false; 35 | bool M = false; 36 | bool F = false; 37 | 38 | opts(int argc, char *argv[]); 39 | }; 40 | 41 | int main(int argc, char *argv[]) 42 | { 43 | opts o(argc, argv); 44 | git_blame_options blameopts = GIT_BLAME_OPTIONS_INIT; 45 | 46 | if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES; 47 | if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES; 48 | if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT; 49 | 50 | auto_git_initializer; 51 | 52 | git::Repository repo("."); 53 | /** 54 | * The commit range comes in "commitish" form. Use the rev-parse API to 55 | * nail down the end points. 56 | */ 57 | if (o.commitspec) 58 | { 59 | auto revspec = repo.revparse(o.commitspec); 60 | 61 | if (revspec.flags() & GIT_REVSPEC_SINGLE) 62 | { 63 | blameopts.newest_commit = revspec.single()->id(); 64 | } 65 | else 66 | { 67 | auto const & range = *revspec.range(); 68 | blameopts.oldest_commit = range.from.id(); 69 | blameopts.newest_commit = range.to.id(); 70 | } 71 | } 72 | 73 | /** Run the blame. */ 74 | auto blame = repo.blame_file(o.path, blameopts); 75 | 76 | /** 77 | * Get the raw data inside the blob for output. We use the 78 | * `commitish:path/to/file.txt` format to find it. 79 | */ 80 | std::string spec = git_oid_is_zero(&blameopts.newest_commit) 81 | ? "HEAD" 82 | : git::id_to_str(blameopts.newest_commit); 83 | spec += ":"; 84 | spec += o.path; 85 | 86 | auto blob = repo.blob_lookup(repo.revparse_single(spec.c_str()).single()->id()); 87 | 88 | char const * rawdata = reinterpret_cast(blob.content()); 89 | size_t rawsize = blob.size(); 90 | 91 | /** Produce the output. */ 92 | size_t line = 1; 93 | bool break_on_null_hunk = false; 94 | for (size_t i = 0; i < rawsize; ++line) { 95 | const git_blame_hunk *hunk = blame.hunk_byline(line); 96 | 97 | if (break_on_null_hunk && !hunk) 98 | break; 99 | 100 | char const * eol = reinterpret_cast(memchr(rawdata + i, '\n', rawsize - i)); 101 | if (hunk) { 102 | break_on_null_hunk = true; 103 | 104 | char oid[10] = {0}; 105 | git_oid_tostr(oid, 10, &hunk->final_commit_id); 106 | char sig[128] = {0}; 107 | snprintf(sig, 127, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email); 108 | 109 | printf("%s ( %-30s %3d) %.*s\n", 110 | oid, 111 | sig, 112 | (int)line, 113 | (int)(eol - rawdata - i), 114 | rawdata + i); 115 | } 116 | 117 | i = eol - rawdata + 1; 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | /** Tell the user how to make this thing work. */ 124 | static void usage(const char *msg) 125 | { 126 | if (msg) 127 | fprintf(stderr, "%s\n", msg); 128 | fprintf(stderr, "usage: blame [options] [] \n"); 129 | fprintf(stderr, "\n"); 130 | fprintf(stderr, " example: `HEAD~10..HEAD`, or `1234abcd`\n"); 131 | fprintf(stderr, " -L process only line range n-m, counting from 1\n"); 132 | fprintf(stderr, " -M find line moves within and across files\n"); 133 | fprintf(stderr, " -C find line copies within and across files\n"); 134 | fprintf(stderr, " -F follow only the first parent commits\n"); 135 | fprintf(stderr, "\n"); 136 | exit(1); 137 | } 138 | 139 | /** Parse the arguments. */ 140 | opts::opts(int argc, char *argv[]) 141 | { 142 | int i; 143 | const char * bare_args[3] = {}; 144 | 145 | if (argc < 2) usage(nullptr); 146 | 147 | for (i=1; i= 3) 154 | usage("Invalid argument set"); 155 | bare_args[i] = a; 156 | } 157 | else if (!strcmp(a, "--")) 158 | continue; 159 | else if (!strcasecmp(a, "-M")) 160 | M = true; 161 | else if (!strcasecmp(a, "-C")) 162 | C = true; 163 | else if (!strcasecmp(a, "-F")) 164 | F = true; 165 | else if (!strcasecmp(a, "-L")) { 166 | i++; a = argv[i]; 167 | if (i >= argc) throw std::runtime_error("Not enough arguments to -L"); 168 | if (sscanf(a, "%d,%d", &start_line, &end_line) != 2) 169 | { 170 | fprintf(stderr, "-L format error\n"); 171 | std::abort(); 172 | } 173 | } 174 | else { 175 | /* commit range */ 176 | if (commitspec) throw std::runtime_error("Only one commit spec allowed"); 177 | commitspec = a; 178 | } 179 | } 180 | 181 | /* Handle the bare arguments */ 182 | if (!bare_args[0]) usage("Please specify a path"); 183 | path = bare_args[0]; 184 | if (bare_args[1]) { 185 | /* */ 186 | path = bare_args[1]; 187 | commitspec = bare_args[0]; 188 | } 189 | if (bare_args[2]) { 190 | /* */ 191 | char spec[128] = {0}; 192 | path = bare_args[2]; 193 | sprintf(spec, "%s..%s", bare_args[0], bare_args[1]); 194 | commitspec = spec; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /include/git2cpp/internal/optional.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef USE_BOOST 4 | #include 5 | #include 6 | #define GIT_USE_BOOST_OPTIONAL 7 | 8 | #elif defined(__has_include) && __has_include() 9 | #include 10 | #define GIT_USE_STD_OPTIONAL 11 | 12 | #elif defined(__has_include) && __has_include() 13 | #include 14 | #define GIT_USE_STD_EXPERIMENTAL_OPTIONAL 15 | 16 | #else 17 | #include 18 | #include 19 | #endif 20 | 21 | namespace git { 22 | namespace internal 23 | { 24 | #if defined(GIT_USE_BOOST_OPTIONAL) 25 | #undef GIT_USE_BOOST_OPTIONAL 26 | using boost::optional; 27 | using boost::none_t; 28 | using boost::none; 29 | 30 | template 31 | optional & emplace(optional & opt, Args&&... args) 32 | { 33 | opt = boost::in_place(std::forward(args)...); 34 | return opt; 35 | } 36 | 37 | template 38 | bool has_value(optional const & opt) 39 | { 40 | return opt.is_initialized(); 41 | } 42 | 43 | #elif defined(GIT_USE_STD_OPTIONAL) 44 | #undef GIT_USE_STD_OPTIONAL 45 | using std::optional; 46 | using none_t = std::nullopt_t; 47 | const none_t none = std::nullopt; 48 | 49 | template 50 | optional & emplace(optional & opt, Args&&... args) 51 | { 52 | opt.emplace(std::forward(args)...); 53 | return opt; 54 | } 55 | 56 | template 57 | bool has_value(optional const & opt) 58 | { 59 | return opt.has_value(); 60 | } 61 | 62 | #elif defined(GIT_USE_STD_EXPERIMENTAL_OPTIONAL) 63 | #undef GIT_USE_STD_EXPERIMENTAL_OPTIONAL 64 | using std::experimental::optional; 65 | using none_t = std::experimental::nullopt_t; 66 | const none_t none = std::experimental::nullopt; 67 | 68 | template 69 | optional & emplace(optional & opt, Args&&... args) 70 | { 71 | opt.emplace(std::forward(args)...); 72 | return opt; 73 | } 74 | 75 | template 76 | bool has_value(optional const & opt) 77 | { 78 | return static_cast(opt); 79 | } 80 | 81 | #else 82 | typedef std::nullptr_t none_t; 83 | const none_t none = nullptr; 84 | 85 | //Simple (and incomplete) optional implementations 86 | template 87 | class optional 88 | { 89 | private: //type alias 90 | typedef typename std::aligned_storage::type StorageT; 91 | 92 | public: //type traits 93 | enum non_movable_t{}; 94 | enum non_copyable_t{}; 95 | 96 | static const bool movable = std::is_move_constructible::value && std::is_move_assignable::value; 97 | static const bool copyable = std::is_copy_constructible::value && std::is_copy_assignable::value; 98 | typedef typename std::conditional::type MovableT; 99 | typedef typename std::conditional::type CopyableT; 100 | 101 | public: //initializing and attribution 102 | constexpr optional(none_t t = none) 103 | : initialized(false) 104 | {} 105 | 106 | optional(T const & v) { assign(v);} 107 | optional(T && v) { assign(std::move(v));} 108 | 109 | optional(optional const & other) { assign(other);} 110 | optional(optional && other) { assign(std::move(other));} 111 | 112 | optional & operator=(none_t none) { reset(); return *this;} 113 | optional & operator=(T && v) { return assign(std::move(v));} 114 | optional & operator=(const T & v) { return assign(v);} 115 | 116 | optional & operator=(optional const & other){ return assign(other);} 117 | optional & operator=(optional && other){ return assign(std::move(other));} 118 | 119 | ~optional() { reset(); } 120 | 121 | public: //getting value 122 | 123 | T const & value() const { return reinterpret_cast(storage); } 124 | T & value() { return reinterpret_cast(storage); } 125 | 126 | T const & operator* () const { return value(); } 127 | T & operator * () { return value(); } 128 | 129 | T const * operator -> () const { return &value(); } 130 | T * operator -> () { return &value(); } 131 | 132 | public: //querying state 133 | 134 | explicit operator bool() const { return initialized; } 135 | 136 | public: //change state 137 | void reset() 138 | { 139 | if (initialized) 140 | { 141 | value().~T(); 142 | initialized = false; 143 | } 144 | } 145 | 146 | template 147 | void emplace(Args&&... args){ 148 | if (initialized) 149 | reset(); 150 | init(std::forward(args)...); 151 | } 152 | 153 | private: //assign 154 | optional & assign(MovableT && value) { 155 | return set_from_value(std::move(value)); 156 | } 157 | optional & assign(CopyableT const & value) { 158 | return set_from_value(value); 159 | } 160 | optional & assign(optional const & other) { 161 | return set_or_reset(other.value(), other.initialized); 162 | } 163 | optional & assign(optional && other) { 164 | return set_or_reset(std::move(other.value()), other.initialized); 165 | } 166 | 167 | protected: 168 | template 169 | optional & set_or_reset(U&& u, bool isValid) 170 | { 171 | if (isValid) 172 | set_from_value(std::forward(u)); 173 | else 174 | reset(); 175 | 176 | return *this; 177 | } 178 | 179 | template 180 | optional & set_from_value(U&& u) 181 | { 182 | if (initialized) 183 | reinterpret_cast(storage) = std::forward(u); 184 | else 185 | init(std::forward(u)); 186 | 187 | return *this; 188 | } 189 | 190 | template 191 | void init(Args&& ... args){ 192 | new (&storage) T(std::forward(args)...); 193 | initialized = true; 194 | } 195 | 196 | protected: 197 | StorageT storage; 198 | bool initialized; 199 | }; 200 | 201 | template 202 | optional & emplace(optional & opt, Args&&... args){ 203 | opt.emplace(std::forward(args)...); 204 | return opt; 205 | } 206 | 207 | template 208 | bool has_value(optional const & opt) 209 | { 210 | return static_cast(opt); 211 | } 212 | #endif 213 | 214 | }}//namespace git 215 | -------------------------------------------------------------------------------- /examples/init.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample program that is similar to "git init". See the 3 | * documentation for that (try "git help init") to understand what this 4 | * program is emulating. 5 | * 6 | * This demonstrates using the libgit2 APIs to initialize a new repository. 7 | * 8 | * This also contains a special additional option that regular "git init" 9 | * does not support which is "--initial-commit" to make a first empty commit. 10 | * That is demonstrated in the "create_initial_commit" helper function. 11 | * 12 | * Copyright (C) the libgit2 contributors. All rights reserved. 13 | * 14 | * This file is part of libgit2, distributed under the GNU GPL v2 with 15 | * a Linking Exception. For full terms see the included COPYING file. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "git2cpp/initializer.h" 24 | #include "git2cpp/repo.h" 25 | 26 | #ifdef USE_BOOST 27 | #include 28 | 29 | using StringView = boost::string_view; 30 | #else 31 | #include 32 | 33 | using StringView = std::string_view; 34 | #endif 35 | 36 | namespace { 37 | 38 | [[noreturn]] void usage(const char * error, const char * arg) 39 | { 40 | fprintf(stderr, "error: %s '%s'\n", error, arg); 41 | fprintf(stderr, "usage: init [-q | --quiet] [--bare] " 42 | "[--template=] [--shared[=perms]] \n"); 43 | exit(1); 44 | } 45 | 46 | /* simple string prefix test used in argument parsing */ 47 | size_t is_prefixed(StringView arg, StringView pfx) 48 | { 49 | return arg == pfx ? pfx.size() : 0; 50 | } 51 | 52 | /* parse the tail of the --shared= argument */ 53 | uint32_t parse_shared(const char * shared) 54 | { 55 | if (!strcmp(shared, "false") || !strcmp(shared, "umask")) 56 | return GIT_REPOSITORY_INIT_SHARED_UMASK; 57 | 58 | else if (!strcmp(shared, "true") || !strcmp(shared, "group")) 59 | return GIT_REPOSITORY_INIT_SHARED_GROUP; 60 | 61 | else if (!strcmp(shared, "all") || !strcmp(shared, "world") || 62 | !strcmp(shared, "everybody")) 63 | return GIT_REPOSITORY_INIT_SHARED_ALL; 64 | 65 | else if (shared[0] == '0') 66 | { 67 | char * end = nullptr; 68 | long val = strtol(shared + 1, &end, 8); 69 | if (end == shared + 1 || *end != 0) 70 | usage("invalid octal value for --shared", shared); 71 | return (uint32_t)val; 72 | } 73 | 74 | else 75 | usage("unknown value for --shared", shared); 76 | } 77 | 78 | using namespace git; 79 | 80 | /* Unlike regular "git init", this example shows how to create an initial 81 | * empty commit in the repository. This is the helper function that does 82 | * that. 83 | */ 84 | static void create_initial_commit(Repository & repo) 85 | { 86 | /* First use the config to initialize a commit signature for the user */ 87 | Signature sig = repo.signature(); 88 | 89 | /* Now let's create an empty tree for this commit */ 90 | Index index = repo.index(); 91 | 92 | /* Outside of this example, you could call git_index_add_bypath() 93 | * here to put actual files into the index. For our purposes, we'll 94 | * leave it empty for now. 95 | */ 96 | 97 | git_oid tree_id = index.write_tree(); 98 | Tree tree = repo.tree_lookup(tree_id); 99 | 100 | /* Ready to create the initial commit 101 | * 102 | * Normally creating a commit would involve looking up the current 103 | * HEAD commit and making that be the parent of the initial commit, 104 | * but here this is the first commit so there will be no parent. 105 | */ 106 | repo.create_commit("HEAD", sig, sig, "Initial commit", tree); 107 | } 108 | 109 | git_repository_init_options make_opts(bool bare, const char * templ, 110 | uint32_t shared, 111 | const char * gitdir, 112 | const char * dir) 113 | { 114 | git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; 115 | 116 | if (bare) 117 | opts.flags |= GIT_REPOSITORY_INIT_BARE; 118 | 119 | if (templ) 120 | { 121 | opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; 122 | opts.template_path = templ; 123 | } 124 | 125 | if (gitdir) 126 | { 127 | /* if you specified a separate git directory, then initialize 128 | * the repository at that path and use the second path as the 129 | * working directory of the repository (with a git-link file) 130 | */ 131 | opts.workdir_path = dir; 132 | dir = gitdir; 133 | } 134 | 135 | if (shared != 0) 136 | opts.mode = shared; 137 | 138 | return opts; 139 | } 140 | 141 | } 142 | 143 | int main(int argc, char * argv[]) 144 | { 145 | bool no_options = true, quiet = false, bare = false, initial_commit = false; 146 | uint32_t shared = GIT_REPOSITORY_INIT_SHARED_UMASK; 147 | const char *templ = nullptr, *gitdir = nullptr, *dir = nullptr; 148 | 149 | auto_git_initializer; 150 | 151 | /* Process arguments */ 152 | 153 | for (int i = 1; i < argc; ++i) 154 | { 155 | auto arg = argv[i]; 156 | StringView a = arg; 157 | 158 | if (arg[0] == '-') 159 | no_options = false; 160 | 161 | if (arg[0] != '-') 162 | { 163 | if (dir) 164 | usage("extra argument", arg); 165 | dir = arg; 166 | } 167 | else if (a == "-q" || a == "--quiet") 168 | quiet = true; 169 | else if (a == "--bare") 170 | bare = true; 171 | else if (auto pfxlen = is_prefixed(a, "--template=")) 172 | templ = arg + pfxlen; 173 | else if (a == "--separate-git-dir") 174 | gitdir = argv[++i]; 175 | else if (auto pfxlen = is_prefixed(a, "--separate-git-dir=")) 176 | gitdir = arg + pfxlen; 177 | else if (a == "--shared") 178 | shared = GIT_REPOSITORY_INIT_SHARED_GROUP; 179 | else if (auto pfxlen = is_prefixed(a, "--shared=")) 180 | shared = parse_shared(arg + pfxlen); 181 | else if (a == "--initial-commit") 182 | initial_commit = true; 183 | else 184 | usage("unknown option", arg); 185 | } 186 | 187 | if (!dir) 188 | usage("must specify directory to init", nullptr); 189 | 190 | /* Initialize repository */ 191 | 192 | Repository repo = no_options 193 | ? Repository(dir, Repository::init) 194 | : Repository(dir, Repository::init, make_opts(bare, templ, shared, gitdir, dir)); 195 | 196 | /* Print a message to stdout like "git init" does */ 197 | 198 | if (!quiet) 199 | { 200 | printf("Initialized empty Git repository in %s\n", 201 | (bare || gitdir) ? repo.path() : repo.workdir()); 202 | } 203 | 204 | /* As an extension to the basic "git init" command, this example 205 | * gives the option to create an empty initial commit. This is 206 | * mostly to demonstrate what it takes to do that, but also some 207 | * people like to have that empty base commit in their repo. 208 | */ 209 | if (initial_commit) 210 | { 211 | create_initial_commit(repo); 212 | printf("Created empty initial commit\n"); 213 | } 214 | 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /include/git2cpp/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "id_to_str.h" 6 | #include "internal/format.h" 7 | 8 | namespace git 9 | { 10 | 11 | struct error_t : std::runtime_error 12 | { 13 | explicit error_t(std::string const & message) 14 | : std::runtime_error(message) 15 | {} 16 | }; 17 | 18 | struct repository_open_error : error_t 19 | { 20 | repository_open_error(std::string const & dir) 21 | : error_t("Could not open repository on path " + dir) 22 | {} 23 | }; 24 | 25 | struct repository_init_error : error_t 26 | { 27 | explicit repository_init_error(std::string const & dir) 28 | : error_t("Could not initialize repository on path " + dir) 29 | {} 30 | }; 31 | 32 | struct index_open_error : error_t 33 | { 34 | index_open_error() 35 | : error_t("Could not open repository index") 36 | {} 37 | }; 38 | 39 | struct odb_open_error : error_t 40 | { 41 | odb_open_error() 42 | : error_t("Could not open ODB") 43 | {} 44 | }; 45 | 46 | struct commit_lookup_error : error_t 47 | { 48 | explicit commit_lookup_error(git_oid const & id) 49 | : error_t("Could not lookup commit " + id_to_str(id)) 50 | {} 51 | }; 52 | 53 | struct tree_lookup_error : error_t 54 | { 55 | explicit tree_lookup_error(git_oid const & id) 56 | : error_t("Could not lookup tree " + id_to_str(id)) 57 | {} 58 | }; 59 | 60 | struct tag_lookup_error : error_t 61 | { 62 | explicit tag_lookup_error(git_oid const & id) 63 | : error_t("Could not lookup tag " + id_to_str(id)) 64 | {} 65 | }; 66 | 67 | struct blob_lookup_error : error_t 68 | { 69 | explicit blob_lookup_error(git_oid const & id) 70 | : error_t("Could not lookup blob " + id_to_str(id)) 71 | {} 72 | }; 73 | 74 | struct submodule_lookup_error : error_t 75 | { 76 | explicit submodule_lookup_error(std::string const & name) 77 | : error_t("Could not lookup submodule " + name) 78 | {} 79 | }; 80 | 81 | struct remote_lookup_error : error_t 82 | { 83 | explicit remote_lookup_error(std::string const & name) 84 | : error_t("Could not lookup remote " + name) 85 | {} 86 | }; 87 | 88 | struct remote_delete_error : error_t 89 | { 90 | explicit remote_delete_error(std::string const & name) 91 | : error_t("Could not delete remote " + name) 92 | {} 93 | }; 94 | 95 | struct remote_create_error : error_t 96 | { 97 | explicit remote_create_error(const char * name, const char * url) 98 | : error_t(internal::format("Could not create remote '%s', url='%s'", name, url)) 99 | {} 100 | }; 101 | 102 | struct remote_set_url_error : error_t 103 | { 104 | explicit remote_set_url_error(const char * name, const char * url) 105 | : error_t(internal::format("Could not set url '%s' for remote '%s'", url, name)) 106 | {} 107 | }; 108 | 109 | struct remote_set_pushurl_error : error_t 110 | { 111 | explicit remote_set_pushurl_error(const char * name, const char * url) 112 | : error_t(internal::format("Could not set pushurl '%s' for remote '%s'", url, name)) 113 | {} 114 | }; 115 | 116 | struct commit_parent_error : error_t 117 | { 118 | explicit commit_parent_error(git_oid const & id) 119 | : error_t("Could not get parent for commit " + id_to_str(id)) 120 | {} 121 | }; 122 | 123 | struct commit_tree_error : error_t 124 | { 125 | explicit commit_tree_error(git_oid const & id) 126 | : error_t("Could not get tree for commit " + id_to_str(id)) 127 | {} 128 | }; 129 | 130 | struct revparse_error : error_t 131 | { 132 | explicit revparse_error(const char * spec) 133 | : error_t(internal::format("Could not resolve %s", spec)) 134 | {} 135 | }; 136 | 137 | struct odb_read_error : error_t 138 | { 139 | explicit odb_read_error(git_oid const & id) 140 | : error_t("Could not find obj " + id_to_str(id)) 141 | {} 142 | }; 143 | 144 | struct odb_write_error : error_t 145 | { 146 | odb_write_error() 147 | : error_t("odb write error") 148 | {} 149 | }; 150 | 151 | struct file_not_found_error : error_t 152 | { 153 | explicit file_not_found_error(const char * filepath) 154 | : error_t(internal::format("file path \"%s\" not found", filepath)) 155 | {} 156 | }; 157 | 158 | struct ambiguous_path_error : error_t 159 | { 160 | explicit ambiguous_path_error(const char * filepath) 161 | : error_t(internal::format("file path \"%s\" is ambiguous", filepath)) 162 | {} 163 | }; 164 | 165 | struct unknown_file_status_error : error_t 166 | { 167 | explicit unknown_file_status_error(const char * filepath) 168 | : error_t(internal::format("unknown error during getting status for file \"%s\"", filepath)) 169 | {} 170 | }; 171 | 172 | struct pathspec_new_error : error_t 173 | { 174 | pathspec_new_error() 175 | : error_t("Could not build pathspec") 176 | {} 177 | }; 178 | 179 | struct revwalk_new_error : error_t 180 | { 181 | revwalk_new_error() 182 | : error_t("Could not create revision walker") 183 | {} 184 | }; 185 | 186 | struct invalid_head_error : error_t 187 | { 188 | invalid_head_error() 189 | : error_t("Could not find repository HEAD") 190 | {} 191 | }; 192 | 193 | struct non_commit_object_error : error_t 194 | { 195 | explicit non_commit_object_error(git_oid const & id) 196 | : error_t("object " + id_to_str(id) + " is not a commit") 197 | {} 198 | }; 199 | 200 | struct unknown_get_current_branch_error : error_t 201 | { 202 | unknown_get_current_branch_error() 203 | : error_t("failed to get current branch") 204 | {} 205 | }; 206 | 207 | struct get_status_error : error_t 208 | { 209 | get_status_error() 210 | : error_t("Could not get status") 211 | {} 212 | }; 213 | 214 | struct signature_create_error : error_t 215 | { 216 | signature_create_error() 217 | : error_t("Unable to create a commit signature." 218 | " Perhaps 'user.name' and 'user.email' are not set") 219 | {} 220 | }; 221 | 222 | struct index_write_tree_error : error_t 223 | { 224 | index_write_tree_error() 225 | : error_t("Unable to write tree from index") 226 | {} 227 | }; 228 | 229 | struct index_write_error : error_t 230 | { 231 | index_write_error() 232 | : error_t("Unable to write index") 233 | {} 234 | }; 235 | 236 | struct commit_create_error : error_t 237 | { 238 | commit_create_error() 239 | : error_t("Could not create commit") 240 | {} 241 | }; 242 | 243 | struct merge_base_error : error_t 244 | { 245 | merge_base_error(git_oid const & c1, git_oid const & c2) 246 | : error_t("Could not find merge base for commits " + id_to_str(c1, 8) + " and " + id_to_str(c2, 8)) 247 | {} 248 | }; 249 | 250 | struct config_open_error : error_t 251 | { 252 | config_open_error() 253 | : error_t("Could not open config") 254 | {} 255 | }; 256 | 257 | struct branch_create_error : error_t 258 | { 259 | enum reason_t 260 | { 261 | already_exists, 262 | invalid_spec, 263 | unknown 264 | } reason; 265 | 266 | branch_create_error(reason_t r) 267 | : error_t("Could not create branch") 268 | , reason(r) 269 | {} 270 | }; 271 | 272 | struct blame_file_error : error_t 273 | { 274 | explicit blame_file_error(std::string const & path) 275 | : error_t("Could not blame file " + path) 276 | {} 277 | }; 278 | } 279 | -------------------------------------------------------------------------------- /examples/checkout.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * libgit2 "checkout" example - shows how to perform checkouts 3 | * 4 | * Written by the libgit2 contributors 5 | * 6 | * To the extent possible under law, the author(s) have dedicated all copyright 7 | * and related and neighboring rights to this software to the public domain 8 | * worldwide. This software is distributed without any warranty. 9 | * 10 | * You should have received a copy of the CC0 Public Domain Dedication along 11 | * with this software. If not, see 12 | * . 13 | */ 14 | 15 | #include "git2cpp/repo.h" 16 | #include "git2cpp/initializer.h" 17 | #include "git2cpp/annotated_commit.h" 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /* Define the printf format specifer to use for size_t output */ 27 | #if defined(_MSC_VER) || defined(__MINGW32__) 28 | # define PRIuZ "Iu" 29 | # define PRIxZ "Ix" 30 | # define PRIdZ "Id" 31 | #else 32 | # define PRIuZ "zu" 33 | # define PRIxZ "zx" 34 | # define PRIdZ "zd" 35 | #endif 36 | 37 | /** 38 | * The following example demonstrates how to do checkouts with libgit2. 39 | * 40 | * Recognized options are : 41 | * --force: force the checkout to happen. 42 | * --[no-]progress: show checkout progress, on by default. 43 | * --perf: show performance data. 44 | */ 45 | 46 | namespace { 47 | 48 | struct args_info { 49 | int argc; 50 | char **argv; 51 | int pos; 52 | }; 53 | 54 | typedef struct { 55 | int force : 1; 56 | int progress : 1; 57 | int perf : 1; 58 | } checkout_options; 59 | 60 | [[noreturn]] void print_usage() 61 | { 62 | fprintf(stderr, "usage: checkout [options] \n" 63 | "Options are :\n" 64 | " --git-dir: use the following git repository.\n" 65 | " --force: force the checkout.\n" 66 | " --[no-]progress: show checkout progress.\n" 67 | " --perf: show performance data.\n"); 68 | exit(1); 69 | } 70 | 71 | bool match_bool_arg(int *out, args_info *args, const char *opt) 72 | { 73 | const char *found = args->argv[args->pos]; 74 | 75 | if (!strcmp(found, opt)) { 76 | *out = 1; 77 | return true; 78 | } 79 | 80 | if (!strncmp(found, "--no-", strlen("--no-")) && 81 | !strcmp(found + strlen("--no-"), opt + 2)) { 82 | *out = 0; 83 | return true; 84 | } 85 | 86 | *out = -1; 87 | return false; 88 | } 89 | 90 | [[noreturn]] void fatal(const char *message, const char *extra) 91 | { 92 | if (extra) 93 | fprintf(stderr, "%s %s\n", message, extra); 94 | else 95 | fprintf(stderr, "%s\n", message); 96 | 97 | exit(1); 98 | } 99 | 100 | size_t is_prefixed(const char *str, const char *pfx) 101 | { 102 | size_t len = strlen(pfx); 103 | return strncmp(str, pfx, len) ? 0 : len; 104 | } 105 | 106 | bool match_str_arg(const char **out, struct args_info *args, const char *opt) 107 | { 108 | const char *found = args->argv[args->pos]; 109 | size_t len = is_prefixed(found, opt); 110 | 111 | if (!len) 112 | return false; 113 | 114 | if (!found[len]) { 115 | if (args->pos + 1 == args->argc) 116 | fatal("expected value following argument", opt); 117 | args->pos += 1; 118 | *out = args->argv[args->pos]; 119 | return true; 120 | } 121 | 122 | if (found[len] == '=') { 123 | *out = found + len + 1; 124 | return true; 125 | } 126 | 127 | return false; 128 | } 129 | 130 | void parse_options(const char **repo_path, checkout_options *opts, struct args_info *args) 131 | { 132 | if (args->argc <= 1) 133 | print_usage(); 134 | 135 | memset(opts, 0, sizeof(*opts)); 136 | 137 | /* Default values */ 138 | opts->progress = 1; 139 | 140 | for (args->pos = 1; args->pos < args->argc; ++args->pos) { 141 | const char *curr = args->argv[args->pos]; 142 | int bool_arg; 143 | 144 | if (strcmp(curr, "--") == 0) { 145 | break; 146 | } else if (!strcmp(curr, "--force")) { 147 | opts->force = 1; 148 | } else if (match_bool_arg(&bool_arg, args, "--progress")) { 149 | opts->progress = bool_arg; 150 | } else if (match_bool_arg(&bool_arg, args, "--perf")) { 151 | opts->perf = bool_arg; 152 | } else if (match_str_arg(repo_path, args, "--git-dir")) { 153 | continue; 154 | } else { 155 | break; 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * This function is called to report progression, ie. it's called once with 162 | * a NULL path and the number of total steps, then for each subsequent path, 163 | * the current completed_step value. 164 | */ 165 | void print_checkout_progress(const char *path, size_t completed_steps, size_t total_steps, void */*payload*/) 166 | { 167 | if (path == NULL) { 168 | printf("checkout started: %" PRIuZ " steps\n", total_steps); 169 | } else { 170 | printf("checkout: %s %" PRIuZ "/%" PRIuZ "\n", path, completed_steps, total_steps); 171 | } 172 | } 173 | 174 | /** 175 | * This function is called when the checkout completes, and is used to report the 176 | * number of syscalls performed. 177 | */ 178 | void print_perf_data(const git_checkout_perfdata *perfdata, void* /*payload*/) 179 | { 180 | printf("perf: stat: %" PRIuZ " mkdir: %" PRIuZ " chmod: %" PRIuZ "\n", 181 | perfdata->stat_calls, perfdata->mkdir_calls, perfdata->chmod_calls); 182 | } 183 | 184 | /** 185 | * This is the main "checkout " function, responsible for performing 186 | * a branch-based checkout. 187 | */ 188 | void perform_checkout_ref(git::Repository & repo, git::AnnotatedCommit const & target, checkout_options const & opts) 189 | { 190 | git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; 191 | 192 | /** Setup our checkout options from the parsed options */ 193 | checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; 194 | if (opts.force) 195 | checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; 196 | 197 | if (opts.progress) 198 | checkout_opts.progress_cb = print_checkout_progress; 199 | 200 | if (opts.perf) 201 | checkout_opts.perfdata_cb = print_perf_data; 202 | 203 | /** Grab the commit we're interested to move to */ 204 | auto target_commit = repo.commit_lookup(target.commit_id()); 205 | /** 206 | * Perform the checkout so the workdir corresponds to what target_commit 207 | * contains. 208 | * 209 | * Note that it's okay to pass a git_commit here, because it will be 210 | * peeled to a tree. 211 | */ 212 | if (repo.checkout_tree(target_commit, checkout_opts)) 213 | { 214 | fprintf(stderr, "failed to checkout tree: %s\n", git_error_last()->message); 215 | return; 216 | } 217 | 218 | /** 219 | * Now that the checkout has completed, we have to update HEAD. 220 | * 221 | * Depending on the "origin" of target (ie. it's an OID or a branch name), 222 | * we might need to detach HEAD. 223 | */ 224 | int err; 225 | if (auto ref = target.commit_ref()) { 226 | err = repo.set_head(ref); 227 | } else { 228 | err = repo.set_head_detached(target); 229 | } 230 | if (err != 0) { 231 | fprintf(stderr, "failed to update HEAD reference: %s\n", git_error_last()->message); 232 | } 233 | } 234 | 235 | git::AnnotatedCommit resolve_refish(git::Repository const & repo, const char *refish) 236 | { 237 | if (auto ref = repo.dwim(refish)) 238 | return repo.annotated_commit_from_ref(ref); 239 | 240 | auto obj = repo.revparse_single(refish); 241 | return repo.annotated_commit_lookup(obj.single()->id()); 242 | } 243 | 244 | } 245 | 246 | /** That example's entry point */ 247 | int main(int argc, char **argv) 248 | { 249 | args_info args { argc, argv }; 250 | checkout_options opts; 251 | const char *path = "."; 252 | 253 | /** Parse our command line options */ 254 | parse_options(&path, &opts, &args); 255 | 256 | /** Initialize the library */ 257 | auto_git_initializer; 258 | 259 | git::Repository repo(path); 260 | 261 | /** Make sure we're not about to checkout while something else is going on */ 262 | auto const state = repo.state(); 263 | if (state != GIT_REPOSITORY_STATE_NONE) { 264 | fprintf(stderr, "repository is in unexpected state %d\n", state); 265 | return EXIT_FAILURE; 266 | } 267 | 268 | if (args.pos >= args.argc) { 269 | fprintf(stderr, "unhandled\n"); 270 | return EXIT_FAILURE; 271 | } 272 | 273 | if (strcmp("--", args.argv[args.pos])) 274 | { 275 | /** 276 | * Try to checkout the given path 277 | */ 278 | 279 | fprintf(stderr, "unhandled path-based checkout\n"); 280 | return EXIT_FAILURE; 281 | } 282 | 283 | /** 284 | * Try to resolve a "refish" argument to a target libgit2 can use 285 | */ 286 | auto checkout_target = resolve_refish(repo, args.argv[args.pos]); 287 | perform_checkout_ref(repo, checkout_target, opts); 288 | } 289 | -------------------------------------------------------------------------------- /examples/log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "git2cpp/diff.h" 8 | #include "git2cpp/id_to_str.h" 9 | #include "git2cpp/initializer.h" 10 | #include "git2cpp/pathspec.h" 11 | #include "git2cpp/repo.h" 12 | #include "git2cpp/revspec.h" 13 | #include "git2cpp/revwalker.h" 14 | 15 | #include "git2cpp/internal/optional.h" 16 | 17 | static void usage(const char * message, const char * arg) 18 | { 19 | if (message && arg) 20 | fprintf(stderr, "%s: %s\n", message, arg); 21 | else if (message) 22 | fprintf(stderr, "%s\n", message); 23 | fprintf(stderr, "usage: log []\n"); 24 | exit(1); 25 | } 26 | 27 | struct log_state 28 | { 29 | const char * repodir = "."; 30 | git::internal::optional repo; 31 | git::internal::optional walker; 32 | int hide = 0; 33 | git::revwalker::sorting::type sorting = git::revwalker::sorting::time; 34 | }; 35 | 36 | static void set_sorting(struct log_state * s, git::revwalker::sorting::type sort_mode) 37 | { 38 | if (!s->repo) 39 | { 40 | git::internal::emplace(s->repo, s->repodir); 41 | } 42 | 43 | if (!s->walker) 44 | s->walker = s->repo->rev_walker(); 45 | 46 | if (sort_mode == git::revwalker::sorting::reverse) 47 | s->sorting = s->sorting ^ git::revwalker::sorting::reverse; 48 | else 49 | s->sorting = sort_mode | (s->sorting & git::revwalker::sorting::reverse); 50 | 51 | s->walker->sort(s->sorting); 52 | } 53 | 54 | void push_rev(log_state * s, git::Commit const & commit, int hide) 55 | { 56 | hide = s->hide ^ hide; 57 | 58 | if (!s->walker) 59 | { 60 | s->walker = s->repo->rev_walker(); 61 | s->walker->sort(s->sorting); 62 | } 63 | 64 | if (!commit) 65 | s->walker->push_head(); 66 | else if (hide) 67 | s->walker->hide(commit.id()); 68 | else 69 | s->walker->push(commit.id()); 70 | } 71 | 72 | void push_rev(struct log_state * s, git::Object const & obj, int hide) 73 | { 74 | hide = s->hide ^ hide; 75 | 76 | if (!s->walker) 77 | { 78 | s->walker = s->repo->rev_walker(); 79 | s->walker->sort(s->sorting); 80 | } 81 | 82 | if (!obj) 83 | s->walker->push_head(); 84 | else if (hide) 85 | s->walker->hide(obj.id()); 86 | else 87 | s->walker->push(obj.id()); 88 | } 89 | 90 | void add_revision(struct log_state * s, const char * revstr) 91 | { 92 | int hide = 0; 93 | 94 | if (!s->repo) 95 | { 96 | git::internal::emplace(s->repo, s->repodir); 97 | } 98 | 99 | if (!revstr) 100 | { 101 | push_rev(s, git::Commit(), hide); 102 | return; 103 | } 104 | 105 | if (*revstr == '^') 106 | hide = !hide; 107 | 108 | git::Revspec revs = (*revstr == '^') 109 | ? s->repo->revparse_single(revstr + 1) 110 | : s->repo->revparse(revstr); 111 | 112 | if (auto obj = revs.single()) 113 | { 114 | push_rev(s, *obj, hide); 115 | } 116 | else 117 | { 118 | git::Revspec::Range const & range = *revs.range(); 119 | push_rev(s, range.to, hide); 120 | 121 | if ((revs.flags() & GIT_REVSPEC_MERGE_BASE) != 0) 122 | { 123 | git_oid base = s->repo->merge_base(range); 124 | push_rev(s, s->repo->commit_lookup(base), hide); 125 | } 126 | 127 | push_rev(s, range.from, !hide); 128 | } 129 | } 130 | 131 | static void print_time(const git_time * intime, const char * prefix) 132 | { 133 | char sign, out[32]; 134 | 135 | int offset = intime->offset; 136 | if (offset < 0) 137 | { 138 | sign = '-'; 139 | offset = -offset; 140 | } 141 | else 142 | { 143 | sign = '+'; 144 | } 145 | 146 | int hours = offset / 60; 147 | int minutes = offset % 60; 148 | 149 | time_t t = static_cast(intime->time) + (intime->offset * 60); 150 | 151 | auto intm = gmtime(&t); 152 | strftime(out, sizeof(out), "%a %b %e %T %Y", intm); 153 | 154 | printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes); 155 | } 156 | 157 | static void print_commit(git::Commit const & commit) 158 | { 159 | printf("commit %s\n", git::id_to_str(commit.id()).c_str()); 160 | 161 | const auto count = commit.parents_num(); 162 | if (count > 1) 163 | { 164 | printf("Merge:"); 165 | for (size_t i = 0; i < count; ++i) 166 | { 167 | printf(" %s", git::id_to_str(commit.parent_id(i), 7).c_str()); 168 | } 169 | printf("\n"); 170 | } 171 | 172 | if (auto sig = commit.author()) 173 | { 174 | printf("Author: %s <%s>\n", sig->name, sig->email); 175 | print_time(&sig->when, "Date: "); 176 | } 177 | printf("\n"); 178 | 179 | for (const char* scan = commit.message(); scan && *scan;) 180 | { 181 | const char *eol = scan; 182 | for (; *eol && *eol != '\n'; ++eol) /* find eol */ 183 | ; 184 | 185 | printf(" %.*s\n", (int)(eol - scan), scan); 186 | scan = *eol ? eol + 1 : nullptr; 187 | } 188 | printf("\n"); 189 | } 190 | 191 | void print_diff(git_diff_delta const &, git_diff_hunk const &, git_diff_line const & line) 192 | { 193 | fwrite(line.content, 1, line.content_len, stdout); 194 | } 195 | 196 | static int match_int(int * value, const char * arg, int allow_negative) 197 | { 198 | char * found; 199 | *value = (int)strtol(arg, &found, 10); 200 | return (found && *found == '\0' && (allow_negative || *value >= 0)); 201 | } 202 | 203 | static int match_int_arg( 204 | int * value, const char * arg, const char * pfx, int allow_negative) 205 | { 206 | size_t pfxlen = strlen(pfx); 207 | if (strncmp(arg, pfx, pfxlen) != 0) 208 | return 0; 209 | if (!match_int(value, arg + pfxlen, allow_negative)) 210 | usage("Invalid value after argument", arg); 211 | return 1; 212 | } 213 | 214 | static int match_with_parent(git::Commit const & commit, int i, git_diff_options const & opts) 215 | { 216 | auto parent = commit.parent(i); 217 | auto c_tree = commit.tree(); 218 | auto p_tree = parent.tree(); 219 | auto diff = commit.repo().diff(p_tree, c_tree, opts); 220 | 221 | return diff.deltas_num() > 0; 222 | } 223 | 224 | struct log_options 225 | { 226 | int show_diff; 227 | int skip, limit; 228 | int min_parents, max_parents; 229 | git_time_t before; 230 | git_time_t after; 231 | char * author; 232 | char * committer; 233 | }; 234 | 235 | int parse_options(int argc, char * argv[], log_state & s, log_options & opt, int count) 236 | { 237 | int i = 1; 238 | for (; i < argc; ++i) 239 | { 240 | char * a = argv[i]; 241 | 242 | if (a[0] != '-') 243 | { 244 | try 245 | { 246 | add_revision(&s, a); 247 | ++count; 248 | } 249 | catch (...) /* try failed revision parse as filename */ 250 | { 251 | break; 252 | } 253 | } 254 | else if (!strcmp(a, "--")) 255 | { 256 | ++i; 257 | break; 258 | } 259 | else if (!strcmp(a, "--date-order")) 260 | set_sorting(&s, git::revwalker::sorting::time); 261 | else if (!strcmp(a, "--topo-order")) 262 | set_sorting(&s, git::revwalker::sorting::topological); 263 | else if (!strcmp(a, "--reverse")) 264 | set_sorting(&s, git::revwalker::sorting::reverse); 265 | else if (!strncmp(a, "--git-dir=", strlen("--git-dir="))) 266 | s.repodir = a + strlen("--git-dir="); 267 | else if (match_int_arg(&opt.skip, a, "--skip=", 0)) 268 | /* found valid --skip */; 269 | else if (match_int_arg(&opt.limit, a, "--max-count=", 0)) 270 | /* found valid --max-count */; 271 | else if (a[1] >= '0' && a[1] <= '9') 272 | { 273 | if (!match_int(&opt.limit, a + 1, 0)) 274 | usage("Invalid limit on number of commits", a); 275 | } 276 | else if (!strcmp(a, "-n")) 277 | { 278 | if (i + 1 == argc || !match_int(&opt.limit, argv[i + 1], 0)) 279 | usage("Argument -n not followed by valid count", argv[i + 1]); 280 | else 281 | ++i; 282 | } 283 | else if (!strcmp(a, "--merges")) 284 | opt.min_parents = 2; 285 | else if (!strcmp(a, "--no-merges")) 286 | opt.max_parents = 1; 287 | else if (!strcmp(a, "--no-min-parents")) 288 | opt.min_parents = 0; 289 | else if (!strcmp(a, "--no-max-parents")) 290 | opt.max_parents = -1; 291 | else if (match_int_arg(&opt.max_parents, a, "--max-parents=", 1)) 292 | /* found valid --max-parents */; 293 | else if (match_int_arg(&opt.min_parents, a, "--min-parents=", 0)) 294 | /* found valid --min_parents */; 295 | else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch")) 296 | opt.show_diff = 1; 297 | else 298 | usage("Unsupported argument", a); 299 | } 300 | 301 | return i; 302 | } 303 | 304 | int main(int argc, char * argv[]) 305 | { 306 | log_options opt; 307 | memset(&opt, 0, sizeof(opt)); 308 | opt.max_parents = -1; 309 | opt.limit = -1; 310 | 311 | git::Initializer threads_initializer; 312 | 313 | log_state s; 314 | 315 | int count = 0; 316 | int parsed_options_num = parse_options(argc, argv, s, opt, count); 317 | 318 | if (!count) 319 | add_revision(&s, NULL); 320 | 321 | git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; 322 | diffopts.pathspec.strings = &argv[parsed_options_num]; 323 | diffopts.pathspec.count = argc - parsed_options_num; 324 | git::Pathspec ps(diffopts.pathspec); 325 | 326 | count = 0; 327 | int printed = 0; 328 | 329 | while (git::Commit commit = s.walker->next()) 330 | { 331 | int parents = commit.parents_num(); 332 | if (parents < opt.min_parents) 333 | continue; 334 | if (opt.max_parents > 0 && parents > opt.max_parents) 335 | continue; 336 | 337 | if (diffopts.pathspec.count > 0) 338 | { 339 | int unmatched = parents; 340 | 341 | if (parents == 0) 342 | { 343 | if (commit.tree().pathspec_match(GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0) 344 | unmatched = 1; 345 | } 346 | else if (parents == 1) 347 | { 348 | unmatched = match_with_parent(commit, 0, diffopts) ? 0 : 1; 349 | } 350 | else 351 | { 352 | for (int i = 0; i < parents; ++i) 353 | { 354 | if (match_with_parent(commit, i, diffopts)) 355 | unmatched--; 356 | } 357 | } 358 | 359 | if (unmatched > 0) 360 | continue; 361 | } 362 | 363 | if (count++ < opt.skip) 364 | continue; 365 | if (opt.limit != -1 && printed++ >= opt.limit) 366 | { 367 | break; 368 | } 369 | 370 | print_commit(commit); 371 | 372 | if (opt.show_diff) 373 | { 374 | if (parents > 1) 375 | continue; 376 | git::Tree b = commit.tree(); 377 | git::Tree a; 378 | if (parents == 1) 379 | { 380 | a = commit.parent(0).tree(); 381 | } 382 | 383 | s.repo->diff(a, b, diffopts).print(git::diff::format::patch, print_diff); 384 | } 385 | } 386 | 387 | return 0; 388 | } 389 | -------------------------------------------------------------------------------- /examples/diff.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "git2cpp/diff.h" 9 | #include "git2cpp/initializer.h" 10 | #include "git2cpp/repo.h" 11 | 12 | using namespace git; 13 | 14 | namespace { 15 | 16 | Tree resolve_to_tree(Repository const & repo, const char * identifier) 17 | { 18 | Object obj = revparse_single(repo, identifier); 19 | 20 | switch (obj.type()) 21 | { 22 | case GIT_OBJ_TREE: 23 | return obj.to_tree(); 24 | case GIT_OBJ_COMMIT: 25 | return obj.to_commit().tree(); 26 | } 27 | 28 | std::ostringstream ss; 29 | ss 30 | << "object corresponding to identifier " 31 | << identifier 32 | << " is neither commit or tree"; 33 | 34 | throw std::logic_error(ss.str()); 35 | } 36 | 37 | const char * colors[] = { 38 | "\033[m", /* reset */ 39 | "\033[1m", /* bold */ 40 | "\033[31m", /* red */ 41 | "\033[32m", /* green */ 42 | "\033[36m" /* cyan */ 43 | }; 44 | 45 | [[noreturn]] void usage(const char * message, const char * arg) 46 | { 47 | if (message && arg) 48 | fprintf(stderr, "%s: %s\n", message, arg); 49 | else if (message) 50 | fprintf(stderr, "%s\n", message); 51 | fprintf(stderr, "usage: diff [ []]\n"); 52 | exit(1); 53 | } 54 | 55 | namespace output 56 | { 57 | typedef tagged_mask_t type; 58 | 59 | const type diff(1 << 0); 60 | const type stat(1 << 1); 61 | const type shortstat(1 << 2); 62 | const type numstat(1 << 3); 63 | const type summary(1 << 4); 64 | } 65 | 66 | enum class cache_t 67 | { 68 | normal, 69 | only, 70 | none 71 | }; 72 | 73 | /** The 'opts' struct captures all the various parsed command line options. */ 74 | struct opts_t 75 | { 76 | git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; 77 | git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; 78 | int color = -1; 79 | cache_t cache = cache_t::normal; 80 | output::type output; 81 | diff::format format = diff::format::patch; 82 | const char * treeish1 = nullptr; 83 | const char * treeish2 = nullptr; 84 | const char * dir = "."; 85 | 86 | opts_t(int argc, char * argv[]); 87 | }; 88 | 89 | size_t is_prefixed(const char * str, const char * pfx) 90 | { 91 | size_t len = strlen(pfx); 92 | return strncmp(str, pfx, len) ? 0 : len; 93 | } 94 | 95 | void fatal(const char * message, const char * extra) 96 | { 97 | if (extra) 98 | fprintf(stderr, "%s %s\n", message, extra); 99 | else 100 | fprintf(stderr, "%s\n", message); 101 | 102 | exit(1); 103 | } 104 | 105 | struct args_info 106 | { 107 | private: 108 | int argc; 109 | char ** argv; 110 | 111 | public: 112 | int pos; 113 | 114 | args_info(int argc, char ** argv) 115 | : argc(argc) 116 | , argv(argv) 117 | , pos(0) 118 | { 119 | } 120 | 121 | int match_str(const char ** out, const char * opt); 122 | int match_uint16(uint16_t * out, const char * opt); 123 | 124 | private: 125 | const char * match_numeric(const char * opt); 126 | }; 127 | 128 | int args_info::match_str(const char ** out, const char * opt) 129 | { 130 | const char * found = argv[pos]; 131 | size_t len = is_prefixed(found, opt); 132 | 133 | if (!len) 134 | return 0; 135 | 136 | if (!found[len]) 137 | { 138 | if (pos + 1 == argc) 139 | fatal("expected value following argument", opt); 140 | ++pos; 141 | *out = argv[pos]; 142 | return 1; 143 | } 144 | 145 | if (found[len] == '=') 146 | { 147 | *out = found + len + 1; 148 | return 1; 149 | } 150 | 151 | return 0; 152 | } 153 | 154 | const char * args_info::match_numeric(const char * opt) 155 | { 156 | const char * found = argv[pos]; 157 | size_t len = is_prefixed(found, opt); 158 | 159 | if (!len) 160 | return NULL; 161 | 162 | if (!found[len]) 163 | { 164 | if (pos + 1 == argc) 165 | fatal("expected numeric value following argument", opt); 166 | pos += 1; 167 | found = argv[pos]; 168 | } 169 | else 170 | { 171 | found = found + len; 172 | if (*found == '=') 173 | found++; 174 | } 175 | 176 | return found; 177 | } 178 | 179 | int args_info::match_uint16(uint16_t * out, const char * opt) 180 | { 181 | const char * found = match_numeric(opt); 182 | uint16_t val; 183 | char * endptr = NULL; 184 | 185 | if (!found) 186 | return 0; 187 | 188 | val = (uint16_t)strtoul(found, &endptr, 0); 189 | if (!endptr || *endptr != '\0') 190 | fatal("expected number after argument", opt); 191 | 192 | if (out) 193 | *out = val; 194 | return 1; 195 | } 196 | 197 | int match_uint16_arg(uint32_t * out, args_info & args, const char * opt) 198 | { 199 | uint16_t tmp; 200 | const int res = args.match_uint16(&tmp, opt); 201 | *out = tmp; 202 | return res; 203 | } 204 | 205 | /** Parse arguments as copied from git-diff. */ 206 | opts_t::opts_t(int argc, char * argv[]) 207 | { 208 | args_info args(argc, argv); 209 | 210 | for (args.pos = 1; args.pos < argc; ++args.pos) 211 | { 212 | const char * a = argv[args.pos]; 213 | 214 | if (a[0] != '-') 215 | { 216 | if (treeish1 == NULL) 217 | treeish1 = a; 218 | else if (treeish2 == NULL) 219 | treeish2 = a; 220 | else 221 | usage("Only one or two tree identifiers can be provided", NULL); 222 | } 223 | else if (!strcmp(a, "-p") || !strcmp(a, "-u") || 224 | !strcmp(a, "--patch")) 225 | { 226 | output |= output::diff; 227 | format = diff::format::patch; 228 | } 229 | else if (!strcmp(a, "--cached")) 230 | cache = cache_t::only; 231 | else if (!strcmp(a, "--nocache")) 232 | cache = cache_t::none; 233 | else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name")) 234 | format = diff::format::name_only; 235 | else if (!strcmp(a, "--name-status") || 236 | !strcmp(a, "--format=name-status")) 237 | format = diff::format::name_status; 238 | else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw")) 239 | format = diff::format::raw; 240 | else if (!strcmp(a, "--format=diff-index")) 241 | { 242 | format = diff::format::raw; 243 | diffopts.id_abbrev = 40; 244 | } 245 | else if (!strcmp(a, "--color")) 246 | color = 0; 247 | else if (!strcmp(a, "--no-color")) 248 | color = -1; 249 | else if (!strcmp(a, "-R")) 250 | diffopts.flags |= GIT_DIFF_REVERSE; 251 | else if (!strcmp(a, "-a") || !strcmp(a, "--text")) 252 | diffopts.flags |= GIT_DIFF_FORCE_TEXT; 253 | else if (!strcmp(a, "--ignore-space-at-eol")) 254 | diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL; 255 | else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change")) 256 | diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE; 257 | else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space")) 258 | diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE; 259 | else if (!strcmp(a, "--ignored")) 260 | diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED; 261 | else if (!strcmp(a, "--untracked")) 262 | diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; 263 | else if (!strcmp(a, "--patience")) 264 | diffopts.flags |= GIT_DIFF_PATIENCE; 265 | else if (!strcmp(a, "--minimal")) 266 | diffopts.flags |= GIT_DIFF_MINIMAL; 267 | else if (!strcmp(a, "--stat")) 268 | output |= output::stat; 269 | else if (!strcmp(a, "--numstat")) 270 | output |= output::numstat; 271 | else if (!strcmp(a, "--shortstat")) 272 | output |= output::shortstat; 273 | else if (!strcmp(a, "--summary")) 274 | output |= output::summary; 275 | else if (args.match_uint16( 276 | &findopts.rename_threshold, "-M") || 277 | args.match_uint16( 278 | &findopts.rename_threshold, "--find-renames")) 279 | findopts.flags |= GIT_DIFF_FIND_RENAMES; 280 | else if (args.match_uint16( 281 | &findopts.copy_threshold, "-C") || 282 | args.match_uint16( 283 | &findopts.copy_threshold, "--find-copies")) 284 | findopts.flags |= GIT_DIFF_FIND_COPIES; 285 | else if (!strcmp(a, "--find-copies-harder")) 286 | findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; 287 | else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites")) 288 | /* TODO: parse thresholds */ 289 | findopts.flags |= GIT_DIFF_FIND_REWRITES; 290 | else if (!match_uint16_arg( 291 | &diffopts.context_lines, args, "-U") && 292 | !match_uint16_arg( 293 | &diffopts.context_lines, args, "--unified") && 294 | !match_uint16_arg( 295 | &diffopts.interhunk_lines, args, "--inter-hunk-context") && 296 | !args.match_uint16( 297 | &diffopts.id_abbrev, "--abbrev") && 298 | !args.match_str(&diffopts.old_prefix, "--src-prefix") && 299 | !args.match_str(&diffopts.new_prefix, "--dst-prefix") && 300 | !args.match_str(&dir, "--git-dir")) 301 | usage("Unknown command line argument", a); 302 | } 303 | } 304 | 305 | Diff find_diff(Repository const & repo, Tree & t1, Tree & t2, cache_t cache, git_diff_options const & opts) 306 | { 307 | if (t1 && t2) 308 | { 309 | return repo.diff(t1, t2, opts); 310 | } 311 | else if (cache == cache_t::normal) 312 | { 313 | if (t1) 314 | return repo.diff_to_workdir_with_index(t1, opts); 315 | else 316 | return repo.diff_index_to_workdir(opts); 317 | } 318 | else 319 | { 320 | if (!t1) 321 | t1 = resolve_to_tree(repo, "HEAD"); 322 | 323 | if (cache == cache_t::none) 324 | return repo.diff_to_workdir(t1, opts); 325 | else 326 | return repo.diff_to_index(t1, opts); 327 | } 328 | } 329 | 330 | int color_printer(git_diff_delta const &, git_diff_hunk const &, git_diff_line const & l, int & last_color) 331 | { 332 | int color = 0; 333 | 334 | if (last_color >= 0) 335 | { 336 | switch (l.origin) 337 | { 338 | case GIT_DIFF_LINE_ADDITION: 339 | color = 3; 340 | break; 341 | case GIT_DIFF_LINE_DELETION: 342 | color = 2; 343 | break; 344 | case GIT_DIFF_LINE_ADD_EOFNL: 345 | color = 3; 346 | break; 347 | case GIT_DIFF_LINE_DEL_EOFNL: 348 | color = 2; 349 | break; 350 | case GIT_DIFF_LINE_FILE_HDR: 351 | color = 1; 352 | break; 353 | case GIT_DIFF_LINE_HUNK_HDR: 354 | color = 4; 355 | break; 356 | default: 357 | break; 358 | } 359 | 360 | if (color != last_color) 361 | { 362 | if (last_color == 1 || color == 1) 363 | fputs(colors[0], stdout); 364 | fputs(colors[color], stdout); 365 | last_color = color; 366 | } 367 | } 368 | 369 | if (l.origin == GIT_DIFF_LINE_CONTEXT || 370 | l.origin == GIT_DIFF_LINE_ADDITION || 371 | l.origin == GIT_DIFF_LINE_DELETION) 372 | fputc(l.origin, stdout); 373 | 374 | fwrite(l.content, 1, l.content_len, stdout); 375 | 376 | return 0; 377 | } 378 | 379 | void diff_print_stats(Diff const & diff, output::type output) 380 | { 381 | namespace fmt = diff::stats::format; 382 | fmt::type format = fmt::none; 383 | if (output & output::stat) 384 | format |= fmt::full; 385 | if (output & output::shortstat) 386 | format |= fmt::_short; 387 | if (output & output::numstat) 388 | format |= fmt::number; 389 | if (output & output::summary) 390 | format |= fmt::include_summary; 391 | 392 | auto stats = diff.stats(); 393 | if (Buffer buf = stats.to_buf(format, 80)) 394 | fputs(buf.ptr(), stdout); 395 | } 396 | 397 | } 398 | 399 | int main(int argc, char * argv[]) 400 | { 401 | auto_git_initializer; 402 | opts_t opts(argc, argv); 403 | Repository repo(opts.dir); 404 | Tree t1, t2; 405 | if (opts.treeish1) 406 | t1 = resolve_to_tree(repo, opts.treeish1); 407 | if (opts.treeish2) 408 | t2 = resolve_to_tree(repo, opts.treeish2); 409 | 410 | Diff diff = find_diff(repo, t1, t2, opts.cache, opts.diffopts); 411 | 412 | if (opts.findopts.flags & GIT_DIFF_FIND_ALL) 413 | diff.find_similar(opts.findopts); 414 | 415 | if (!opts.output) 416 | opts.output = output::diff; 417 | 418 | if (opts.output != output::diff) 419 | diff_print_stats(diff, opts.output); 420 | 421 | if (opts.output & output::diff) 422 | { 423 | if (opts.color >= 0) 424 | fputs(colors[0], stdout); 425 | 426 | using namespace std::placeholders; 427 | diff.print(opts.format, std::bind(&color_printer, _1, _2, _3, std::ref(opts.color))); 428 | 429 | if (opts.color >= 0) 430 | fputs(colors[0], stdout); 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /examples/status.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) the libgit2 contributors. All rights reserved. 3 | * 4 | * This file is part of libgit2, distributed under the GNU GPL v2 with 5 | * a Linking Exception. For full terms see the included COPYING file. 6 | */ 7 | #include 8 | 9 | #include 10 | 11 | #include "git2cpp/initializer.h" 12 | #include "git2cpp/repo.h" 13 | 14 | #ifdef USE_BOOST 15 | #include 16 | #include 17 | 18 | template 19 | using Optional = boost::optional; 20 | 21 | using StringView = boost::string_view; 22 | #else 23 | #include 24 | #include 25 | 26 | template 27 | using Optional = std::optional; 28 | 29 | using StringView = std::string_view; 30 | #endif 31 | 32 | using namespace git; 33 | 34 | namespace { 35 | 36 | enum class Format 37 | { 38 | DEFAULT, 39 | LONG, 40 | SHORT, 41 | PORCELAIN, 42 | }; 43 | 44 | /* 45 | * This example demonstrates the use of the libgit2 status APIs, 46 | * particularly the `git_status_list` object, to roughly simulate the 47 | * output of running `git status`. It serves as a simple example of 48 | * using those APIs to get basic status information. 49 | * 50 | * This does not have: 51 | * - Robust error handling 52 | * - Colorized or paginated output formatting 53 | * 54 | * This does have: 55 | * - Examples of translating command line arguments to the status 56 | * options settings to mimic `git status` results. 57 | * - A sample status formatter that matches the default "long" format 58 | * from `git status` 59 | * - A sample status formatter that matches the "short" format 60 | */ 61 | 62 | bool starts_with(StringView str, StringView prefix) 63 | { 64 | return str.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), str.begin()); 65 | } 66 | 67 | void show_branch(Repository const & repo, Format format) 68 | { 69 | Optional branch; 70 | try 71 | { 72 | branch = repo.head().name(); 73 | static const StringView refs_heads = "refs/heads/"; 74 | if (starts_with(*branch, refs_heads)) 75 | branch->remove_prefix(refs_heads.length()); 76 | } 77 | catch (non_existing_branch_error const &) 78 | { 79 | } 80 | catch (missing_head_error const &) 81 | { 82 | } 83 | catch (std::exception const & e) 84 | { 85 | throw e; 86 | } 87 | 88 | if (format == Format::LONG) 89 | std::cout << "# On branch " << branch.value_or("Not currently on any branch."); 90 | else 91 | std::cout << "## " << branch.value_or("HEAD (no branch)"); 92 | std::cout << "\n"; 93 | } 94 | 95 | void print_long(Status const & status) 96 | { 97 | size_t maxi = status.entrycount(); 98 | int rm_in_workdir = 0; 99 | const char *old_path, *new_path; 100 | 101 | /* print index changes */ 102 | 103 | bool changes_in_index = false; 104 | 105 | for (size_t i = 0; i < maxi; ++i) 106 | { 107 | auto const & s = status[i]; 108 | 109 | if (s.status == GIT_STATUS_CURRENT) 110 | continue; 111 | 112 | if (s.status & GIT_STATUS_WT_DELETED) 113 | rm_in_workdir = 1; 114 | 115 | const char * istatus = nullptr; 116 | 117 | if (s.status & GIT_STATUS_INDEX_NEW) 118 | istatus = "new file: "; 119 | if (s.status & GIT_STATUS_INDEX_MODIFIED) 120 | istatus = "modified: "; 121 | if (s.status & GIT_STATUS_INDEX_DELETED) 122 | istatus = "deleted: "; 123 | if (s.status & GIT_STATUS_INDEX_RENAMED) 124 | istatus = "renamed: "; 125 | if (s.status & GIT_STATUS_INDEX_TYPECHANGE) 126 | istatus = "typechange:"; 127 | 128 | if (!istatus) 129 | continue; 130 | 131 | if (!changes_in_index) 132 | { 133 | std::cout << "# Changes to be committed:\n" 134 | << "# (use \"git reset HEAD ...\" to unstage)\n" 135 | << "#\n"; 136 | changes_in_index = true; 137 | } 138 | 139 | std::cout << "#\t" << istatus << " "; 140 | 141 | old_path = s.head_to_index->old_file.path; 142 | new_path = s.head_to_index->new_file.path; 143 | 144 | if (old_path && new_path && strcmp(old_path, new_path)) 145 | std::cout << old_path << " -> " << new_path; 146 | else if (old_path) 147 | std::cout << old_path; 148 | else 149 | std::cout << new_path; 150 | 151 | std::cout << "\n"; 152 | } 153 | 154 | if (changes_in_index) 155 | std::cout << "#\n"; 156 | 157 | bool changed_in_workdir = false; 158 | 159 | /* print workdir changes to tracked files */ 160 | 161 | for (size_t i = 0; i < maxi; ++i) 162 | { 163 | auto const & s = status[i]; 164 | 165 | if (s.status == GIT_STATUS_CURRENT || !s.index_to_workdir) 166 | continue; 167 | 168 | const char * wstatus = nullptr; 169 | 170 | if (s.status & GIT_STATUS_WT_MODIFIED) 171 | wstatus = "modified: "; 172 | if (s.status & GIT_STATUS_WT_DELETED) 173 | wstatus = "deleted: "; 174 | if (s.status & GIT_STATUS_WT_RENAMED) 175 | wstatus = "renamed: "; 176 | if (s.status & GIT_STATUS_WT_TYPECHANGE) 177 | wstatus = "typechange:"; 178 | 179 | if (!wstatus) 180 | continue; 181 | 182 | if (!changed_in_workdir) 183 | { 184 | std::cout << "# Changes not staged for commit:\n" 185 | << "# (use \"git add" << (rm_in_workdir ? "/rm" : "") << "...\" to update what will be committed)\n" 186 | << "# (use \"git checkout -- ...\" to discard changes in working directory)\n" 187 | << "#\n"; 188 | changed_in_workdir = true; 189 | } 190 | 191 | std::cout << "#\t" << wstatus << " "; 192 | 193 | old_path = s.index_to_workdir->old_file.path; 194 | new_path = s.index_to_workdir->new_file.path; 195 | 196 | if (old_path && new_path && strcmp(old_path, new_path)) 197 | std::cout << old_path << " -> " << new_path; 198 | else if (old_path) 199 | std::cout << old_path; 200 | else 201 | std::cout << new_path; 202 | std::cout << "\n"; 203 | } 204 | 205 | if (changed_in_workdir) 206 | std::cout << "#\n"; 207 | 208 | /* print untracked files */ 209 | bool were_untracked_files = false; 210 | for (size_t i = 0; i < maxi; ++i) 211 | { 212 | auto const & s = status[i]; 213 | 214 | if (s.status == GIT_STATUS_WT_NEW) 215 | { 216 | if (!were_untracked_files) 217 | { 218 | std::cout << "# Untracked files:\n" 219 | << "# (use \"git add ...\" to include in what will be committed)\n" 220 | << "#\n"; 221 | were_untracked_files = true; 222 | } 223 | 224 | std::cout << "#\t" << s.index_to_workdir->old_file.path << "\n"; 225 | } 226 | } 227 | 228 | /* print ignored files */ 229 | bool were_ignored_files = false; 230 | 231 | for (size_t i = 0; i < maxi; ++i) 232 | { 233 | auto const & s = status[i]; 234 | 235 | if (s.status == GIT_STATUS_IGNORED) 236 | { 237 | if (!were_ignored_files) 238 | { 239 | std::cout << "# Ignored files:\n" 240 | << "# (use \"git add -f ...\" to include in what will be committed)\n" 241 | << "#\n"; 242 | were_ignored_files = true; 243 | } 244 | 245 | std::cout << "#\t" << s.index_to_workdir->old_file.path << "\n"; 246 | } 247 | } 248 | 249 | if (!changes_in_index && changed_in_workdir) 250 | std::cout << "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"; 251 | } 252 | 253 | void print_short(Repository const & repo, Status const & status) 254 | { 255 | size_t maxi = status.entrycount(); 256 | 257 | for (size_t i = 0; i < maxi; ++i) 258 | { 259 | auto const & s = status[i]; 260 | 261 | if (s.status == GIT_STATUS_CURRENT) 262 | continue; 263 | 264 | const char * a = nullptr; 265 | const char * b = nullptr; 266 | const char * c = nullptr; 267 | char istatus = ' '; 268 | char wstatus = ' '; 269 | const char * extra = ""; 270 | 271 | if (s.status & GIT_STATUS_INDEX_NEW) 272 | istatus = 'A'; 273 | if (s.status & GIT_STATUS_INDEX_MODIFIED) 274 | istatus = 'M'; 275 | if (s.status & GIT_STATUS_INDEX_DELETED) 276 | istatus = 'D'; 277 | if (s.status & GIT_STATUS_INDEX_RENAMED) 278 | istatus = 'R'; 279 | if (s.status & GIT_STATUS_INDEX_TYPECHANGE) 280 | istatus = 'T'; 281 | 282 | if (s.status & GIT_STATUS_WT_NEW) 283 | { 284 | if (istatus == ' ') 285 | istatus = '?'; 286 | wstatus = '?'; 287 | } 288 | if (s.status & GIT_STATUS_WT_MODIFIED) 289 | wstatus = 'M'; 290 | if (s.status & GIT_STATUS_WT_DELETED) 291 | wstatus = 'D'; 292 | if (s.status & GIT_STATUS_WT_RENAMED) 293 | wstatus = 'R'; 294 | if (s.status & GIT_STATUS_WT_TYPECHANGE) 295 | wstatus = 'T'; 296 | 297 | if (s.status & GIT_STATUS_IGNORED) 298 | { 299 | istatus = '!'; 300 | wstatus = '!'; 301 | } 302 | 303 | if (istatus == '?' && wstatus == '?') 304 | continue; 305 | 306 | if (s.index_to_workdir && s.index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT) 307 | { 308 | auto sm = repo.submodule_lookup(s.index_to_workdir->new_file.path); 309 | auto smstatus = sm.get_status(); 310 | 311 | typedef Submodule::status status; 312 | 313 | if (contains(smstatus, status::wd_modified)) 314 | extra = " (new commits)"; 315 | else if (contains(smstatus, status::wd_index_modified)) 316 | extra = " (modified content)"; 317 | else if (contains(smstatus, status::wd_wd_modified)) 318 | extra = " (modified content)"; 319 | else if (contains(smstatus, status::wd_untracked)) 320 | extra = " (untracked content)"; 321 | } 322 | 323 | if (s.head_to_index) 324 | { 325 | a = s.head_to_index->old_file.path; 326 | b = s.head_to_index->new_file.path; 327 | } 328 | if (s.index_to_workdir) 329 | { 330 | if (!a) 331 | a = s.index_to_workdir->old_file.path; 332 | if (!b) 333 | b = s.index_to_workdir->old_file.path; 334 | c = s.index_to_workdir->new_file.path; 335 | } 336 | 337 | std::cout << istatus << wstatus << ' ' << a; 338 | if (istatus == 'R') 339 | std::cout << ' ' << b; 340 | if (wstatus == 'R') 341 | std::cout << ' ' << c; 342 | std::cout << extra << "\n"; 343 | } 344 | 345 | for (size_t i = 0; i < maxi; ++i) 346 | { 347 | auto const & s = status[i]; 348 | 349 | if (s.status == GIT_STATUS_WT_NEW) 350 | std::cout << "?? " << s.index_to_workdir->old_file.path << "\n"; 351 | } 352 | } 353 | 354 | } 355 | 356 | int main(int argc, char * argv[]) 357 | { 358 | auto_git_initializer; 359 | 360 | size_t npaths = 0; 361 | Format format = Format::DEFAULT; 362 | bool showbranch = false; 363 | Status::Options opt; 364 | const char * repodir = "."; 365 | 366 | const size_t MAX_PATHSPEC = 8; 367 | char * pathspec[MAX_PATHSPEC]; 368 | 369 | opt.include_untracked().renames_head_to_index(); 370 | 371 | for (int i = 1; i < argc; ++i) 372 | { 373 | if (argv[i][0] != '-') 374 | { 375 | if (npaths < MAX_PATHSPEC) 376 | { 377 | pathspec[npaths++] = argv[i]; 378 | } 379 | else 380 | { 381 | std::cerr << "Example only supports a limited pathspec\n"; 382 | return 1; 383 | } 384 | } 385 | else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short")) 386 | format = Format::SHORT; 387 | else if (!strcmp(argv[i], "--long")) 388 | format = Format::LONG; 389 | else if (!strcmp(argv[i], "--porcelain")) 390 | format = Format::PORCELAIN; 391 | else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch")) 392 | showbranch = 1; 393 | else if (!strcmp(argv[i], "-z")) 394 | { 395 | if (format == Format::DEFAULT) 396 | format = Format::PORCELAIN; 397 | } 398 | else if (!strcmp(argv[i], "--ignored")) 399 | opt.include_ignored(); 400 | else if (!strcmp(argv[i], "-uno") || 401 | !strcmp(argv[i], "--untracked-files=no")) 402 | opt.exclude_untracked(); 403 | else if (!strcmp(argv[i], "-unormal") || 404 | !strcmp(argv[i], "--untracked-files=normal")) 405 | opt.include_untracked(); 406 | else if (!strcmp(argv[i], "-uall") || 407 | !strcmp(argv[i], "--untracked-files=all")) 408 | opt.include_untracked().recurse_untracked_dirs(); 409 | else if (!strcmp(argv[i], "--ignore-submodules=all")) 410 | opt.exclude_submodules(); 411 | else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir="))) 412 | repodir = argv[i] + strlen("--git-dir="); 413 | else 414 | { 415 | std::cerr << "Unsupported option '" << argv[i] << "'\n"; 416 | return 1; 417 | } 418 | } 419 | 420 | if (format == Format::DEFAULT) 421 | format = Format::LONG; 422 | if (format == Format::LONG) 423 | showbranch = true; 424 | if (npaths > 0) 425 | opt.set_pathspec(pathspec, npaths); 426 | 427 | Repository repo(repodir); 428 | 429 | if (repo.is_bare()) 430 | { 431 | std::cerr << "Cannot report status on bare repository\n"; 432 | return 1; 433 | } 434 | 435 | /* 436 | * Run status on the repository 437 | * 438 | * Because we want to simluate a full "git status" run and want to 439 | * support some command line options, we use `git_status_foreach_ext()` 440 | * instead of just the plain status call. This allows (a) iterating 441 | * over the index and then the workdir and (b) extra flags that control 442 | * which files are included. If you just want simple status (e.g. to 443 | * enumerate files that are modified) then you probably don't need the 444 | * extended API. 445 | */ 446 | Status status = repo.status(opt); 447 | 448 | if (showbranch) 449 | show_branch(repo, format); 450 | 451 | if (format == Format::LONG) 452 | print_long(status); 453 | else 454 | print_short(repo, status); 455 | 456 | return 0; 457 | } 458 | -------------------------------------------------------------------------------- /src/repo.cpp: -------------------------------------------------------------------------------- 1 | #include "git2cpp/repo.h" 2 | 3 | #include "git2/clone.h" 4 | 5 | #include "git2cpp/annotated_commit.h" 6 | #include "git2cpp/error.h" 7 | #include "git2cpp/internal/optional.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | namespace git 24 | { 25 | const Repository::init_tag Repository::init; 26 | 27 | Repository::Repository(git_repository * repo) 28 | : repo_(repo) 29 | { 30 | } 31 | 32 | Repository::Repository(const char * dir) 33 | { 34 | git_repository * repo; 35 | if (git_repository_open_ext(&repo, dir, 0, nullptr)) 36 | throw repository_open_error(dir); 37 | repo_.reset(repo); 38 | } 39 | 40 | Repository::Repository(std::string const & dir) 41 | : Repository(dir.c_str()) 42 | { 43 | } 44 | 45 | Repository::Repository(const char * dir, init_tag) 46 | { 47 | git_repository * repo; 48 | if (git_repository_init(&repo, dir, false) < 0) 49 | throw repository_init_error(dir); 50 | repo_.reset(repo); 51 | } 52 | 53 | Repository::Repository(std::string const & dir, init_tag tag) 54 | : Repository(dir.c_str(), tag) 55 | {} 56 | 57 | Repository::Repository(const char * dir, init_tag, git_repository_init_options opts) 58 | { 59 | git_repository * repo; 60 | if (git_repository_init_ext(&repo, dir, &opts) < 0) 61 | throw repository_init_error(dir); 62 | repo_.reset(repo); 63 | } 64 | 65 | git_fetch_options fetch_options_from_callbacks(Remote::FetchCallbacks &); 66 | 67 | Repository Repository::clone(const char * url, const char* path, git_checkout_options const & checkout_opts, Remote::FetchCallbacks & fetch_callbacks) 68 | { 69 | const git_clone_options clone_opts = { GIT_CLONE_OPTIONS_VERSION, checkout_opts, fetch_options_from_callbacks(fetch_callbacks) }; 70 | git_repository *cloned_repo = nullptr; 71 | if (auto error = git_clone(&cloned_repo, url, path, &clone_opts)) 72 | { 73 | if (auto err = git_error_last()) 74 | throw repository_clone_error{ repository_clone_error::detailed_info{ err->message, err->klass } }; 75 | else 76 | throw repository_clone_error{ error }; 77 | } 78 | return Repository(cloned_repo); 79 | } 80 | 81 | bool Repository::is_bare() const 82 | { 83 | return git_repository_is_bare(repo_.get()) != 0; 84 | } 85 | 86 | Signature Repository::signature() const 87 | { 88 | return Signature(repo_.get()); 89 | } 90 | 91 | Commit Repository::commit_lookup(git_oid const & oid) const 92 | { 93 | git_commit * commit; 94 | if (git_commit_lookup(&commit, repo_.get(), &oid)) 95 | throw commit_lookup_error(oid); 96 | else 97 | return {commit, *this}; 98 | } 99 | 100 | Tree Repository::tree_lookup(git_oid const & oid) const 101 | { 102 | git_tree * tree; 103 | if (git_tree_lookup(&tree, repo_.get(), &oid)) 104 | throw tree_lookup_error(oid); 105 | else 106 | return {tree, *this}; 107 | } 108 | 109 | Tag Repository::tag_lookup(git_oid const & oid) const 110 | { 111 | git_tag * tag; 112 | if (git_tag_lookup(&tag, repo_.get(), &oid)) 113 | throw tag_lookup_error(oid); 114 | else 115 | return Tag(tag); 116 | } 117 | 118 | Blob Repository::blob_lookup(git_oid const & oid) const 119 | { 120 | git_blob * blob; 121 | if (git_blob_lookup(&blob, repo_.get(), &oid)) 122 | throw blob_lookup_error(oid); 123 | else 124 | return Blob(blob); 125 | } 126 | 127 | Revspec Repository::revparse(const char * spec) const 128 | { 129 | git_revspec revspec; 130 | if (git_revparse(&revspec, repo_.get(), spec)) 131 | throw revparse_error(spec); 132 | else 133 | return {revspec, *this}; 134 | } 135 | 136 | Revspec Repository::revparse_single(const char * spec) const 137 | { 138 | git_object * obj; 139 | if (git_revparse_single(&obj, repo_.get(), spec) < 0) 140 | throw revparse_error(spec); 141 | return Revspec(obj, *this); 142 | } 143 | 144 | Index Repository::index() const 145 | { 146 | return Index(repo_.get()); 147 | } 148 | 149 | Odb Repository::odb() const 150 | { 151 | return Odb(repo_.get()); 152 | } 153 | 154 | git_status_t Repository::file_status(const char * filepath) const 155 | { 156 | static_assert(sizeof(git_status_t) == sizeof(unsigned int), "sizeof(git_status_t) != sizeof(unsigned int)"); 157 | git_status_t res; 158 | switch (git_status_file(reinterpret_cast(&res), repo_.get(), filepath)) 159 | { 160 | case GIT_ENOTFOUND: 161 | throw file_not_found_error(filepath); 162 | case GIT_EAMBIGUOUS: 163 | throw ambiguous_path_error(filepath); 164 | case -1: 165 | throw unknown_file_status_error(filepath); 166 | } 167 | 168 | return res; 169 | } 170 | 171 | struct branch_iterator 172 | { 173 | branch_iterator(git_repository * repo, branch_type type) 174 | : type_(convert(type)) 175 | { 176 | git_branch_iterator_new(&base_, repo, type_); 177 | ++(*this); 178 | } 179 | 180 | ~branch_iterator() 181 | { 182 | git_branch_iterator_free(base_); 183 | } 184 | 185 | explicit operator bool() const { return internal::has_value(ref_); } 186 | 187 | void operator++() 188 | { 189 | git_branch_t type; 190 | git_reference * ref; 191 | switch (git_branch_next(&ref, &type, base_)) 192 | { 193 | case GIT_OK: 194 | assert(type & type_); 195 | internal::emplace(ref_, ref); 196 | break; 197 | case GIT_ITEROVER: 198 | ref_ = internal::none; 199 | break; 200 | default: 201 | ref_ = internal::none; 202 | throw std::logic_error("unknown git_branch_next error"); 203 | } 204 | } 205 | 206 | Reference & operator*() 207 | { 208 | return *ref_; 209 | } 210 | 211 | Reference * operator->() 212 | { 213 | return &*ref_; 214 | } 215 | 216 | private: 217 | static git_branch_t convert(branch_type t) 218 | { 219 | switch (t) 220 | { 221 | case branch_type::LOCAL: 222 | return GIT_BRANCH_LOCAL; 223 | case branch_type::REMOTE: 224 | return GIT_BRANCH_REMOTE; 225 | case branch_type::ALL: 226 | return GIT_BRANCH_ALL; 227 | } 228 | throw std::logic_error("invalid branch type"); 229 | } 230 | 231 | private: 232 | git_branch_t type_; 233 | git_branch_iterator * base_; 234 | internal::optional ref_; 235 | }; 236 | 237 | std::vector Repository::branches(branch_type type, git_reference_t ref_kind) const 238 | { 239 | std::vector res; 240 | for (branch_iterator it(repo_.get(), type); it; ++it) 241 | { 242 | if (ref_kind == GIT_REFERENCE_ALL || it->type() == ref_kind) 243 | res.emplace_back(std::move(*it)); 244 | } 245 | return res; 246 | } 247 | 248 | Reference Repository::create_branch(const char * name, Commit const & target, bool force) 249 | { 250 | git_reference * ref; 251 | const auto err = git_branch_create(&ref, repo_.get(), name, target.ptr(), force); 252 | switch (err) 253 | { 254 | case GIT_OK: 255 | return Reference(ref); 256 | case GIT_EEXISTS: 257 | assert(!force); 258 | throw branch_create_error(branch_create_error::already_exists); 259 | case GIT_EINVALIDSPEC: 260 | throw branch_create_error(branch_create_error::invalid_spec); 261 | default: 262 | throw branch_create_error(branch_create_error::unknown); 263 | } 264 | } 265 | 266 | Reference Repository::dwim(const char* shorthand) const 267 | { 268 | git_reference * ref; 269 | if (git_reference_dwim(&ref, repo_.get(), shorthand) == GIT_OK) 270 | return Reference(ref); 271 | else 272 | return Reference(); 273 | } 274 | 275 | AnnotatedCommit Repository::annotated_commit_from_ref(Reference const& ref) const 276 | { 277 | git_annotated_commit * commit; 278 | const auto err = git_annotated_commit_from_ref(&commit, repo_.get(), ref.ptr()); 279 | assert(err == GIT_OK); 280 | return AnnotatedCommit(commit); 281 | } 282 | 283 | AnnotatedCommit Repository::annotated_commit_lookup(git_oid const& id) const 284 | { 285 | git_annotated_commit * commit; 286 | const auto err = git_annotated_commit_lookup(&commit, repo_.get(), &id); 287 | assert(err == GIT_OK); 288 | return AnnotatedCommit(commit); 289 | } 290 | 291 | RevWalker Repository::rev_walker() const 292 | { 293 | git_revwalk * walker; 294 | if (git_revwalk_new(&walker, repo_.get())) 295 | throw revwalk_new_error(); 296 | else 297 | return {walker, *this}; 298 | } 299 | 300 | git_oid Repository::merge_base(git_oid const & a, git_oid const & b) const 301 | { 302 | git_oid res; 303 | if (git_merge_base(&res, repo_.get(), &a, &b)) 304 | throw merge_base_error(a, b); 305 | return res; 306 | } 307 | 308 | git_oid Repository::merge_base(Revspec::Range const & range) const 309 | { 310 | return merge_base(range.from.id(), range.to.id()); 311 | } 312 | 313 | Reference Repository::head() const 314 | { 315 | git_reference * head; 316 | switch (git_repository_head(&head, repo_.get())) 317 | { 318 | case GIT_OK: 319 | return Reference(head); 320 | case GIT_EUNBORNBRANCH: 321 | throw non_existing_branch_error(); 322 | case GIT_ENOTFOUND: 323 | throw missing_head_error(); 324 | default: 325 | throw unknown_get_current_branch_error(); 326 | } 327 | } 328 | 329 | Reference Repository::ref(const char * name) const 330 | { 331 | git_reference * ref; 332 | git_reference_lookup(&ref, repo_.get(), name); 333 | return Reference(ref); 334 | } 335 | 336 | Status Repository::status(Status::Options const & opts) const 337 | { 338 | return Status(repo_.get(), opts); 339 | } 340 | 341 | git_repository_state_t Repository::state() const 342 | { 343 | return static_cast(git_repository_state(repo_.get())); 344 | } 345 | 346 | StrArray Repository::reference_list() const 347 | { 348 | git_strarray str_array; 349 | git_reference_list(&str_array, repo_.get()); 350 | return StrArray(str_array); 351 | } 352 | 353 | Submodule Repository::submodule_lookup(const char * name) const 354 | { 355 | git_submodule * sm; 356 | if (git_submodule_lookup(&sm, repo_.get(), name)) 357 | throw submodule_lookup_error(name); 358 | else 359 | return {sm, repo_.get()}; 360 | } 361 | 362 | const char * Repository::path() const 363 | { 364 | return git_repository_path(repo_.get()); 365 | } 366 | 367 | const char * Repository::workdir() const 368 | { 369 | return git_repository_workdir(repo_.get()); 370 | } 371 | 372 | git_oid Repository::create_commit(const char * update_ref, 373 | Signature const & author, 374 | Signature const & commiter, 375 | const char * message, 376 | Tree const & tree, 377 | const char * message_encoding) 378 | { 379 | git_oid res; 380 | if (git_commit_create_v(&res, repo_.get(), update_ref, 381 | author.ptr(), commiter.ptr(), 382 | message_encoding, message, 383 | tree.ptr(), 0)) 384 | { 385 | throw commit_create_error(); 386 | } 387 | return res; 388 | } 389 | 390 | git_oid Repository::create_commit(const char * update_ref, 391 | Signature const & author, 392 | Signature const & commiter, 393 | const char * message, 394 | Tree const & tree, 395 | Commit const & parent, 396 | const char * message_encoding) 397 | { 398 | git_oid res; 399 | if (git_commit_create_v(&res, repo_.get(), update_ref, 400 | author.ptr(), commiter.ptr(), 401 | message_encoding, message, 402 | tree.ptr(), 1, parent.ptr())) 403 | { 404 | throw commit_create_error(); 405 | } 406 | return res; 407 | } 408 | 409 | git_oid Repository::amend_commit(Commit const & commit_to_amend, const char * update_ref, const char * message, Tree const & tree) 410 | { 411 | git_oid res; 412 | auto op_res = git_commit_amend(&res, commit_to_amend.ptr(), update_ref, nullptr, nullptr, nullptr, message, tree.ptr()); 413 | assert(op_res == GIT_OK); 414 | return res; 415 | } 416 | 417 | namespace 418 | { 419 | Object tree_entry_to_object(git_tree_entry const * entry, Repository const & repo, git_repository * repo_ptr) 420 | { 421 | git_object * obj; 422 | git_tree_entry_to_object(&obj, repo_ptr, entry); 423 | return Object(obj, repo); 424 | } 425 | } 426 | 427 | Object Repository::entry_to_object(Tree::BorrowedEntry entry) const 428 | { 429 | return tree_entry_to_object(entry.ptr(), *this, repo_.get()); 430 | } 431 | 432 | Object Repository::entry_to_object(Tree::OwnedEntry entry) const 433 | { 434 | return tree_entry_to_object(entry.ptr(), *this, repo_.get()); 435 | } 436 | 437 | Object revparse_single(Repository const & repo, const char * spec) 438 | { 439 | return std::move(*repo.revparse_single(spec).single()); 440 | } 441 | 442 | Diff Repository::diff(Tree & a, Tree & b, git_diff_options const & opts) const 443 | { 444 | git_diff * diff; 445 | auto op_res = git_diff_tree_to_tree(&diff, repo_.get(), a.ptr(), b.ptr(), &opts); 446 | assert(op_res == 0); 447 | return Diff(diff); 448 | } 449 | 450 | Diff Repository::diff_to_index(Tree & t, git_diff_options const & opts) const 451 | { 452 | git_diff * diff; 453 | auto op_res = git_diff_tree_to_index(&diff, repo_.get(), t.ptr(), nullptr, &opts); 454 | assert(op_res == 0); 455 | return Diff(diff); 456 | } 457 | 458 | Diff Repository::diff_to_workdir(Tree & t, git_diff_options const & opts) const 459 | { 460 | git_diff * diff; 461 | auto op_res = git_diff_tree_to_workdir(&diff, repo_.get(), t.ptr(), &opts); 462 | assert(op_res == 0); 463 | return Diff(diff); 464 | } 465 | 466 | Diff Repository::diff_to_workdir_with_index(Tree & t, git_diff_options const & opts) const 467 | { 468 | git_diff * diff; 469 | auto op_res = git_diff_tree_to_workdir_with_index(&diff, repo_.get(), t.ptr(), &opts); 470 | assert(op_res == 0); 471 | return Diff(diff); 472 | } 473 | 474 | Diff Repository::diff_index_to_workdir(git_diff_options const & opts) const 475 | { 476 | git_diff * diff; 477 | auto op_res = git_diff_index_to_workdir(&diff, repo_.get(), nullptr, &opts); 478 | assert(op_res == 0); 479 | return Diff(diff); 480 | } 481 | 482 | void Repository::reset_default(Commit const & commit, git_strarray const & pathspecs) 483 | { 484 | auto op_res = git_reset_default(repo_.get(), reinterpret_cast(const_cast(commit.ptr())), const_cast(&pathspecs)); 485 | assert(op_res == GIT_OK); 486 | } 487 | 488 | void Repository::file_diff(std::string const & old_path, git_oid const & old_id, 489 | std::string const & new_path, git_oid const & new_id, 490 | FileDiffHandler & diff_handler) const 491 | { 492 | auto old_file = blob_lookup(old_id); 493 | auto new_file = blob_lookup(new_id); 494 | git_diff_options options = GIT_DIFF_OPTIONS_INIT; 495 | auto data_callback = [](git_diff_delta const *, git_diff_hunk const *, git_diff_line const * line, void * payload) { 496 | auto handler = reinterpret_cast(payload); 497 | handler->line(*line); 498 | return 0; 499 | }; 500 | auto op_res = git_diff_blobs(old_file.ptr(), old_path.c_str(), new_file.ptr(), new_path.c_str(), 501 | &options, nullptr, nullptr, nullptr, data_callback, &diff_handler); 502 | assert(op_res == GIT_OK); 503 | } 504 | 505 | StrArray Repository::remotes() const 506 | { 507 | git_strarray result; 508 | auto op_res = git_remote_list(&result, repo_.get()); 509 | assert(op_res == GIT_OK); 510 | return StrArray(result); 511 | } 512 | 513 | Remote Repository::remote(const char * name) const 514 | { 515 | git_remote * remote; 516 | if (git_remote_lookup(&remote, repo_.get(), name)) 517 | throw remote_lookup_error(name); 518 | else 519 | return Remote(remote); 520 | } 521 | 522 | Remote Repository::create_remote(const char * name, const char * url) 523 | { 524 | git_remote * remote; 525 | if (git_remote_create(&remote, repo_.get(), name, url)) 526 | throw remote_create_error(name, url); 527 | else 528 | return Remote(remote); 529 | } 530 | 531 | void Repository::delete_remote(const char * name) 532 | { 533 | if (git_remote_delete(repo_.get(), name)) 534 | throw remote_delete_error(name); 535 | } 536 | 537 | internal::optional Repository::rename_remote(const char * old_name, const char * new_name) 538 | { 539 | git_strarray problems {}; 540 | if (git_remote_rename(&problems, repo_.get(), old_name, new_name)) 541 | return StrArray(problems); 542 | else 543 | return internal::none; 544 | } 545 | 546 | void Repository::set_url(const char * name, const char * url) 547 | { 548 | if (git_remote_set_url(repo_.get(), name, url)) 549 | throw remote_set_url_error(name, url); 550 | } 551 | 552 | void Repository::set_pushurl(const char * name, const char * url) 553 | { 554 | if (git_remote_set_pushurl(repo_.get(), name, url)) 555 | throw remote_set_pushurl_error(name, url); 556 | } 557 | 558 | int Repository::checkout_tree(Commit const& commit, git_checkout_options const& options) 559 | { 560 | return git_checkout_tree(repo_.get(), reinterpret_cast(commit.ptr()), &options); 561 | } 562 | 563 | int Repository::checkout_head(git_checkout_options const & options) 564 | { 565 | return git_checkout_head(repo_.get(), &options); 566 | } 567 | 568 | int Repository::set_head(char const* ref) 569 | { 570 | return git_repository_set_head(repo_.get(), ref); 571 | } 572 | 573 | int Repository::set_head_detached(git_oid const & commit) 574 | { 575 | return git_repository_set_head_detached(repo_.get(), &commit); 576 | } 577 | 578 | int Repository::set_head_detached(AnnotatedCommit const& commit) 579 | { 580 | return git_repository_set_head_detached_from_annotated(repo_.get(), commit.ptr()); 581 | } 582 | 583 | Blame Repository::blame_file(const char* path, git_blame_options const& options) 584 | { 585 | git_blame * blame; 586 | const auto err = git_blame_file( 587 | &blame, repo_.get(), path, 588 | /*IMO `options` can be const, git_blame_file doesn't change it*/ const_cast(&options) 589 | ); 590 | if (err != GIT_OK) 591 | throw blame_file_error(path); 592 | return Blame(blame); 593 | } 594 | 595 | internal::optional Repository::discover(const char * start_path) 596 | { 597 | git_buf buf = GIT_BUF_INIT; 598 | if (git_repository_discover(&buf, start_path, 0, nullptr)) 599 | return internal::none; 600 | return std::string(buf.ptr, buf.size); 601 | } 602 | 603 | void Repository::Destroy::operator() (git_repository* repo) const 604 | { 605 | git_repository_free(repo); 606 | } 607 | } 608 | --------------------------------------------------------------------------------