Skip to content

Instantly share code, notes, and snippets.

@TheVice
Last active March 8, 2019 20:43
Show Gist options
  • Save TheVice/950fd83f342c773863315a2b919c19f3 to your computer and use it in GitHub Desktop.
Save TheVice/950fd83f342c773863315a2b919c19f3 to your computer and use it in GitHub Desktop.
git_extractor – tool that browse repository at specific revision and save all files from that state to output path. Sample using: ‘git_extractor <path to repository> <hash of commit> <output path>’.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Bootstrap" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="libgit2">
<libgit2_version Condition="'$(libgit2_version)'=='' And '$(libgit2_url)'==''">0.27.8</libgit2_version>
<libgit2_url Condition="'$(libgit2_url)'=='' And '$(libgit2_version)'=='0.28.1'">https://github.com/libgit2/libgit2/archive/v0.28.1.zip</libgit2_url>
<libgit2_url Condition="'$(libgit2_url)'=='' And '$(libgit2_version)'=='0.28.0'">https://github.com/libgit2/libgit2/archive/v0.28.0.zip</libgit2_url>
<libgit2_url Condition="'$(libgit2_url)'=='' And '$(libgit2_version)'=='0.27.8'">https://github.com/libgit2/libgit2/archive/v0.27.8.zip</libgit2_url>
<libgit2_zip Condition="'$(libgit2_zip)'==''"></libgit2_zip>
</PropertyGroup>
<PropertyGroup Label="cmake">
<cmake_exe Condition="'$(cmake_exe)'=='' And '$(OS)'=='Unix'">/usr/local/bin/cmake</cmake_exe>
<cmake_exe Condition="!Exists('$(cmake_exe)') And '$(OS)'=='Unix'">cmake</cmake_exe>
<cmake_exe Condition="'$(cmake_exe)'=='' And '$(OS)'!='Unix' And '$(VCTargetsPath)'!=''">$([System.IO.Path]::GetFullPath('$(VCTargetsPath)..\..\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe'))</cmake_exe>
<cmake_exe Condition="'$(cmake_exe)'=='' And '$(OS)'!='Unix' And '$(MSBuildExtensionsPath)'!=''">$([System.IO.Path]::GetFullPath('$(MSBuildExtensionsPath)..\..\..\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe'))</cmake_exe>
<cmake_exe Condition="!Exists('$(cmake_exe)') And '$(OS)'!='Unix'">cmake.exe</cmake_exe>
<VS15_x86>Visual Studio 15 2017</VS15_x86>
<VS15_x64>Visual Studio 15 2017 Win64</VS15_x64>
<!--VS16>Visual Studio 16 2019</VS16-->
<Unix_Makefiles>Eclipse CDT4 - Unix Makefiles</Unix_Makefiles>
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And '$(MSBuildRuntimeType)'=='Core' And '$(OS)'!='Unix'">$(VS15_x86)</cmake_generator_name>
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And '$(MSBuildRuntimeType)'!='Core' And !$([System.Environment]::Is64BitOperatingSystem)">$(VS15_x86)</cmake_generator_name>
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And '$(MSBuildRuntimeType)'!='Core' And $([System.Environment]::Is64BitOperatingSystem)">$(VS15_x64)</cmake_generator_name>
<cmake_generator_name Condition="'$(cmake_generator_name)'=='' And '$(OS)'=='Unix'">$(Unix_Makefiles)</cmake_generator_name>
<cmake_output_directory Condition="'$(cmake_output_directory)'==''">$(MSBuildThisFileDirectory)$(cmake_generator_name.Replace(" ", "_"))</cmake_output_directory>
<CONFIG Condition="'$(CONFIG)'==''">Release</CONFIG>
</PropertyGroup>
<Target Name="download_zip_archives" Condition="15.8 &lt; $(MSBuildVersion)">
<DownloadFile
Condition="'$(libgit2_zip)'==''"
SourceUrl="$(libgit2_url)"
DestinationFolder="$(MSBuildThisFileDirectory)">
<Output TaskParameter="DownloadedFile" PropertyName="libgit2_zip" />
</DownloadFile>
</Target>
<Target Name="unzip_archives" Condition="15.8 &lt; $(MSBuildVersion)" DependsOnTargets="download_zip_archives">
<Unzip
Condition="Exists('$(libgit2_zip)')"
ContinueOnError="true"
OverwriteReadOnlyFiles="true"
SourceFiles="$(libgit2_zip)"
DestinationFolder="$(MSBuildThisFileDirectory)" />
</Target>
<Target Name="CMake" Condition="Exists('$(cmake_exe)')" DependsOnTargets="unzip_archives">
<PropertyGroup>
<source_directory>$(MSBuildThisFileDirectory)</source_directory>
<libgit2_folder Condition="'$(libgit2_zip)'!=''">$(MSBuildThisFileDirectory)$([System.IO.Path]::GetFileNameWithoutExtension('$(libgit2_zip)'))</libgit2_folder>
<libgit2_folder Condition="'$(libgit2_zip)'==''">$(MSBuildThisFileDirectory)$([System.IO.Path]::GetFileNameWithoutExtension('$(libgit2_url)'))</libgit2_folder>
</PropertyGroup>
<MakeDir
Condition="Exists('$(libgit2_folder)') And !Exists('$(cmake_output_directory)')"
Directories="$(cmake_output_directory)" />
<Exec
Condition="Exists('$(libgit2_folder)')"
Command="&quot;$(cmake_exe)&quot; -G &quot;$(cmake_generator_name)&quot; -DLIBGIT2_PATH=&quot;$(libgit2_folder)&quot; &quot;$(source_directory)&quot;"
WorkingDirectory="$(cmake_output_directory)" />
<Exec
Condition="Exists('$(libgit2_folder)')"
Command="&quot;$(cmake_exe)&quot; --build &quot;$(cmake_output_directory)&quot; --config $(CONFIG)" />
</Target>
<Target Name="Bootstrap" DependsOnTargets="CMake" />
</Project>
cmake_minimum_required(VERSION 3.8)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "Configuration process cannot start from project source directory.")
endif()
project("git_extractor")
if(DEFINED ENV{LIBGIT2_PATH})
file(TO_CMAKE_PATH $ENV{LIBGIT2_PATH} libgit2_path)
elseif(DEFINED LIBGIT2_PATH)
file(TO_CMAKE_PATH ${LIBGIT2_PATH} libgit2_path)
else()
message(FATAL_ERROR "LIBGIT2_PATH not set. Can be found here - https://github.com/libgit2/libgit2/releases/")
endif()
option(BUILD_CLAR "" OFF)
option(USE_SSH "" OFF)
option(USE_HTTPS "" OFF)
option(CURL "" OFF)
option(USE_EXT_HTTP_PARSER "" OFF)
add_subdirectory("${libgit2_path}" ${CMAKE_BINARY_DIR}/libgit2)
add_executable(${PROJECT_NAME}_c "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.c")
add_executable(${PROJECT_NAME}_cpp "${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.cpp")
target_include_directories(${PROJECT_NAME}_c SYSTEM PRIVATE "${libgit2_path}/include")
target_link_libraries(${PROJECT_NAME}_c PRIVATE git2)
target_include_directories(${PROJECT_NAME}_cpp SYSTEM PRIVATE "${libgit2_path}/include")
target_link_libraries(${PROJECT_NAME}_cpp PRIVATE git2)
add_custom_command(TARGET ${PROJECT_NAME}_c POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:git2>" "$<TARGET_FILE_DIR:${PROJECT_NAME}_c>/$<TARGET_FILE_NAME:git2>")
add_custom_command(TARGET ${PROJECT_NAME}_cpp POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:git2>" "$<TARGET_FILE_DIR:${PROJECT_NAME}_cpp>/$<TARGET_FILE_NAME:git2>")
if(NOT MSVC)
set_target_properties(${PROJECT_NAME}_c PROPERTIES C_STANDARD 11)
set_target_properties(${PROJECT_NAME}_cpp PROPERTIES CXX_STANDARD 11)
endif()
if(MSVC)
set(FLAGS "${FLAGS} /W4 /GS")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEFAULT_CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEFAULT_CMAKE_CXX_FLAGS} ${FLAGS}")
if(CMAKE_CL_64)
set(LINK_FLAGS "${LINK_FLAGS} /DynamicBase /NXCompat")
else()
set(LINK_FLAGS "${LINK_FLAGS} /SafeSEH /DynamicBase /NXCompat")
endif()
else()
set(FLAGS "${FLAGS} -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DEFAULT_CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DEFAULT_CMAKE_CXX_FLAGS} ${FLAGS}")
endif()
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 https://github.com/TheVice/
*
*/
#include <git2.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
char write_to_stream(FILE* stream, const char* content, size_t content_size)
{
return (content_size == fwrite(content, 1, content_size, stream)) ? 1 : 0;
}
char write_to_file(const char* file_name, const char* content, size_t content_size)
{
#if defined(__STDC_LIB_EXT1__) || (defined(_MSC_VER) && (_MSC_VER >= 1400))
FILE* stream = NULL;
if (0 != fopen_s(&stream, file_name, "w"))
{
return 0;
}
#else
FILE* stream = fopen(file_name, "w");
#endif
if (NULL != stream)
{
const char result = write_to_stream(stream, content, content_size);
return (result && 0 != fclose(stream)) ? 1 : 0;
}
return 0;
}
#ifdef _WIN32
char is_path_a_directory(const char* path)
{
char is_directory = 0;
WIN32_FIND_DATAA file_data;
memset(&file_data, 0, sizeof(file_data));
HANDLE file_handle = FindFirstFileA(path, &file_data);
if (INVALID_HANDLE_VALUE != file_handle)
{
is_directory = ((0 != (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) ? 1 : 0);
FindClose(file_handle);
memset(&file_data, 0, sizeof(file_data));
file_handle = INVALID_HANDLE_VALUE;
}
return is_directory;
}
char create_a_directory_(const char* path)
{
const BOOL status = CreateDirectoryA(path, NULL);
return 0 != status;
}
#else
char is_path_a_directory(const char* path)
{
char is_directory = 0;
DIR* dir = NULL;
if (NULL != (dir = opendir(path)))
{
closedir(dir);
dir = NULL;
is_directory = 1;
}
return is_directory;
}
char create_a_directory_(const char* path)
{
static const mode_t mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
const int status = mkdir(path, mode);
return ((0 == status) ? 1 : 0);
}
#endif
char create_a_directory(const char* path)
{
if (0 == strlen(path) ||
0 == strcmp("/", path) ||
is_path_a_directory(path) ||
create_a_directory_(path))
{
return 1;
}
const size_t size = strlen(path);
const char* pos = &path[0];
char is_created = 0;
char current_directory[FILENAME_MAX];
while (NULL != (pos = strstr(pos, "/")))
{
const size_t distance = (pos - &path[0]);
assert(distance < FILENAME_MAX);
if (FILENAME_MAX <= distance)
{
is_created = 0;
break;
}
memcpy(current_directory, path, distance);
current_directory[distance] = '\0';
if (distance < size)
{
++pos;
}
if (is_path_a_directory(current_directory))
{
continue;
}
if ((0 == (is_created = create_a_directory_(current_directory))) || (size <= distance))
{
break;
}
}
if (!is_created)
{
return 0;
}
return create_a_directory_(path);
}
char git_error_info(int error)
{
char result = 0;
if (error < 0)
{
const git_error* err = giterr_last();
/**/
fprintf(stderr, "[Error][libgit2] '%i/%i' '%s'.", error, err->klass, err->message);
/**/
++result;
}
return result;
}
size_t get_blob_content(const git_blob* blob, char* content)
{
const git_off_t blob_size = git_blob_rawsize(blob);
if (0 < blob_size)
{
if (NULL != content)
{
memcpy(content, git_blob_rawcontent(blob), (size_t)blob_size);
}
return (size_t)blob_size;
}
return 0;
}
static char* content = NULL;
static size_t content_capacity = 0;
typedef struct
{
char* real_root;
git_repository* repo;
} walk_data;
int walk_cb(const char* root,
const git_tree_entry* entry,
void* payload)
{
(void)root;
walk_data* data = (walk_data*)payload;
const char* entry_name = git_tree_entry_name(entry);
const size_t real_root_size = strlen(data->real_root);
const size_t entry_name_size = strlen(entry_name);
size_t offset = 0;
char* full_name = (char*)malloc(real_root_size + 1 + entry_name_size + 1);
if (NULL == full_name)
{
return -1;
}
if (0 < real_root_size)
{
offset = real_root_size;
memcpy(full_name, data->real_root, real_root_size);
full_name[offset] = '/';
++offset;
}
if (0 < entry_name_size)
{
memcpy(&full_name[offset], entry_name, entry_name_size);
full_name[offset + entry_name_size] = '\0';
}
const git_otype entry_type = git_tree_entry_type(entry);
if (GIT_OBJ_TREE == entry_type)
{
walk_data sub_payload;
sub_payload.real_root = full_name;
sub_payload.repo = data->repo;
/**/
git_tree* sub_tree = NULL;
if (git_error_info(git_tree_lookup(&sub_tree, sub_payload.repo, git_tree_entry_id(entry))))
{
free(full_name);
full_name = NULL;
/**/
return -1;
}
const int result = git_error_info(git_tree_walk(sub_tree, GIT_TREEWALK_PRE, walk_cb, &sub_payload));
git_tree_free(sub_tree);
sub_tree = NULL;
if (1 == result)
{
free(full_name);
full_name = NULL;
/**/
return -1;
}
}
else if (GIT_OBJ_BLOB == entry_type)
{
fprintf(stdout, "%s\n", full_name);
git_object* obj = NULL;
if (git_error_info(git_tree_entry_to_object(&obj, data->repo, entry)))
{
free(full_name);
full_name = NULL;
/**/
return -1;
}
size_t blob_size = get_blob_content((git_blob*)obj, NULL);
if (0 < blob_size)
{
if (NULL == content)
{
content = (char*)malloc(blob_size);
content_capacity = blob_size;
}
else if (content_capacity < blob_size)
{
void* new_content = realloc(content, blob_size);
if (NULL == new_content)
{
free(content);
content = NULL;
content_capacity = 0;
//
git_object_free(obj);
obj = NULL;
//
free(full_name);
full_name = NULL;
/**/
return -1;
}
content = (char*)new_content;
content_capacity = blob_size;
}
blob_size = get_blob_content((git_blob*)obj, content);
}
git_object_free(obj);
obj = NULL;
if (0 < blob_size)
{
if (0 < real_root_size)
{
if (!create_a_directory(data->real_root))
{
fprintf(stderr, "[Warning]: Unable to create directory '%s'.", data->real_root);
}
}
if (!write_to_file(full_name, content, blob_size))
{
fprintf(stderr, "[Warning]: Unable write to file '%s'.", full_name);
}
}
}
free(full_name);
full_name = NULL;
/**/
return 1;
}
char save_files_from_revision(
const char* path_to_repository,
const char* commit_sha,
const char* output_directory)
{
git_oid oid;
if (git_error_info(git_oid_fromstr(&oid, commit_sha)))
{
return 0;
}
git_repository* repo = NULL;
if (git_error_info(git_repository_open(&repo, path_to_repository)))
{
return 0;
}
git_commit* commit = NULL;
if (git_error_info(git_commit_lookup(&commit, repo, &oid)))
{
git_repository_free(repo);
repo = NULL;
/**/
return 0;
}
const char* summary = git_commit_summary(commit);
const git_time_t time = git_commit_time(commit);
#if defined(__STDC_LIB_EXT1__) || (defined(_MSC_VER) && (_MSC_VER >= 1400))
#define str_time_size 32
char str_time[str_time_size];
if (0 != ctime_s(str_time, str_time_size, &time))
{
str_time[0] = '\0';
}
#else
char* str_time = ctime(&time);
#endif
for (size_t i = 0, count = strlen(str_time); i < count; ++i)
{
if ('\n' == str_time[i])
{
str_time[i] = '\0';
break;
}
}
const git_signature* committer = git_commit_committer(commit);
const git_signature* author = git_commit_author(commit);
/**/
fprintf(stdout, "Commit : '%s'\n", commit_sha);
fprintf(stdout, "Author : '%s'\n", author->name);
fprintf(stdout, "Committer : '%s'\n", committer->name);
fprintf(stdout, "Date : '%s'\n", str_time);
fprintf(stdout, "Summary : '%s'\n", summary);
/**/
git_tree* tree = NULL;
if (git_error_info(git_commit_tree(&tree, commit)))
{
git_commit_free(commit);
commit = NULL;
/**/
git_repository_free(repo);
repo = NULL;
/**/
return 0;
}
walk_data payload;
const size_t output_directory_size = strlen(output_directory);
const size_t commit_sha_size = strlen(commit_sha);
payload.real_root = (char*)malloc(output_directory_size + 1 + commit_sha_size + 1);
if (NULL == payload.real_root)
{
git_commit_free(commit);
commit = NULL;
/**/
git_repository_free(repo);
repo = NULL;
/**/
return 0;
}
size_t offset = 0;
if (0 < output_directory_size)
{
offset = output_directory_size;
memcpy(payload.real_root, output_directory, output_directory_size);
payload.real_root[offset] = '/';
++offset;
}
if (0 < commit_sha_size)
{
memcpy(&payload.real_root[offset], commit_sha, commit_sha_size);
payload.real_root[offset + commit_sha_size] = '\0';
}
payload.repo = repo;
/**/
const char result = git_error_info(git_tree_walk(tree, GIT_TREEWALK_PRE, walk_cb, &payload));
if (NULL != content)
{
free(content);
content = NULL;
content_capacity = 0;
}
free(payload.real_root);
payload.real_root = NULL;
/**/
git_tree_free(tree);
tree = NULL;
/**/
git_commit_free(commit);
commit = NULL;
/**/
git_repository_free(repo);
repo = NULL;
/**/
return (1 == result) ? 0 : 1;
}
int main(int argc, char** argv)
{
if (argc != 4 || git_error_info(git_libgit2_init()))
{
return EXIT_FAILURE;
}
const char result = save_files_from_revision(argv[1], argv[2], argv[3]);
if (git_error_info(git_libgit2_shutdown()) || 0 == result)
{
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 https://github.com/TheVice/
*
*/
#include <git2.h>
#include <ctime>
#include <string>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
#else
#include <ftw.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
bool write_to_stream(std::ostream& stream, const std::string& content)
{
return !std::copy(content.cbegin(), content.cend(),
std::ostreambuf_iterator<char>(stream)).failed();
}
bool write_to_file(const std::string& file_name, const std::string& content)
{
std::ofstream file_stream(file_name);
if (file_stream.is_open())
{
return write_to_stream(file_stream, content);
}
return false;
}
#ifdef _WIN32
bool is_path_a_directory(const std::string& path)
{
auto is_directory = false;
WIN32_FIND_DATAA file_data;
std::memset(&file_data, 0, sizeof(file_data));
auto file_handle = FindFirstFileA(path.c_str(), &file_data);
if (INVALID_HANDLE_VALUE != file_handle)
{
is_directory = (0 != (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
FindClose(file_handle);
std::memset(&file_data, 0, sizeof(file_data));
file_handle = INVALID_HANDLE_VALUE;
}
return is_directory;
}
bool create_a_directory_(const std::string& path)
{
const auto status = CreateDirectoryA(path.c_str(), nullptr);
return 0 != status;
}
#else
bool is_path_a_directory(const std::string& path)
{
auto is_directory = false;
DIR* dir = nullptr;
if (nullptr != (dir = opendir(path.c_str())))
{
closedir(dir);
dir = nullptr;
is_directory = true;
}
return is_directory;
}
bool create_a_directory_(const std::string& path)
{
static const auto mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
const auto status = mkdir(path.c_str(), mode);
return 0 == status;
}
#endif // _WIN32
bool create_a_directory(const std::string& path)
{
if (path.empty() ||
"/" == path ||
is_path_a_directory(path) ||
create_a_directory_(path))
{
return true;
}
auto pos = path.cbegin();
auto is_created = false;
std::string current_directory(FILENAME_MAX, '\0');
while (path.cend() != (pos = std::find_if(pos, path.cend(), [](const char& ch)
{
return '/' == ch;
})))
{
current_directory.clear();
//
std::copy(
path.cbegin(),
pos,
std::back_inserter(current_directory));
//
std::advance(pos, 1);
if (is_path_a_directory(current_directory))
{
continue;
}
if (false == (is_created = create_a_directory_(current_directory)))
{
break;
}
}
if (!is_created)
{
return false;
}
return create_a_directory_(path);
}
bool git_error_info(int error)
{
if (error < 0)
{
const auto err = giterr_last();
//
std::cerr << "[Error][libgit2]: '";
std::cerr << std::to_string(error) + "/" + std::to_string(err->klass);
std::cerr << "' '" << err->message << "'.";
//
return true;
}
return false;
}
std::size_t get_blob_content(const git_blob* blob, std::string& content)
{
const auto blob_size = git_blob_rawsize(blob);
if (0 < blob_size)
{
content.resize(static_cast<std::size_t>(blob_size));
std::memcpy(&content.front(), git_blob_rawcontent(blob), content.size());
}
else
{
content.clear();
}
return content.size();
}
struct walk_data
{
static std::string content;
const std::string real_root;
git_repository* const repo;
};
std::string walk_data::content;
int walk_cb(const char* root,
const git_tree_entry* entry,
void* payload)
{
(void)root;
const auto entry_name = git_tree_entry_name(entry);
auto data = (walk_data*)payload;
const std::string full_name(
(data->real_root.empty() ? "" : data->real_root + "/") + entry_name);
const auto entry_type = git_tree_entry_type(entry);
if (GIT_OBJ_TREE == entry_type)
{
walk_data sub_payload({ full_name, data->repo });
//
git_tree* sub_tree = nullptr;
if (git_error_info(git_tree_lookup(&sub_tree, sub_payload.repo, git_tree_entry_id(entry))))
{
return -1;
}
const auto result = git_error_info(git_tree_walk(sub_tree, GIT_TREEWALK_PRE, walk_cb, &sub_payload));
git_tree_free(sub_tree);
sub_tree = nullptr;
if (result)
{
return -1;
}
}
else if (GIT_OBJ_BLOB == entry_type)
{
git_object* obj = nullptr;
if (git_error_info(git_tree_entry_to_object(&obj, data->repo, entry)))
{
return -1;
}
const auto blob_size = get_blob_content(reinterpret_cast<git_blob*>(obj), walk_data::content);
//
git_object_free(obj);
obj = nullptr;
if (0 < blob_size)
{
if (!data->real_root.empty())
{
if (!create_a_directory(data->real_root))
{
std::cerr << "[Warning]: Unable to create directory '";
std::cerr << data->real_root;
std::cerr << "'." << std::endl;
}
}
if (!write_to_file(full_name, walk_data::content))
{
std::cerr << "[Warning]: Unable write to file '";
std::cerr << full_name;
std::cerr << "'." << std::endl;
}
}
}
return 1;
}
bool save_files_from_revision(
const char* path_to_repository,
const char* commit_sha,
const char* output_directory)
{
git_oid oid;
if (git_error_info(git_oid_fromstr(&oid, commit_sha)))
{
return false;
}
git_repository* repo = nullptr;
if (git_error_info(git_repository_open(&repo, path_to_repository)))
{
return false;
}
git_commit* commit = nullptr;
if (git_error_info(git_commit_lookup(&commit, repo, &oid)))
{
git_repository_free(repo);
repo = nullptr;
//
return false;
}
const auto summary = git_commit_summary(commit);
const auto time = git_commit_time(commit);
#if defined(__STDC_LIB_EXT1__) || (defined(_MSC_VER) && (_MSC_VER >= 1400))
std::string str_time(32, '\0');
if (0 != ctime_s(&str_time.front(), str_time.size(), &time))
{
str_time.clear();
}
#else
auto str_time = std::string(std::ctime(&time));
#endif
if (!str_time.empty())
{
const auto pos = str_time.find('\n');
if (std::string::npos != pos)
{
str_time.resize(pos);
}
}
const auto committer = git_commit_committer(commit);
const auto author = git_commit_author(commit);
//
std::cout << "Commit : '";
std::cout << commit_sha << "'" << std::endl;
std::cout << "Author : '";
std::cout << author->name << "'" << std::endl;
std::cout << "Committer : '";
std::cout << committer->name << "'" << std::endl;
std::cout << "Date : '";
std::cout << str_time << "'" << std::endl;
std::cout << "Summary : '";
std::cout << summary << "'" << std::endl;
//
git_tree* tree = nullptr;
if (git_error_info(git_commit_tree(&tree, commit)))
{
git_commit_free(commit);
commit = nullptr;
//
git_repository_free(repo);
repo = nullptr;
//
return false;
}
walk_data payload({ std::string(output_directory) + "/" + commit_sha, repo });
const auto result = git_error_info(git_tree_walk(tree, GIT_TREEWALK_PRE, walk_cb, &payload));
//
git_tree_free(tree);
tree = nullptr;
//
git_commit_free(commit);
commit = nullptr;
//
git_repository_free(repo);
repo = nullptr;
//
return !result;
}
int main(int argc, char** argv)
{
if (argc != 4 || git_error_info(git_libgit2_init()))
{
return EXIT_FAILURE;
}
const auto result = save_files_from_revision(argv[1], argv[2], argv[3]);
if (git_error_info(git_libgit2_shutdown()) || !result)
{
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 https://github.com/TheVice/
*
*/
using System;
using System.IO;
using LibGit2Sharp;
namespace GitExtractor
{
public class GitExtractor
{
static long get_blob_content(Blob blob, Stream content)
{
var blob_size = blob.Size;
if (0 < blob_size)
{
blob.GetContentStream().CopyTo(content);
}
else
{
blob_size = 0;
}
return blob_size;
}
static void walk(Tree tree, string real_root)
{
foreach (var element in tree)
{
var full_name = string.IsNullOrEmpty(real_root) ? element.Name : Path.Combine(real_root, element.Name);
var element_target = element.Target;
if (TreeEntryTargetType.Tree == element.TargetType)
{
walk(element_target as Tree, full_name);
}
else if (TreeEntryTargetType.Blob == element.TargetType)
{
if (!string.IsNullOrEmpty(real_root))
{
Directory.CreateDirectory(real_root);
}
using (var fs = new FileStream(full_name, FileMode.Create))
{
get_blob_content(element_target as Blob, fs);
}
}
}
}
static bool save_files_from_revision(
string path_to_repository,
string commit_sha,
string output_directory)
{
var oid = new ObjectId(commit_sha);
var repository = new Repository(path_to_repository);
var commit = repository.Lookup(oid);
if (!(commit is Commit))
{
return false;
}
var real_commit = commit as Commit;
Console.WriteLine($"Commit : '{commit_sha}'");
Console.WriteLine($"Author : '{real_commit.Author.Name}'");
Console.WriteLine($"Committer : '{real_commit.Committer.Name}'");
//"Date : '{}'";
Console.WriteLine($"Summary : '{real_commit.MessageShort}'");
walk(real_commit.Tree, Path.Combine(output_directory, commit_sha));
return true;
}
#if !NETSTANDARD2_0
public static void Main(string[] args)
{
if (3 == args.Length)
{
save_files_from_revision(args[0], args[1], args[2]);
}
}
#endif
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LibGit2Sharp" Version="0.25.4" />
</ItemGroup>
</Project>
#
# The MIT License (MIT)
#
# Copyright (c) 2019 https://github.com/TheVice/
#
import io
from os import path, makedirs
from time import ctime
from pygit2 import Oid, Repository, Commit, Blob, Tree
def walk(repo, tree, root):
for treeElement in tree:
full_path = path.join(root, treeElement.name)
element = repo[treeElement.id]
if 'tree' == treeElement.type:
walk(repo, element, full_path)
elif 'blob' == treeElement.type:
if not path.exists(root):
makedirs(root)
file = open(full_path, 'wb')
file.write(element.data)
file.close()
def save_files_from_revision(path_to_repository,
commit_sha,
output_directory):
oid = Oid(hex=commit_sha)
repo = Repository(path=path_to_repository)
commit = repo[oid]
if isinstance(commit, Commit):
print("Commit : '" + commit_sha + "'")
print("Author : '" + commit.author.name + "'")
print("Committer : '" + commit.committer.name + "'")
print("Date : '" + ctime(commit.commit_time) + "'")
print("Summary : '" +
commit.message[:(
commit.message.find('\n') if -1 != commit.message.find('\n') else len(commit.message))] + "'")
walk(repo=repo, tree=commit.tree, root=path.join(output_directory, commit_sha))
if __name__ == '__main__':
# import sys
import argparse
parser = argparse.ArgumentParser(description='Process input arguments.')
parser.add_argument('-path', '-path_to_repository', '--path', '--path_to_repository', required=True)
parser.add_argument('-sha', '-commit_sha', '--sha', '--commit_sha', required=True)
parser.add_argument('-output', '-output_directory', '--output', '--output_directory', required=True)
# args = vars(parser.parse_args(sys.argv))
args = vars(parser.parse_args())
# print(args['path'] + ' ' + args['sha'] + ' ' + args['output'])
save_files_from_revision(args['path'], args['sha'], args['output'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment