├── .travis.yml ├── CMakeLists.txt ├── cmake ├── FindLua.cmake ├── dist.cmake └── lua.cmake ├── dist.info ├── lua ├── git.lua └── git │ ├── objects.lua │ ├── pack.lua │ ├── protocol.lua │ ├── repo.lua │ └── util.lua ├── src ├── bit.c ├── core.c ├── sha.c └── zlib.c ├── test.lua ├── test_create.lua ├── test_fetch-win.lua ├── test_fetch.lua ├── test_fetch_stress.lua └── test_remotes.lua /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # LuaDist Travis-CI Hook 3 | # 4 | 5 | # We assume C build environments 6 | language: C 7 | 8 | # Try using multiple Lua Implementations 9 | env: 10 | - TOOL="" # Use native compiler (GCC usually) 11 | - COMPILER="clang" # Use clang 12 | # - COMPILER="fortran" # Use fortran, make sure to modify the matrix section 13 | - TOOL="i686-w64-mingw32" # 32bit MinGW 14 | - TOOL="x86_64-w64-mingw32" # 64bit MinGW 15 | - TOOL="arm-linux-gnueabihf" # ARM hard-float (hf), linux 16 | 17 | # Crosscompile builds may fail 18 | matrix: 19 | allow_failures: 20 | - env: TOOL="i686-w64-mingw32" 21 | - env: TOOL="x86_64-w64-mingw32" 22 | - env: TOOL="arm-linux-gnueabihf" 23 | 24 | # Install dependencies 25 | install: 26 | - git clone git://github.com/LuaDist/_util.git ~/_util 27 | - ~/_util/travis install 28 | 29 | # Bootstap 30 | before_script: 31 | - ~/_util/travis bootstrap 32 | 33 | # Build the module 34 | script: 35 | - ~/_util/travis build 36 | 37 | # Execute additional tests or commands 38 | #after_script: 39 | # - ~/_util/travis test 40 | 41 | # Only watch the master branch 42 | branches: 43 | only: 44 | - master 45 | 46 | # Notify the LuaDist Dev group if needed 47 | notifications: 48 | recipients: 49 | - luadist-dev@googlegroups.com 50 | email: 51 | on_success: change 52 | on_failure: always 53 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 LuaDist. 2 | # Created by Michal Kottman 3 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 4 | # For details see the COPYRIGHT file distributed with LuaDist. 5 | # Please note that the package source code is licensed under its own license. 6 | 7 | project ( lua-git C ) 8 | cmake_minimum_required ( VERSION 2.8 ) 9 | include ( cmake/dist.cmake ) 10 | include ( lua ) 11 | 12 | find_package ( ZLIB REQUIRED ) 13 | 14 | set ( SRC src/bit.c src/core.c src/sha.c src/zlib.c ) 15 | set ( LIB ${ZLIB_LIBRARIES} ) 16 | 17 | include_directories ( ${ZLIB_INCLUDE_DIR} ) 18 | 19 | # Binary 20 | install_lua_module ( git.core ${SRC} LINK ${LIB} ) 21 | # Lua 22 | install_lua_module ( git lua/git.lua ) 23 | install_lua_module ( git.objects lua/git/objects.lua ) 24 | install_lua_module ( git.pack lua/git/pack.lua ) 25 | install_lua_module ( git.protocol lua/git/protocol.lua ) 26 | install_lua_module ( git.repo lua/git/repo.lua ) 27 | install_lua_module ( git.util lua/git/util.lua ) 28 | -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA_EXECUTABLE, if found 4 | # LUA_FOUND, if false, do not try to link to Lua 5 | # LUA_LIBRARIES 6 | # LUA_INCLUDE_DIR, where to find lua.h 7 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 8 | # 9 | # Note that the expected include convention is 10 | # #include "lua.h" 11 | # and not 12 | # #include 13 | # This is because, the lua location is not standardized and may exist 14 | # in locations other than lua/ 15 | 16 | #============================================================================= 17 | # Copyright 2007-2009 Kitware, Inc. 18 | # Modified to support Lua 5.2 by LuaDist 2012 19 | # 20 | # Distributed under the OSI-approved BSD License (the "License"); 21 | # see accompanying file Copyright.txt for details. 22 | # 23 | # This software is distributed WITHOUT ANY WARRANTY; without even the 24 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25 | # See the License for more information. 26 | #============================================================================= 27 | # (To distribute this file outside of CMake, substitute the full 28 | # License text for the above reference.) 29 | # 30 | # The required version of Lua can be specified using the 31 | # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) 32 | # Otherwise the module will search for any available Lua implementation 33 | 34 | # Always search for non-versioned lua first (recommended) 35 | SET(_POSSIBLE_LUA_INCLUDE include include/lua) 36 | SET(_POSSIBLE_LUA_EXECUTABLE lua) 37 | SET(_POSSIBLE_LUA_LIBRARY lua) 38 | 39 | # Determine possible naming suffixes (there is no standard for this) 40 | IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 41 | SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") 42 | ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 43 | SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") 44 | ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 45 | 46 | # Set up possible search names and locations 47 | FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) 48 | LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") 49 | LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") 50 | LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") 51 | ENDFOREACH(_SUFFIX) 52 | 53 | # Find the lua executable 54 | FIND_PROGRAM(LUA_EXECUTABLE 55 | NAMES ${_POSSIBLE_LUA_EXECUTABLE} 56 | ) 57 | 58 | # Find the lua header 59 | FIND_PATH(LUA_INCLUDE_DIR lua.h 60 | HINTS 61 | $ENV{LUA_DIR} 62 | PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} 63 | PATHS 64 | ~/Library/Frameworks 65 | /Library/Frameworks 66 | /usr/local 67 | /usr 68 | /sw # Fink 69 | /opt/local # DarwinPorts 70 | /opt/csw # Blastwave 71 | /opt 72 | ) 73 | 74 | # Find the lua library 75 | FIND_LIBRARY(LUA_LIBRARY 76 | NAMES ${_POSSIBLE_LUA_LIBRARY} 77 | HINTS 78 | $ENV{LUA_DIR} 79 | PATH_SUFFIXES lib64 lib 80 | PATHS 81 | ~/Library/Frameworks 82 | /Library/Frameworks 83 | /usr/local 84 | /usr 85 | /sw 86 | /opt/local 87 | /opt/csw 88 | /opt 89 | ) 90 | 91 | IF(LUA_LIBRARY) 92 | # include the math library for Unix 93 | IF(UNIX AND NOT APPLE) 94 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 95 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 96 | # For Windows and Mac, don't need to explicitly include the math library 97 | ELSE(UNIX AND NOT APPLE) 98 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 99 | ENDIF(UNIX AND NOT APPLE) 100 | ENDIF(LUA_LIBRARY) 101 | 102 | # Determine Lua version 103 | IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 104 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 105 | 106 | STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 107 | UNSET(lua_version_str) 108 | ENDIF() 109 | 110 | # Lua 5.2 111 | IF(NOT LUA_VERSION_STRING) 112 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") 113 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) 114 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) 115 | STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) 116 | SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) 117 | ENDIF() 118 | 119 | INCLUDE(FindPackageHandleStandardArgs) 120 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 121 | # all listed variables are TRUE 122 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua 123 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 124 | VERSION_VAR LUA_VERSION_STRING) 125 | 126 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) 127 | 128 | -------------------------------------------------------------------------------- /cmake/dist.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library. 2 | # Provides sane project defaults and macros common to LuaDist CMake builds. 3 | # 4 | # Copyright (C) 2007-2012 LuaDist. 5 | # by David Manura, Peter Drahoš 6 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 7 | # For details see the COPYRIGHT file distributed with LuaDist. 8 | # Please note that the package source code is licensed under its own license. 9 | 10 | ## Extract information from dist.info 11 | if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) 12 | message ( FATAL_ERROR 13 | "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) 14 | endif () 15 | file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) 16 | if ( "${DIST_INFO}" STREQUAL "" ) 17 | message ( FATAL_ERROR "Failed to load dist.info." ) 18 | endif () 19 | # Reads field `name` from dist.info string `DIST_INFO` into variable `var`. 20 | macro ( _parse_dist_field name var ) 21 | string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 22 | ${var} "${DIST_INFO}" ) 23 | if ( ${var} STREQUAL DIST_INFO ) 24 | message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) 25 | endif () 26 | endmacro () 27 | # 28 | _parse_dist_field ( name DIST_NAME ) 29 | _parse_dist_field ( version DIST_VERSION ) 30 | _parse_dist_field ( license DIST_LICENSE ) 31 | _parse_dist_field ( author DIST_AUTHOR ) 32 | _parse_dist_field ( maintainer DIST_MAINTAINER ) 33 | _parse_dist_field ( url DIST_URL ) 34 | _parse_dist_field ( desc DIST_DESC ) 35 | message ( "DIST_NAME: ${DIST_NAME}") 36 | message ( "DIST_VERSION: ${DIST_VERSION}") 37 | message ( "DIST_LICENSE: ${DIST_LICENSE}") 38 | message ( "DIST_AUTHOR: ${DIST_AUTHOR}") 39 | message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") 40 | message ( "DIST_URL: ${DIST_URL}") 41 | message ( "DIST_DESC: ${DIST_DESC}") 42 | string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 43 | DIST_DEPENDS ${DIST_INFO} ) 44 | if ( DIST_DEPENDS STREQUAL DIST_INFO ) 45 | set ( DIST_DEPENDS "" ) 46 | endif () 47 | message ( "DIST_DEPENDS: ${DIST_DEPENDS}") 48 | ## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add 49 | 50 | 51 | ## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) 52 | # Primary paths 53 | set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) 54 | set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) 55 | set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) 56 | set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) 57 | set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) 58 | 59 | # Secondary paths 60 | option ( INSTALL_VERSION 61 | "Install runtime libraries and executables with version information." OFF) 62 | set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH 63 | "Directory the package can store documentation, tests or other data in.") 64 | set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH 65 | "Recommended directory to install documentation into.") 66 | set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH 67 | "Recommended directory to install examples into.") 68 | set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH 69 | "Recommended directory to install tests into.") 70 | set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH 71 | "Where to install additional files") 72 | 73 | # Tweaks and other defaults 74 | # Setting CMAKE to use loose block and search for find modules in source directory 75 | set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) 76 | set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) 77 | option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) 78 | 79 | # In MSVC, prevent warnings that can occur when using standard libraries. 80 | if ( MSVC ) 81 | add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) 82 | endif () 83 | 84 | # RPath and relative linking 85 | option ( USE_RPATH "Use relative linking." ON) 86 | if ( USE_RPATH ) 87 | string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) 88 | set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) 89 | set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) 90 | set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} 91 | CACHE STRING "" FORCE ) 92 | set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) 93 | set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} 94 | CACHE STRING "" FORCE ) 95 | endif () 96 | 97 | ## MACROS 98 | # Parser macro 99 | macro ( parse_arguments prefix arg_names option_names) 100 | set ( DEFAULT_ARGS ) 101 | foreach ( arg_name ${arg_names} ) 102 | set ( ${prefix}_${arg_name} ) 103 | endforeach () 104 | foreach ( option ${option_names} ) 105 | set ( ${prefix}_${option} FALSE ) 106 | endforeach () 107 | 108 | set ( current_arg_name DEFAULT_ARGS ) 109 | set ( current_arg_list ) 110 | foreach ( arg ${ARGN} ) 111 | set ( larg_names ${arg_names} ) 112 | list ( FIND larg_names "${arg}" is_arg_name ) 113 | if ( is_arg_name GREATER -1 ) 114 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 115 | set ( current_arg_name ${arg} ) 116 | set ( current_arg_list ) 117 | else () 118 | set ( loption_names ${option_names} ) 119 | list ( FIND loption_names "${arg}" is_option ) 120 | if ( is_option GREATER -1 ) 121 | set ( ${prefix}_${arg} TRUE ) 122 | else () 123 | set ( current_arg_list ${current_arg_list} ${arg} ) 124 | endif () 125 | endif () 126 | endforeach () 127 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 128 | endmacro () 129 | 130 | 131 | # install_executable ( executable_targets ) 132 | # Installs any executables generated using "add_executable". 133 | # USE: install_executable ( lua ) 134 | # NOTE: subdirectories are NOT supported 135 | set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) 136 | set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION 137 | "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) 138 | macro ( install_executable ) 139 | foreach ( _file ${ARGN} ) 140 | if ( INSTALL_VERSION ) 141 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 142 | SOVERSION ${DIST_VERSION} ) 143 | endif () 144 | install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} 145 | COMPONENT Runtime ) 146 | endforeach() 147 | endmacro () 148 | 149 | # install_library ( library_targets ) 150 | # Installs any libraries generated using "add_library" into apropriate places. 151 | # USE: install_library ( libexpat ) 152 | # NOTE: subdirectories are NOT supported 153 | set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) 154 | set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION 155 | "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) 156 | macro ( install_library ) 157 | foreach ( _file ${ARGN} ) 158 | if ( INSTALL_VERSION ) 159 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 160 | SOVERSION ${DIST_VERSION} ) 161 | endif () 162 | install ( TARGETS ${_file} 163 | RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime 164 | LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime 165 | ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) 166 | endforeach() 167 | endmacro () 168 | 169 | # helper function for various install_* functions, for PATTERN/REGEX args. 170 | macro ( _complete_install_args ) 171 | if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) 172 | set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) 173 | endif () 174 | if ( NOT("${_ARG_REGEX}" STREQUAL "") ) 175 | set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) 176 | endif () 177 | endmacro () 178 | 179 | # install_header ( files/directories [INTO destination] ) 180 | # Install a directories or files into header destination. 181 | # USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) 182 | # USE: install_header ( mylib.h INTO mylib ) 183 | # For directories, supports optional PATTERN/REGEX arguments like install(). 184 | set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) 185 | set ( CPACK_COMPONENT_HEADER_DESCRIPTION 186 | "Headers needed for development. Installed into ${INSTALL_INC}." ) 187 | macro ( install_header ) 188 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 189 | _complete_install_args() 190 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 191 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 192 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 193 | COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) 194 | else () 195 | install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 196 | COMPONENT Header ) 197 | endif () 198 | endforeach() 199 | endmacro () 200 | 201 | # install_data ( files/directories [INTO destination] ) 202 | # This installs additional data files or directories. 203 | # USE: install_data ( extra data.dat ) 204 | # USE: install_data ( image1.png image2.png INTO images ) 205 | # For directories, supports optional PATTERN/REGEX arguments like install(). 206 | set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) 207 | set ( CPACK_COMPONENT_DATA_DESCRIPTION 208 | "Application data. Installed into ${INSTALL_DATA}." ) 209 | macro ( install_data ) 210 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 211 | _complete_install_args() 212 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 213 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 214 | install ( DIRECTORY ${_file} 215 | DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 216 | COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) 217 | else () 218 | install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 219 | COMPONENT Data ) 220 | endif () 221 | endforeach() 222 | endmacro () 223 | 224 | # INSTALL_DOC ( files/directories [INTO destination] ) 225 | # This installs documentation content 226 | # USE: install_doc ( doc/ doc.pdf ) 227 | # USE: install_doc ( index.html INTO html ) 228 | # For directories, supports optional PATTERN/REGEX arguments like install(). 229 | set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) 230 | set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION 231 | "Application documentation. Installed into ${INSTALL_DOC}." ) 232 | macro ( install_doc ) 233 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 234 | _complete_install_args() 235 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 236 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 237 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 238 | COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) 239 | else () 240 | install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 241 | COMPONENT Documentation ) 242 | endif () 243 | endforeach() 244 | endmacro () 245 | 246 | # install_example ( files/directories [INTO destination] ) 247 | # This installs additional examples 248 | # USE: install_example ( examples/ exampleA ) 249 | # USE: install_example ( super_example super_data INTO super) 250 | # For directories, supports optional PATTERN/REGEX argument like install(). 251 | set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) 252 | set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION 253 | "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) 254 | macro ( install_example ) 255 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 256 | _complete_install_args() 257 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 258 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 259 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 260 | COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) 261 | else () 262 | install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 263 | COMPONENT Example ) 264 | endif () 265 | endforeach() 266 | endmacro () 267 | 268 | # install_test ( files/directories [INTO destination] ) 269 | # This installs tests and test files, DOES NOT EXECUTE TESTS 270 | # USE: install_test ( my_test data.sql ) 271 | # USE: install_test ( feature_x_test INTO x ) 272 | # For directories, supports optional PATTERN/REGEX argument like install(). 273 | set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) 274 | set ( CPACK_COMPONENT_TEST_DESCRIPTION 275 | "Tests and associated data. Installed into ${INSTALL_TEST}." ) 276 | macro ( install_test ) 277 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 278 | _complete_install_args() 279 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 280 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 281 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 282 | COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) 283 | else () 284 | install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 285 | COMPONENT Test ) 286 | endif () 287 | endforeach() 288 | endmacro () 289 | 290 | # install_foo ( files/directories [INTO destination] ) 291 | # This installs optional or otherwise unneeded content 292 | # USE: install_foo ( etc/ example.doc ) 293 | # USE: install_foo ( icon.png logo.png INTO icons) 294 | # For directories, supports optional PATTERN/REGEX argument like install(). 295 | set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) 296 | set ( CPACK_COMPONENT_OTHER_DESCRIPTION 297 | "Other unspecified content. Installed into ${INSTALL_FOO}." ) 298 | macro ( install_foo ) 299 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 300 | _complete_install_args() 301 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 302 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 303 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 304 | COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) 305 | else () 306 | install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 307 | COMPONENT Other ) 308 | endif () 309 | endforeach() 310 | endmacro () 311 | 312 | ## CTest defaults 313 | 314 | ## CPack defaults 315 | set ( CPACK_GENERATOR "ZIP" ) 316 | set ( CPACK_STRIP_FILES TRUE ) 317 | set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) 318 | set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") 319 | set ( CPACK_PACKAGE_VENDOR "LuaDist" ) 320 | set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) 321 | include ( CPack ) 322 | -------------------------------------------------------------------------------- /cmake/lua.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library for Lua. 2 | # 3 | # Copyright (C) 2007-2012 LuaDist. 4 | # by David Manura, Peter Drahos 5 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 6 | # For details see the COPYRIGHT file distributed with LuaDist. 7 | # Please note that the package source code is licensed under its own license. 8 | 9 | set ( INSTALL_LMOD ${INSTALL_LIB}/lua 10 | CACHE PATH "Directory to install Lua modules." ) 11 | set ( INSTALL_CMOD ${INSTALL_LIB}/lua 12 | CACHE PATH "Directory to install Lua binary modules." ) 13 | 14 | option ( LUA_SKIP_WRAPPER 15 | "Do not build and install Lua executable wrappers." OFF ) 16 | option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) 17 | 18 | # List of (Lua module name, file path) pairs. 19 | # Used internally by add_lua_test. Built by add_lua_module. 20 | set ( _lua_modules ) 21 | 22 | # utility function: appends path `path` to path `basepath`, properly 23 | # handling cases when `path` may be relative or absolute. 24 | macro ( _append_path basepath path result ) 25 | if ( IS_ABSOLUTE "${path}" ) 26 | set ( ${result} "${path}" ) 27 | else () 28 | set ( ${result} "${basepath}/${path}" ) 29 | endif () 30 | endmacro () 31 | 32 | # install_lua_executable ( target source ) 33 | # Automatically generate a binary if srlua package is available 34 | # The application or its source will be placed into /bin 35 | # If the application source did not have .lua suffix then it will be added 36 | # USE: lua_executable ( sputnik src/sputnik.lua ) 37 | macro ( install_lua_executable _name _source ) 38 | get_filename_component ( _source_name ${_source} NAME_WE ) 39 | # Find srlua and glue 40 | find_program( SRLUA_EXECUTABLE NAMES srlua ) 41 | find_program( GLUE_EXECUTABLE NAMES glue ) 42 | # Executable output 43 | set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) 44 | if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) 45 | # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems 46 | add_custom_command( 47 | OUTPUT ${_exe} 48 | COMMAND ${GLUE_EXECUTABLE} 49 | ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} 50 | DEPENDS ${_source} 51 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 52 | VERBATIM 53 | ) 54 | # Make sure we have a target associated with the binary 55 | add_custom_target(${_name} ALL 56 | DEPENDS ${_exe} 57 | ) 58 | # Install with run permissions 59 | install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) 60 | # Also install source as optional resurce 61 | install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) 62 | else() 63 | # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic 64 | install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} 65 | RENAME ${_source_name} 66 | COMPONENT Runtime 67 | ) 68 | endif() 69 | endmacro () 70 | 71 | macro ( _lua_module_helper is_install _name ) 72 | parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) 73 | # _target is CMake-compatible target name for module (e.g. socket_core). 74 | # _module is relative path of target (e.g. socket/core), 75 | # without extension (e.g. .lua/.so/.dll). 76 | # _MODULE_SRC is list of module source files (e.g. .lua and .c files). 77 | # _MODULE_NAMES is list of module names (e.g. socket.core). 78 | if ( _MODULE_ALL_IN_ONE ) 79 | string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) 80 | string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) 81 | set ( _target "${_target}_all_in_one") 82 | set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) 83 | set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) 84 | else () 85 | string ( REPLACE "." "_" _target "${_name}" ) 86 | string ( REPLACE "." "/" _module "${_name}" ) 87 | set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) 88 | set ( _MODULE_NAMES ${_name} ) 89 | endif () 90 | if ( NOT _MODULE_SRC ) 91 | message ( FATAL_ERROR "no module sources specified" ) 92 | endif () 93 | list ( GET _MODULE_SRC 0 _first_source ) 94 | 95 | get_filename_component ( _ext ${_first_source} EXT ) 96 | if ( _ext STREQUAL ".lua" ) # Lua source module 97 | list ( LENGTH _MODULE_SRC _len ) 98 | if ( _len GREATER 1 ) 99 | message ( FATAL_ERROR "more than one source file specified" ) 100 | endif () 101 | 102 | set ( _module "${_module}.lua" ) 103 | 104 | get_filename_component ( _module_dir ${_module} PATH ) 105 | get_filename_component ( _module_filename ${_module} NAME ) 106 | _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) 107 | list ( APPEND _lua_modules "${_name}" "${_module_path}" ) 108 | 109 | if ( ${is_install} ) 110 | install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} 111 | RENAME ${_module_filename} 112 | COMPONENT Runtime 113 | ) 114 | endif () 115 | else () # Lua C binary module 116 | enable_language ( C ) 117 | find_package ( Lua REQUIRED ) 118 | include_directories ( ${LUA_INCLUDE_DIR} ) 119 | 120 | set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) 121 | 122 | get_filename_component ( _module_dir ${_module} PATH ) 123 | get_filename_component ( _module_filenamebase ${_module} NAME_WE ) 124 | foreach ( _thisname ${_MODULE_NAMES} ) 125 | list ( APPEND _lua_modules "${_thisname}" 126 | "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) 127 | endforeach () 128 | 129 | # Static module (not linking to lua) 130 | if ( LUA_STATIC_MODULE ) 131 | add_library( ${_target} STATIC ${_MODULE_SRC}) 132 | target_link_libraries ( ${_target} ${_MODULE_LINK} ) 133 | else () 134 | # Dynamic module 135 | add_library( ${_target} MODULE ${_MODULE_SRC}) 136 | target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) 137 | endif () 138 | 139 | set_target_properties ( ${_target} PROPERTIES 140 | ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" 141 | LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" 142 | PREFIX "" 143 | OUTPUT_NAME "${_module_filenamebase}" ) 144 | if ( ${is_install} ) 145 | install ( TARGETS ${_target} 146 | LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} 147 | COMPONENT Runtime 148 | ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} 149 | COMPONENT Library ) 150 | endif () 151 | endif () 152 | endmacro () 153 | 154 | # add_lua_module 155 | # Builds a Lua source module into a destination locatable by Lua 156 | # require syntax. 157 | # Binary modules are also supported where this function takes sources and 158 | # libraries to compile separated by LINK keyword. 159 | # USE: add_lua_module ( socket.http src/http.lua ) 160 | # USE2: add_lua_module ( mime.core src/mime.c ) 161 | # USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 162 | # USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) 163 | # This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing 164 | # both modules ssl.context and ssl.core). The CMake target name will be 165 | # ssl_all_in_one. 166 | # Also sets variable _module_path (relative path where module typically 167 | # would be installed). 168 | macro ( add_lua_module ) 169 | _lua_module_helper ( 0 ${ARGN} ) 170 | endmacro () 171 | 172 | 173 | # install_lua_module 174 | # This is the same as `add_lua_module` but also installs the module. 175 | # USE: install_lua_module ( socket.http src/http.lua ) 176 | # USE2: install_lua_module ( mime.core src/mime.c ) 177 | # USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 178 | macro ( install_lua_module ) 179 | _lua_module_helper ( 1 ${ARGN} ) 180 | endmacro () 181 | 182 | # Builds string representing Lua table mapping Lua modules names to file 183 | # paths. Used internally. 184 | macro ( _make_module_table _outvar ) 185 | set ( ${_outvar} ) 186 | list ( LENGTH _lua_modules _n ) 187 | if ( ${_n} GREATER 0 ) # avoids cmake complaint 188 | foreach ( _i RANGE 1 ${_n} 2 ) 189 | list ( GET _lua_modules ${_i} _path ) 190 | math ( EXPR _ii ${_i}-1 ) 191 | list ( GET _lua_modules ${_ii} _name ) 192 | set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") 193 | endforeach () 194 | endif () 195 | set ( ${_outvar} 196 | "local modules = { 197 | ${_table}}" ) 198 | endmacro () 199 | 200 | # add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) 201 | # Runs Lua script `_testfile` under CTest tester. 202 | # Optional named argument `WORKING_DIRECTORY` is current working directory to 203 | # run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). 204 | # Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. 205 | # Any modules previously defined with install_lua_module are automatically 206 | # preloaded (via package.preload) prior to running the test script. 207 | # Under LuaDist, set test=true in config.lua to enable testing. 208 | # USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) 209 | macro ( add_lua_test _testfile ) 210 | if ( NOT SKIP_TESTING ) 211 | parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) 212 | include ( CTest ) 213 | find_program ( LUA NAMES lua lua.bat ) 214 | get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) 215 | get_filename_component ( TESTFILENAME ${_testfile} NAME ) 216 | get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) 217 | 218 | # Write wrapper script. 219 | # Note: One simple way to allow the script to find modules is 220 | # to just put them in package.preload. 221 | set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) 222 | _make_module_table ( _table ) 223 | set ( TESTWRAPPERSOURCE 224 | "local CMAKE_CFG_INTDIR = ... or '.' 225 | ${_table} 226 | local function preload_modules(modules) 227 | for name, path in pairs(modules) do 228 | if path:match'%.lua' then 229 | package.preload[name] = assert(loadfile(path)) 230 | else 231 | local name = name:gsub('.*%-', '') -- remove any hyphen prefix 232 | local symbol = 'luaopen_' .. name:gsub('%.', '_') 233 | --improve: generalize to support all-in-one loader? 234 | local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) 235 | package.preload[name] = assert(package.loadlib(path, symbol)) 236 | end 237 | end 238 | end 239 | preload_modules(modules) 240 | arg[0] = '${TESTFILEABS}' 241 | table.remove(arg, 1) 242 | return assert(loadfile '${TESTFILEABS}')(unpack(arg)) 243 | " ) 244 | if ( _ARG_WORKING_DIRECTORY ) 245 | get_filename_component ( 246 | TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) 247 | # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. 248 | set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) 249 | endif () 250 | file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) 251 | add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} 252 | ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" 253 | ${_ARG_DEFAULT_ARGS} ) 254 | endif () 255 | # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake 256 | # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper 257 | # expansion by the native build tool. 258 | endmacro () 259 | 260 | 261 | # Converts Lua source file `_source` to binary string embedded in C source 262 | # file `_target`. Optionally compiles Lua source to byte code (not available 263 | # under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua 264 | # versions of bin2c [1] and luac [2] may be passed respectively as additional 265 | # arguments. 266 | # 267 | # [1] http://lua-users.org/wiki/BinToCee 268 | # [2] http://lua-users.org/wiki/LuaCompilerInLua 269 | function ( add_lua_bin2c _target _source ) 270 | find_program ( LUA NAMES lua lua.bat ) 271 | execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" 272 | RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) 273 | if ( NOT ${_LUA_DUMP_RESULT} ) 274 | SET ( HAVE_LUA_DUMP true ) 275 | endif () 276 | message ( "-- string.dump=${HAVE_LUA_DUMP}" ) 277 | 278 | if ( ARGV2 ) 279 | get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) 280 | set ( BIN2C ${LUA} ${BIN2C} ) 281 | else () 282 | find_program ( BIN2C NAMES bin2c bin2c.bat ) 283 | endif () 284 | if ( HAVE_LUA_DUMP ) 285 | if ( ARGV3 ) 286 | get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) 287 | set ( LUAC ${LUA} ${LUAC} ) 288 | else () 289 | find_program ( LUAC NAMES luac luac.bat ) 290 | endif () 291 | endif ( HAVE_LUA_DUMP ) 292 | message ( "-- bin2c=${BIN2C}" ) 293 | message ( "-- luac=${LUAC}" ) 294 | 295 | get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) 296 | if ( HAVE_LUA_DUMP ) 297 | get_filename_component ( SOURCEBASE ${_source} NAME_WE ) 298 | add_custom_command ( 299 | OUTPUT ${_target} DEPENDS ${_source} 300 | COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 301 | ${SOURCEABS} 302 | COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 303 | ">${_target}" ) 304 | else () 305 | add_custom_command ( 306 | OUTPUT ${_target} DEPENDS ${SOURCEABS} 307 | COMMAND ${BIN2C} ${_source} ">${_target}" ) 308 | endif () 309 | endfunction() 310 | -------------------------------------------------------------------------------- /dist.info: -------------------------------------------------------------------------------- 1 | --- This file is part of LuaDist project 2 | 3 | name = "lua-git" 4 | version = "0.4" 5 | 6 | desc = "A library to access Git repositories and its objects (files, commits) in Lua" 7 | author = "Michal Kottman" 8 | license = "MIT/X11" 9 | url = "https://github.com/mkottman/lua-git" 10 | maintainer = "Michal Kottman" 11 | 12 | depends = { 13 | "lua ~> 5.1", 14 | "luasocket >= 2.0.0", 15 | "luafilesystem >= 1.6", 16 | "zlib >= 1.2", 17 | } 18 | -------------------------------------------------------------------------------- /lua/git.lua: -------------------------------------------------------------------------------- 1 | require 'git.util' 2 | require 'git.objects' 3 | require 'git.pack' 4 | require 'git.repo' 5 | require 'git.protocol' 6 | -------------------------------------------------------------------------------- /lua/git/objects.lua: -------------------------------------------------------------------------------- 1 | local util = require 'git.util' 2 | 3 | local assert, next, io, print, os, type, string, pairs, tostring = 4 | assert, next, io, print, os, type, string, pairs, tostring 5 | local join_path = git.util.join_path 6 | 7 | local require = require 8 | 9 | local isPosix = package.config:sub(1,1) == '/' -- wild guess 10 | 11 | module(...) 12 | 13 | Commit = {} 14 | Commit.__index = Commit 15 | 16 | function Commit:tree() 17 | return self.repo:tree(self.tree_sha) 18 | end 19 | 20 | function Commit:checkout(path) 21 | assert(path, 'path argument missing') 22 | self:tree():checkoutTo(path) 23 | end 24 | 25 | 26 | Tree = {} 27 | Tree.__index = function (t,k) 28 | if Tree[k] then return Tree[k] end 29 | return t:entry(k) 30 | end 31 | 32 | function Tree:entries() 33 | return function(t, n) 34 | local n, entry = next(t, n) 35 | if entry then 36 | local object 37 | if entry.type == 'tree' then 38 | object = self.repo:tree(entry.id) 39 | elseif entry.type == 'blob' then 40 | object = self.repo:blob(entry.id) 41 | object.mode = entry.mode 42 | elseif entry.type == 'commit' then 43 | -- this is possibly a commit in a submodule, 44 | -- do not retrieve it from current repo 45 | object = entry 46 | else 47 | error('Unknown entry type: ' .. entry.type) 48 | end 49 | return n, entry.type, object 50 | end 51 | end, self._entries 52 | end 53 | 54 | function Tree:entry(n) 55 | local e = self._entries[n] 56 | if not e then return end 57 | if e.type == 'tree' then 58 | return self.repo:tree(e.id) 59 | elseif e.type == 'commit' then 60 | return self.repo:commit(e.id) 61 | elseif e.type == 'blob' then 62 | return self.repo:blob(e.id) 63 | else 64 | error('Unknown entry type: ' .. e.type) 65 | end 66 | end 67 | 68 | function Tree:walk(func, path) 69 | path = path or '.' 70 | assert(type(func) == "function", "argument is not a function") 71 | local function walk(tree, path) 72 | for name, type, entry in tree:entries() do 73 | local entry_path = join_path(path, name) 74 | func(entry, entry_path, type) 75 | 76 | if type == "tree" then 77 | walk(entry, entry_path) 78 | end 79 | end 80 | end 81 | walk(self, path) 82 | end 83 | 84 | function Tree:checkoutTo(path) 85 | util.make_dir(path) 86 | self:walk(function (entry, entry_path, type) 87 | if type == 'tree' then 88 | util.make_dir(entry_path) 89 | elseif type == 'blob' then 90 | local out = assert(io.open(entry_path, 'wb')) 91 | out:write(entry:content()) 92 | out:close() 93 | if isPosix then 94 | local mode = entry.mode:sub(-3,-1) -- fixme: is this ok? 95 | local cmd = 'chmod '..mode..' "'..entry_path..'"' 96 | os.execute(cmd) 97 | end 98 | elseif type == 'commit' then 99 | -- this is a submodule referencing a commit, 100 | -- make a directory for it 101 | util.make_dir(entry_path) 102 | else 103 | error('Unknown entry type: ', type) 104 | end 105 | end, path) 106 | end 107 | 108 | Blob = {} 109 | Blob.__index = Blob 110 | 111 | function Blob:content() 112 | if self.stored then 113 | local f = self.repo:raw_object(self.id) 114 | local ret = f:read('*a') or "" 115 | f:close() 116 | return ret 117 | else 118 | return self.data 119 | end 120 | end 121 | 122 | -------------------------------------------------------------------------------- /lua/git/pack.lua: -------------------------------------------------------------------------------- 1 | local io = io 2 | local core = require 'git.core' 3 | 4 | local assert, pcall, print, select, setmetatable, string, type, unpack = 5 | assert, pcall, print, select, setmetatable, string, type, unpack 6 | 7 | local ord = string.byte 8 | local fmt = string.format 9 | local concat, insert = table.concat, table.insert 10 | 11 | local band = core.band 12 | local rshift, lshift = core.rshift, core.lshift 13 | 14 | local to_hex = git.util.to_hex 15 | local from_hex = git.util.from_hex 16 | local object_sha = git.util.object_sha 17 | local binary_sha = git.util.binary_sha 18 | local readable_sha = git.util.readable_sha 19 | local reader = git.util.reader 20 | 21 | module(...) 22 | 23 | -- read git/Documentation/technical/pack-format.txt for some inspiration 24 | 25 | -- 1 = commit, 2 = tree ... 26 | local types = {'commit', 'tree', 'blob', 'tag', '???', 'ofs_delta', 'ref_delta'} 27 | 28 | -- read a 4 byte unsigned integer stored in network order 29 | local function read_int(f) 30 | local s = f:read(4) 31 | local a,b,c,d = s:byte(1,4) 32 | return a*256^3 + b*256^2 + c*256 + d 33 | end 34 | 35 | -- read in the type and file length 36 | local function read_object_header(f) 37 | local b = ord(f:read(1)) 38 | local type = band(rshift(b, 4), 0x7) 39 | local len = band(b, 0xF) 40 | local ofs = 0 41 | while band(b, 0x80) ~= 0 do 42 | b = ord(f:read(1)) 43 | len = len + lshift(band(b, 0x7F), ofs * 7 + 4) 44 | ofs = ofs + 1 45 | end 46 | return len, type 47 | end 48 | 49 | -- reads in the delta header and returns the offset where original data is stored 50 | local function read_delta_header(f) 51 | local b = ord(f:read(1)) 52 | local offset = band(b, 0x7F) 53 | while band(b, 0x80) ~= 0 do 54 | offset = offset + 1 55 | b = ord(f:read(1)) 56 | offset = lshift(offset, 7) + band(b, 0x7F) 57 | end 58 | return offset 59 | end 60 | 61 | -- read just enough of file `f` to uncompress `size` bytes 62 | local function uncompress_by_len(f, size) 63 | local z = core.inflate() 64 | local chunks = {} 65 | local CHUNK_SIZE = 1024 66 | local curr_pos = f:seek() 67 | local inflated, eof, total 68 | -- read until end of zlib-compresed stream 69 | while not eof do 70 | local data = f:read(CHUNK_SIZE) 71 | inflated, eof, total = z(data) 72 | insert(chunks, inflated) 73 | end 74 | -- repair the current position in stream 75 | f:seek('set', curr_pos + total) 76 | return concat(chunks) 77 | end 78 | 79 | -- uncompress the object from the current location in `f` 80 | local function unpack_object(f, len, type) 81 | local data = uncompress_by_len(f, len) 82 | return data, len, type 83 | end 84 | 85 | -- returns a size value encoded in delta data 86 | local function delta_size(f) 87 | local size = 0 88 | local i = 0 89 | repeat 90 | local b = ord(f:read(1)) 91 | size = size + lshift(band(b, 0x7F), i) 92 | i = i + 7 93 | until band(b, 0x80) == 0 94 | return size 95 | end 96 | 97 | -- returns a patched object from string `base` according to `delta` data 98 | local function patch_object(base, delta, base_type) 99 | -- insert delta codes into temporary file 100 | local df = reader(delta) 101 | 102 | -- retrieve original and result size (for checks) 103 | local orig_size = delta_size(df) 104 | assert(#base == orig_size, fmt('#base(%d) ~= orig_size(%d)', #base, orig_size)) 105 | 106 | local result_size = delta_size(df) 107 | local size = result_size 108 | 109 | local result = {} 110 | 111 | -- process the delta codes 112 | local cmd = df:read(1) 113 | while cmd do 114 | cmd = ord(cmd) 115 | if cmd == 0 then 116 | error('unexpected delta code 0') 117 | elseif band(cmd, 0x80) ~= 0 then -- copy a selected part of base data 118 | local cp_off, cp_size = 0, 0 119 | -- retrieve offset 120 | if band(cmd, 0x01) ~= 0 then cp_off = ord(df:read(1)) end 121 | if band(cmd, 0x02) ~= 0 then cp_off = cp_off + ord(df:read(1))*256 end 122 | if band(cmd, 0x04) ~= 0 then cp_off = cp_off + ord(df:read(1))*256^2 end 123 | if band(cmd, 0x08) ~= 0 then cp_off = cp_off + ord(df:read(1))*256^3 end 124 | -- retrieve size 125 | if band(cmd, 0x10) ~= 0 then cp_size = ord(df:read(1)) end 126 | if band(cmd, 0x20) ~= 0 then cp_size = cp_size + ord(df:read(1))*256 end 127 | if band(cmd, 0x40) ~= 0 then cp_size = cp_size + ord(df:read(1))*256^2 end 128 | if cp_size == 0 then cp_size = 0x10000 end 129 | if cp_off + cp_size > #base or cp_size > size then break end 130 | -- get the data and append it to result 131 | local data = base:sub(cp_off + 1, cp_off + cp_size) 132 | insert(result, data) 133 | size = size - cp_size 134 | else -- insert new data 135 | if cmd > size then break end 136 | local data = df:read(cmd) 137 | insert(result, data) 138 | size = size - cmd 139 | end 140 | cmd = df:read(1) 141 | end 142 | 143 | df:close() 144 | 145 | result = concat(result) 146 | assert(#result == result_size, fmt('#result(%d) ~= result_size(%d)', #result, result_size)) 147 | return result, result_size, base_type 148 | end 149 | 150 | Pack = {} 151 | Pack.__index = Pack 152 | 153 | -- read an object from the current location in pack, or from a specific `offset` 154 | -- if specified 155 | function Pack:read_object(offset, ignore_data) 156 | local f = self.pack_file 157 | if offset then 158 | f:seek('set', offset) 159 | end 160 | local curr_pos = f:seek() 161 | 162 | local len, type = read_object_header(f) 163 | if type < 5 then -- commit, tree, blob, tag 164 | return unpack_object(f, len, type) 165 | elseif type == 6 then -- ofs_delta 166 | local offset = read_delta_header(f) 167 | local delta_data = uncompress_by_len(f, len) 168 | if not ignore_data then 169 | -- the offset is negative from the current location 170 | local base, base_len, base_type = self:read_object(curr_pos - offset) 171 | return patch_object(base, delta_data, base_type) 172 | end 173 | elseif type == 7 then -- ref_delta 174 | local sha = f:read(20) 175 | local delta_data = uncompress_by_len(f, len) 176 | if not ignore_data then 177 | -- lookup the object in the pack by sha 178 | -- FIXME: maybe lookup in repo/other packs 179 | local base_offset = self.index[binary_sha(sha)] 180 | local base, base_len, base_type = self:read_object(base_offset) 181 | return patch_object(base, delta_data, base_type) 182 | end 183 | else 184 | error('unknown object type: '..type) 185 | end 186 | end 187 | 188 | -- returns true if this pack contains the given object 189 | function Pack:has_object(sha) 190 | return self.index[binary_sha(sha)] ~= nil 191 | end 192 | 193 | -- if the object name `sha` exists in the pack, returns a temporary file with the 194 | -- object content, length and type, otherwise returns nil 195 | function Pack:get_object(sha) 196 | local offset = self.index[binary_sha(sha)] 197 | if not offset then 198 | print('!!! Failed to find object', readable_sha(sha)) 199 | end 200 | 201 | local data, len, type = self:read_object(offset) 202 | local f = reader(data) 203 | 204 | return f, len, types[type] 205 | end 206 | 207 | function Pack:unpack(repo) 208 | for i=1, self.nobjects do 209 | local offset = self.offsets[i] 210 | local data, len, type = self:read_object(offset) 211 | repo:store_object(data, len, types[type]) 212 | end 213 | end 214 | 215 | -- parses the index 216 | function Pack:parse_index(index_file) 217 | local f = index_file 218 | 219 | local head = f:read(4) 220 | assert(head == '\255tOc', "Incorrect header: " .. head) 221 | local version = read_int(f) 222 | assert(version == 2, "Incorrect version: " .. version) 223 | 224 | -- first the fanout table (how many objects are in the index, whose 225 | -- first byte is below or equal to i) 226 | local fanout = {} 227 | for i=0, 255 do 228 | local nobjs = read_int(f) 229 | fanout[i] = nobjs 230 | end 231 | 232 | -- the last element in fanout is the number of all objects in index 233 | local count = fanout[255] 234 | 235 | -- then come the sorted object names (=sha hash) 236 | local tmp = {} 237 | for i=1,count do 238 | local sha = f:read(20) 239 | tmp[i] = { sha = sha } 240 | end 241 | 242 | -- then the CRCs (assume ok, skip them) 243 | for i=1, count do 244 | local crc = f:read(4) 245 | end 246 | 247 | -- then come the offsets - read just the 32bit ones, does not handle packs > 2G 248 | for i=1, count do 249 | local offset = read_int(f) 250 | tmp[i].offset = offset 251 | end 252 | 253 | -- construct the lookup table 254 | local lookup = {} 255 | for i=1, count do 256 | lookup[tmp[i].sha] = tmp[i].offset 257 | end 258 | self.index = lookup 259 | end 260 | 261 | -- constructs the index/offsets if the index file is missing 262 | function Pack:construct_index(path) 263 | local index = {} 264 | for i=1, self.nobjects do 265 | local offset = self.offsets[i] 266 | local data, len, type = self:read_object(offset) 267 | local sha = object_sha(data, len, types[type]) 268 | index[binary_sha(sha)] = offset 269 | end 270 | self.index = index 271 | end 272 | 273 | function Pack:close() 274 | self.pack_file:close() 275 | end 276 | 277 | function Pack.open(path) 278 | local fp = assert(io.open(path, 'rb')) -- stays open 279 | 280 | -- read the pack header 281 | local head = fp:read(4) 282 | assert(head == 'PACK', "Incorrect header: " .. head) 283 | local version = read_int(fp) 284 | assert(version == 2, "Incorrect version: " .. version) 285 | local nobj = read_int(fp) 286 | 287 | local pack = setmetatable({ 288 | offsets = {}, 289 | nobjects = nobj, 290 | pack_file = fp, 291 | }, Pack) 292 | 293 | -- fill the offsets by traversing through the pack 294 | for i=1,nobj do 295 | pack.offsets[i] = fp:seek() 296 | -- ignore the object data, we only need the offset in the pack 297 | pack:read_object(nil, true) 298 | end 299 | 300 | -- read the index 301 | local fi = io.open((path:gsub('%.pack$', '.idx')), 'rb') 302 | if fi then 303 | pack:parse_index(fi) 304 | fi:close() 305 | else 306 | pack:construct_index(path) 307 | end 308 | 309 | return pack 310 | end 311 | 312 | return Pack -------------------------------------------------------------------------------- /lua/git/protocol.lua: -------------------------------------------------------------------------------- 1 | local socket = require 'socket' 2 | local urllib = require 'socket.url' 3 | local lfs = require 'lfs' 4 | 5 | local Repo = git.repo.Repo 6 | local Pack = git.pack.Pack 7 | local join_path = git.util.join_path 8 | local parent_dir = git.util.parent_dir 9 | local make_dir = git.util.make_dir 10 | local correct_separators = git.util.correct_separators 11 | 12 | local assert, error, getmetatable, io, os, pairs, print, require, string, tonumber = 13 | assert, error, getmetatable, io, os, pairs, print, require, string, tonumber 14 | 15 | local _VERSION, newproxy = _VERSION, newproxy 16 | 17 | local isPosix = package.config:sub(1,1) == '/' 18 | 19 | module(...) 20 | 21 | local GIT_PORT = 9418 22 | 23 | local function git_connect(host) 24 | local sock = assert(socket.connect(host, GIT_PORT)) 25 | local gitsocket = {} 26 | 27 | function gitsocket:send(data) 28 | if not data then -- flush packet 29 | sock:send('0000') 30 | else 31 | local len = #data + 4 32 | len = string.format("%04x", len) 33 | assert(sock:send(len .. data)) 34 | end 35 | end 36 | 37 | function gitsocket:receive() 38 | local len = assert(sock:receive(4)) 39 | len = tonumber(len, 16) 40 | if len == 0 then return end -- flush packet 41 | local data = assert(sock:receive(len - 4)) 42 | return data 43 | end 44 | 45 | function gitsocket:close() 46 | sock:close() 47 | end 48 | 49 | return gitsocket 50 | end 51 | 52 | local function addFinalizer(object, finalizer) 53 | if _VERSION <= "Lua 5.1" then 54 | local gc = newproxy(true) 55 | getmetatable(gc).__gc = finalizer 56 | object.__gc = gc 57 | else 58 | local mt = getmetatable(object) 59 | if mt then mt.__gc = finalizer 60 | else setmetatable(object, {__gc = finalizer}) 61 | end 62 | end 63 | end 64 | 65 | local function tmpname() 66 | if not isPosix then 67 | local prefix = os.getenv("TEMP") 68 | local name = os.tmpname() 69 | return join_path(prefix, name) 70 | else 71 | return os.tmpname() 72 | end 73 | end 74 | 75 | local function git_fetch(host, path, repo, head, supress_progress) 76 | local s = git_connect(host) 77 | s:send('git-upload-pack '..path..'\0host='..host..'\0') 78 | 79 | local refs, refsbyname = {}, {} 80 | repeat 81 | local ref = s:receive() 82 | if ref then 83 | local sha, name = ref:sub(1,40), ref:sub(42, -2) 84 | refs[sha] = name 85 | refsbyname[name] = sha 86 | end 87 | until not ref 88 | 89 | local wantedSha 90 | local headsha = head and refsbyname[head] 91 | 92 | for sha, ref in pairs(refs) do 93 | -- we implicitly want this ref 94 | local wantObject = true 95 | -- unless we ask for a specific head 96 | if headsha then 97 | if sha ~= headsha then 98 | wantObject = false 99 | else 100 | wantedSha = sha 101 | end 102 | end 103 | -- or we already have it 104 | if repo and repo:has_object(sha) then 105 | wantObject = false 106 | end 107 | if wantObject then 108 | s:send('want '..sha..' multi_ack_detailed side-band-64k ofs-delta\n') 109 | end 110 | end 111 | 112 | if head and not wantedSha then 113 | error("Server does not have "..head) 114 | end 115 | 116 | s:send('deepen 1') 117 | s:send() 118 | while s:receive() do end 119 | s:send('done\n') 120 | 121 | assert(s:receive() == "NAK\n") 122 | 123 | local packname = tmpname() .. '.pack' 124 | local packfile = assert(io.open(packname, 'wb')) 125 | repeat 126 | local got = s:receive() 127 | if got then 128 | -- get sideband channel, 1=pack data, 2=progress, 3=error 129 | local cmd = string.byte(got:sub(1,1)) 130 | local data = got:sub(2) 131 | if cmd == 1 then 132 | packfile:write(data) 133 | elseif cmd == 2 then 134 | if not supress_progress then io.write(data) end 135 | else 136 | error(data) 137 | end 138 | end 139 | until not got 140 | 141 | packfile:close() 142 | s:close() 143 | 144 | local pack = Pack.open(packname) 145 | if repo then 146 | pack:unpack(repo) 147 | repo.isShallow = true 148 | if wantedSha then 149 | local headfile = correct_separators(join_path(repo.dir, head)) 150 | assert(make_dir(parent_dir(headfile))) 151 | local f = assert(io.open(headfile, 'wb')) 152 | f:write(wantedSha) 153 | f:close() 154 | end 155 | end 156 | 157 | addFinalizer(pack, function() 158 | os.remove(packname) 159 | end) 160 | 161 | return pack, wantedSha 162 | end 163 | 164 | function fetch(url, repo, head, supress_progress) 165 | if repo then assert(getmetatable(repo) == Repo, "arg #2 is not a repository") end 166 | url = urllib.parse(url) 167 | if url.scheme == 'git' then 168 | local pack, sha = git_fetch(url.host, url.path, repo, head, supress_progress) 169 | return pack, sha 170 | else 171 | error('unsupported scheme: '..url.scheme) 172 | end 173 | end 174 | 175 | function remotes(url) 176 | -- TODO: refactor common code 177 | url = assert(urllib.parse(url)) 178 | 179 | if url.scheme ~= 'git' then 180 | error('unsupported scheme: '..url.scheme) 181 | end 182 | 183 | local host, path = url.host, url.path 184 | 185 | local s = git_connect(host) 186 | s:send('git-upload-pack '..path..'\0host='..host..'\0') 187 | 188 | local remote = {} 189 | repeat 190 | local ref = s:receive() 191 | if ref then 192 | local sha, name = ref:sub(1,40), ref:sub(42, -2) 193 | remote[name] = sha 194 | end 195 | until not ref 196 | 197 | s:close() 198 | 199 | return remote 200 | end 201 | -------------------------------------------------------------------------------- /lua/git/repo.lua: -------------------------------------------------------------------------------- 1 | local util = require 'git.util' 2 | local objects = require 'git.objects' 3 | local core = require 'git.core' 4 | local pack = require 'git.pack' 5 | 6 | local join_path = util.join_path 7 | local decompressed = util.decompressed 8 | local read_until_nul = util.read_until_nul 9 | local to_hex = util.to_hex 10 | local object_sha = util.object_sha 11 | local readable_sha = util.readable_sha 12 | 13 | local deflate = core.deflate 14 | 15 | local lfs = require 'lfs' 16 | local assert, error, io, ipairs, print, os, setmetatable, string, table = 17 | assert, error, io, ipairs, print, os, setmetatable, string, table 18 | 19 | module(...) 20 | 21 | Repo = {} 22 | Repo.__index = Repo 23 | 24 | -- retrieves an object identified by `sha` from the repository or its packs 25 | -- returns a file-like object (supports 'read', 'seek' and 'close'), the size 26 | -- of the object and its type 27 | -- errors when the object does not exist 28 | function Repo:raw_object(sha) 29 | -- first, look in 'objects' directory 30 | -- first byte of sha is the directory, the rest is name of object file 31 | sha = readable_sha(sha) 32 | local dir = sha:sub(1,2) 33 | local file = sha:sub(3) 34 | local path = join_path(self.dir, 'objects', dir, file) 35 | 36 | if not lfs.attributes(path, 'size') then 37 | -- then, try to look in packs 38 | for _, pack in ipairs(self.packs) do 39 | local obj, len, typ = pack:get_object(sha) 40 | if obj then 41 | return obj, len, typ 42 | end 43 | end 44 | error('Object not found in object neither in packs: '..sha) 45 | else 46 | -- the objects are zlib compressed 47 | local f = decompressed(path) 48 | 49 | -- retrieve the type and length - SP \0 50 | local content = read_until_nul(f) 51 | local typ, len = content:match('(%w+) (%d+)') 52 | 53 | return f, len, typ 54 | end 55 | end 56 | 57 | --- Store a new object into the repository in `objects` directory. 58 | -- @param data A string containing the contents of the new file. 59 | -- @param len The length of the data. 60 | -- @param type One of 'commit', 'blob', 'tree', 'tag' 61 | function Repo:store_object(data, len, type) 62 | local sha = readable_sha(object_sha(data, len, type)) 63 | local dir = sha:sub(1,2) 64 | local file = sha:sub(3) 65 | util.make_dir(join_path(self.dir, 'objects', dir)) 66 | local path = join_path(self.dir, 'objects', dir, file) 67 | local fo = assert(io.open(path, 'wb')) 68 | local header = type .. ' ' .. len .. '\0' 69 | local compressed = deflate()(header .. data, "finish") 70 | fo:write(compressed) 71 | fo:close() 72 | end 73 | 74 | local function resolvetag(f) 75 | local tag 76 | local line = f:read() 77 | while line do 78 | tag = line:match('^object (%x+)$') 79 | if tag then break end 80 | line = f:read() 81 | end 82 | f:close() 83 | return tag 84 | end 85 | 86 | function Repo:commit(sha) 87 | local f, len, typ = self:raw_object(sha) 88 | while typ == 'tag' do 89 | sha = assert(resolvetag(f), 'could not parse tag for '..readable_sha(sha)) 90 | f, len, typ = self:raw_object(sha) 91 | end 92 | assert(typ == 'commit', string.format('%s (%s) is not a commit', sha, typ)) 93 | 94 | local commit = { id = sha, repo = self, stored = true, parents = {} } 95 | repeat 96 | local line = f:read() 97 | if not line then break end 98 | 99 | local space = line:find(' ') or 0 100 | local word = line:sub(1, space - 1) 101 | local afterSpace = line:sub(space + 1) 102 | 103 | if word == 'tree' then 104 | commit.tree_sha = afterSpace 105 | elseif word == 'parent' then 106 | table.insert(commit.parents, afterSpace) 107 | elseif word == 'author' then 108 | commit.author = afterSpace 109 | elseif word == 'committer' then 110 | commit.committer = afterSpace 111 | elseif commit.message then 112 | table.insert(commit.message, line) 113 | elseif line == '' then 114 | commit.message = {} 115 | end 116 | until false -- ends with break 117 | f:close() 118 | 119 | commit.message = table.concat(commit.message, '\n') 120 | 121 | return setmetatable(commit, objects.Commit) 122 | end 123 | 124 | function Repo:tree(sha) 125 | local f, len, typ = self:raw_object(sha) 126 | assert(typ == 'tree', string.format('%s (%s) is not a tree', sha, typ)) 127 | 128 | local tree = { id = sha, repo = self, stored = true, _entries = {} } 129 | 130 | while true do 131 | local info = read_until_nul(f) 132 | if not info then break end 133 | local entry_sha = to_hex(f:read(20)) 134 | local mode, name = info:match('^(%d+)%s(.+)$') 135 | local entry_type = 'blob' 136 | if mode == '40000' then 137 | entry_type = 'tree' 138 | elseif mode == '160000' then 139 | entry_type = 'commit' 140 | end 141 | tree._entries[name] = { mode = mode, id = entry_sha, type = entry_type } 142 | end 143 | 144 | f:close() 145 | 146 | return setmetatable(tree, objects.Tree) 147 | end 148 | 149 | -- retrieves a Blob 150 | function Repo:blob(sha) 151 | local f, len, typ = self:raw_object(sha) 152 | f:close() -- can be reopened in Blob:content() 153 | 154 | assert(typ == 'blob', string.format('%s (%s) is not a blob', sha, typ)) 155 | return setmetatable({ 156 | id = sha, 157 | len = len, 158 | repo = self, 159 | stored = true }, objects.Blob) 160 | end 161 | 162 | function Repo:head() 163 | return self:commit(self.refs.HEAD) 164 | end 165 | 166 | function Repo:has_object(sha) 167 | local dir = sha:sub(1,2) 168 | local file = sha:sub(3) 169 | local path = join_path(self.dir, 'objects', dir, file) 170 | 171 | if lfs.attributes(path, 'size') then return true end 172 | 173 | for _, pack in ipairs(self.packs) do 174 | local has = pack:has_object(sha) 175 | if has then return true end 176 | end 177 | 178 | return false 179 | end 180 | 181 | function Repo:checkout(sha, target) 182 | if not target then target = self.workDir end 183 | assert(target, 'target directory not specified') 184 | 185 | local commit = self:commit(sha) 186 | commit:checkout(target) 187 | 188 | -- if the repo was checked out using the deepen command (one level of history only) 189 | -- mark the commit's parent as shalow, that is it has no history 190 | if self.isShallow then 191 | -- if it has a parent, mark it shallow 192 | if commit.parents[1] then 193 | local f = assert(io.open(self.dir .. '/shallow', "w")) 194 | f:write(commit.parents[1], '\n') 195 | f:close() 196 | end 197 | end 198 | end 199 | 200 | function Repo:close() 201 | for _, pack in ipairs(self.packs) do 202 | pack:close() 203 | end 204 | end 205 | 206 | function create(dir) 207 | if not dir:match('%.git.?$') then 208 | dir = join_path(dir, '.git') 209 | end 210 | 211 | util.make_dir(dir) 212 | util.make_dir(dir .. '/branches') 213 | util.make_dir(dir .. '/hooks') 214 | util.make_dir(dir .. '/info') 215 | util.make_dir(dir .. '/objects/info') 216 | util.make_dir(dir .. '/objects/pack') 217 | util.make_dir(dir .. '/refs/heads') 218 | util.make_dir(dir .. '/refs/tags') 219 | util.make_dir(dir .. '/refs/remotes') 220 | 221 | do 222 | local f = assert(io.open(dir .. "/HEAD", "w")) 223 | f:write("ref: refs/heads/master\n") 224 | f:close() 225 | end 226 | 227 | local refs = {} 228 | local packs = {} 229 | 230 | return setmetatable({ 231 | dir = dir, 232 | refs = refs, 233 | packs = packs, 234 | }, Repo) 235 | end 236 | 237 | -- opens a repository located in working directory `dir` or directly a .git repo 238 | function open(dir) 239 | local workDir = dir 240 | if not dir:match('%.git.?$') then 241 | dir = join_path(dir, '.git') 242 | else 243 | workDir = nil -- no working directory, working directly with repo 244 | end 245 | 246 | local refs = {} 247 | for _,d in ipairs{'refs/heads', 'refs/tags'} do 248 | for fn in lfs.dir(join_path(dir, d)) do 249 | if fn ~= '.' and fn ~= '..' then 250 | local path = join_path(dir, d, fn) 251 | local f = assert(io.open(path), 'rb') 252 | local ref = f:read() 253 | refs[join_path(d, fn)] = ref 254 | f:close() 255 | end 256 | end 257 | end 258 | 259 | local packs = {} 260 | for fn in lfs.dir(join_path(dir, 'objects/pack')) do 261 | if fn:match('%.pack$') then 262 | local path = join_path(dir, 'objects/pack', fn) 263 | table.insert(packs, pack.open(path)) 264 | end 265 | end 266 | 267 | local head = io.open(join_path(dir, 'HEAD'), 'rb') 268 | if head then 269 | local src = head:read() 270 | local HEAD = src:match('ref: (.-)$') 271 | refs.HEAD = refs[HEAD] 272 | head:close() 273 | end 274 | 275 | return setmetatable({ 276 | dir = dir, 277 | workDir = workDir, 278 | refs = refs, 279 | packs = packs, 280 | }, Repo) 281 | end 282 | 283 | return Repo 284 | -------------------------------------------------------------------------------- /lua/git/util.lua: -------------------------------------------------------------------------------- 1 | local lfs = require 'lfs' 2 | local core = require 'git.core' 3 | local deflate = core.deflate 4 | local inflate = core.inflate 5 | local sha = core.sha 6 | 7 | module(..., package.seeall) 8 | 9 | local BUF_SIZE = 4096 10 | 11 | local dirsep = package.config:sub(1,1) 12 | 13 | -- replaces '/' path separators on Windows with the correct ones ('\\') 14 | function correct_separators(path) 15 | return path:gsub('/', dirsep) 16 | end 17 | 18 | -- joins several path components into a single path, uses system-specific directory 19 | -- separator, cleans input, i.e. join_path('a/', 'b', 'c/') => 'a/b/c' 20 | function join_path(...) 21 | local n = select('#', ...) 22 | local args = {...} 23 | for i=1,n do 24 | args[i] = args[i]:gsub(dirsep..'?$', '') 25 | end 26 | return table.concat(args, dirsep, 1, n) 27 | end 28 | 29 | -- Return the path with the all occurences of '/.' or '\.' (representing 30 | -- the current directory) removed. 31 | local function remove_curr_dir_dots(path) 32 | while path:match(dirsep .. "%." .. dirsep) do -- match("/%./") 33 | path = path:gsub(dirsep .. "%." .. dirsep, dirsep) -- gsub("/%./", "/") 34 | end 35 | return path:gsub(dirsep .. "%.$", "") -- gsub("/%.$", "") 36 | end 37 | 38 | -- Return whether the path is a root. 39 | local function is_root(path) 40 | return path:find("^[%u%U.]?:?[/\\]$") 41 | end 42 | 43 | -- Return the path with the unnecessary trailing separator removed. 44 | local function remove_trailing(path) 45 | if path:sub(-1) == dirsep and not is_root(path) then path = path:sub(1,-2) end 46 | return path 47 | end 48 | 49 | -- Extract file or directory name from its path. 50 | local function extract_name(path) 51 | if is_root(path) then return path end 52 | 53 | path = remove_trailing(path) 54 | path = path:gsub("^.*" .. dirsep, "") 55 | return path 56 | end 57 | 58 | -- Return the string 'str', with all magic (pattern) characters escaped. 59 | local function escape_magic(str) 60 | local escaped = str:gsub('[%-%.%+%[%]%(%)%^%%%?%*%^%$]','%%%1') 61 | return escaped 62 | end 63 | 64 | -- Return parent directory of the 'path' or nil if there's no parent directory. 65 | -- If 'path' is a path to file, return the directory the file is in. 66 | function parent_dir(path) 67 | path = remove_curr_dir_dots(path) 68 | path = remove_trailing(path) 69 | 70 | local dir = path:gsub(escape_magic(extract_name(path)) .. "$", "") 71 | if dir == "" then 72 | return nil 73 | else 74 | return remove_trailing(dir) 75 | end 76 | end 77 | 78 | -- Make a new directory, making also all of its parent directories that doesn't exist. 79 | function make_dir(path) 80 | if lfs.attributes(path) then 81 | return true 82 | else 83 | local par_dir = parent_dir(path) 84 | if par_dir then 85 | assert(make_dir(par_dir)) 86 | end 87 | return lfs.mkdir(path) 88 | end 89 | end 90 | 91 | 92 | -- Reader class 93 | -- adapted from Penlight: https://raw.github.com/stevedonovan/Penlight/master/lua/pl/stringio.lua 94 | 95 | local SR = {} 96 | SR.__index = SR 97 | 98 | function SR:_read(fmt) 99 | local i,str = self.i,self.str 100 | local sz = #str 101 | if i > sz then return nil, "past end of file" end 102 | local res 103 | if fmt == '*l' or fmt == '*L' then 104 | local idx = str:find('\n',i) or (sz+1) 105 | res = str:sub(i,fmt == '*l' and idx-1 or idx) 106 | self.i = idx+1 107 | elseif fmt == '*a' then 108 | res = str:sub(i) 109 | self.i = sz+1 110 | elseif fmt == '*n' then 111 | local _,i2,i2,idx 112 | _,idx = str:find ('%s*%d+',i) 113 | _,i2 = str:find ('^%.%d+',idx+1) 114 | if i2 then idx = i2 end 115 | _,i2 = str:find ('^[eE][%+%-]*%d+',idx+1) 116 | if i2 then idx = i2 end 117 | local val = str:sub(i,idx) 118 | res = tonumber(val) 119 | self.i = idx+1 120 | elseif type(fmt) == 'number' then 121 | res = str:sub(i,i+fmt-1) 122 | self.i = i + fmt 123 | else 124 | error("bad read format",2) 125 | end 126 | return res 127 | end 128 | 129 | function SR:read(...) 130 | if select('#',...) == 0 then 131 | return self:_read('*l') 132 | else 133 | local res, fmts = {},{...} 134 | for i = 1, #fmts do 135 | res[i] = self:_read(fmts[i]) 136 | end 137 | return unpack(res) 138 | end 139 | end 140 | 141 | function SR:seek(whence,offset) 142 | local base 143 | whence = whence or 'cur' 144 | offset = offset or 0 145 | if whence == 'set' then 146 | base = 1 147 | elseif whence == 'cur' then 148 | base = self.i 149 | elseif whence == 'end' then 150 | base = #self.str 151 | end 152 | self.i = base + offset 153 | return self.i 154 | end 155 | 156 | function SR:close() -- for compatibility only 157 | end 158 | 159 | --- create a file-like object for reading from a given string. 160 | -- @param s The input string. 161 | function reader(s) 162 | return setmetatable({str=s,i=1},SR) 163 | end 164 | 165 | 166 | -- decompress the file and return a handle to temporary uncompressed file 167 | function decompressed(path) 168 | local fi = assert(io.open(path, 'rb')) 169 | local result = {} 170 | 171 | local z = inflate() 172 | repeat 173 | local str = fi:read(BUF_SIZE) 174 | local data = z(str) 175 | if type(data) == 'string' then 176 | result[#result+1] = data 177 | else print('!!!', data) end 178 | until not str 179 | fi:close() 180 | 181 | return reader(table.concat(result)) 182 | end 183 | 184 | -- reads until the byte \0, consumes it and returns the string up to the \0 185 | function read_until_nul(f) 186 | local t = {} 187 | repeat 188 | local c = f:read(1) 189 | if c and c ~= '\0' then t[#t+1] = c end 190 | until not c or c == '\0' 191 | if #t > 0 then 192 | return table.concat(t) 193 | else 194 | return nil 195 | end 196 | end 197 | 198 | -- converts a string to lowercase hex 199 | function to_hex(s) 200 | return (s:gsub('.', function(c) 201 | return string.format('%02x', string.byte(c)) 202 | end)) 203 | end 204 | 205 | -- converts a string from hex to binary 206 | function from_hex(s) 207 | return (s:gsub('..', function(cc) 208 | return string.char(tonumber(cc, 16)) 209 | end)) 210 | end 211 | 212 | -- always returns readable (hex) hash 213 | function readable_sha(s) 214 | if #s ~= 40 then return to_hex(s) 215 | else return s end 216 | end 217 | 218 | -- always returns binary hash 219 | function binary_sha(s) 220 | if #s ~= 20 then return from_hex(s) 221 | else return s end 222 | end 223 | 224 | function object_sha(data, len, type) 225 | local header = type .. ' ' .. len .. '\0' 226 | local res = sha(header .. data) 227 | return res 228 | end 229 | 230 | function deflate(data) 231 | local c = deflate() 232 | return c(data, "finish") 233 | end 234 | -------------------------------------------------------------------------------- /src/bit.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Lua BitOp -- a bit operations library for Lua 5.1/5.2. 3 | ** http://bitop.luajit.org/ 4 | ** 5 | ** Copyright (C) 2008-2012 Mike Pall. All rights reserved. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining 8 | ** a copy of this software and associated documentation files (the 9 | ** "Software"), to deal in the Software without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Software, and to 12 | ** permit persons to whom the Software is furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be 16 | ** included in all copies or substantial portions of the Software. 17 | ** 18 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | ** 26 | ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 27 | */ 28 | 29 | #define LUA_BITOP_VERSION "1.0.2" 30 | 31 | #define LUA_LIB 32 | #include "lua.h" 33 | #include "lauxlib.h" 34 | 35 | #if (LUA_VERSION_NUM == 502) 36 | #undef luaL_register 37 | #define luaL_register(L,n,f) \ 38 | { if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); } 39 | 40 | #endif 41 | 42 | #ifdef _MSC_VER 43 | /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ 44 | typedef __int32 int32_t; 45 | typedef unsigned __int32 uint32_t; 46 | typedef unsigned __int64 uint64_t; 47 | #else 48 | #include 49 | #endif 50 | 51 | typedef int32_t SBits; 52 | typedef uint32_t UBits; 53 | 54 | typedef union { 55 | lua_Number n; 56 | #ifdef LUA_NUMBER_DOUBLE 57 | uint64_t b; 58 | #else 59 | UBits b; 60 | #endif 61 | } BitNum; 62 | 63 | /* Convert argument to bit type. */ 64 | static UBits barg(lua_State *L, int idx) 65 | { 66 | BitNum bn; 67 | UBits b; 68 | #if LUA_VERSION_NUM < 502 69 | bn.n = lua_tonumber(L, idx); 70 | #else 71 | bn.n = luaL_checknumber(L, idx); 72 | #endif 73 | #if defined(LUA_NUMBER_DOUBLE) 74 | bn.n += 6755399441055744.0; /* 2^52+2^51 */ 75 | #ifdef SWAPPED_DOUBLE 76 | b = (UBits)(bn.b >> 32); 77 | #else 78 | b = (UBits)bn.b; 79 | #endif 80 | #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ 81 | defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ 82 | defined(LUA_NUMBER_LLONG) 83 | if (sizeof(UBits) == sizeof(lua_Number)) 84 | b = bn.b; 85 | else 86 | b = (UBits)(SBits)bn.n; 87 | #elif defined(LUA_NUMBER_FLOAT) 88 | #error "A 'float' lua_Number type is incompatible with this library" 89 | #else 90 | #error "Unknown number type, check LUA_NUMBER_* in luaconf.h" 91 | #endif 92 | #if LUA_VERSION_NUM < 502 93 | if (b == 0 && !lua_isnumber(L, idx)) { 94 | luaL_typerror(L, idx, "number"); 95 | } 96 | #endif 97 | return b; 98 | } 99 | 100 | /* Return bit type. */ 101 | #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; 102 | 103 | static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } 104 | static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } 105 | 106 | #define BIT_OP(func, opr) \ 107 | static int func(lua_State *L) { int i; UBits b = barg(L, 1); \ 108 | for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) } 109 | BIT_OP(bit_band, &=) 110 | BIT_OP(bit_bor, |=) 111 | BIT_OP(bit_bxor, ^=) 112 | 113 | #define bshl(b, n) (b << n) 114 | #define bshr(b, n) (b >> n) 115 | #define bsar(b, n) ((SBits)b >> n) 116 | #define brol(b, n) ((b << n) | (b >> (32-n))) 117 | #define bror(b, n) ((b << (32-n)) | (b >> n)) 118 | #define BIT_SH(func, fn) \ 119 | static int func(lua_State *L) { \ 120 | UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } 121 | BIT_SH(bit_lshift, bshl) 122 | BIT_SH(bit_rshift, bshr) 123 | BIT_SH(bit_arshift, bsar) 124 | BIT_SH(bit_rol, brol) 125 | BIT_SH(bit_ror, bror) 126 | 127 | static int bit_bswap(lua_State *L) 128 | { 129 | UBits b = barg(L, 1); 130 | b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); 131 | BRET(b) 132 | } 133 | 134 | static int bit_tohex(lua_State *L) 135 | { 136 | UBits b = barg(L, 1); 137 | SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); 138 | const char *hexdigits = "0123456789abcdef"; 139 | char buf[8]; 140 | int i; 141 | if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } 142 | if (n > 8) n = 8; 143 | for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; } 144 | lua_pushlstring(L, buf, (size_t)n); 145 | return 1; 146 | } 147 | 148 | static const struct luaL_Reg bit_funcs[] = { 149 | { "tobit", bit_tobit }, 150 | { "bnot", bit_bnot }, 151 | { "band", bit_band }, 152 | { "bor", bit_bor }, 153 | { "bxor", bit_bxor }, 154 | { "lshift", bit_lshift }, 155 | { "rshift", bit_rshift }, 156 | { "arshift", bit_arshift }, 157 | { "rol", bit_rol }, 158 | { "ror", bit_ror }, 159 | { "bswap", bit_bswap }, 160 | { "tohex", bit_tohex }, 161 | { NULL, NULL } 162 | }; 163 | 164 | /* Signed right-shifts are implementation-defined per C89/C99. 165 | ** But the de facto standard are arithmetic right-shifts on two's 166 | ** complement CPUs. This behaviour is required here, so test for it. 167 | */ 168 | #define BAD_SAR (bsar(-8, 2) != (SBits)-2) 169 | 170 | void init_bit(lua_State *L) 171 | { 172 | UBits b; 173 | lua_pushnumber(L, (lua_Number)1437217655L); 174 | b = barg(L, -1); 175 | if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */ 176 | const char *msg = "compiled with incompatible luaconf.h"; 177 | #ifdef LUA_NUMBER_DOUBLE 178 | #ifdef _WIN32 179 | if (b == (UBits)1610612736L) 180 | msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; 181 | #endif 182 | if (b == (UBits)1127743488L) 183 | msg = "not compiled with SWAPPED_DOUBLE"; 184 | #endif 185 | if (BAD_SAR) 186 | msg = "arithmetic right-shift broken"; 187 | luaL_error(L, "bit library self-test failed (%s)", msg); 188 | } 189 | lua_pop(L, 1); 190 | luaL_register(L, NULL, bit_funcs); 191 | } 192 | 193 | -------------------------------------------------------------------------------- /src/core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void init_bit(lua_State *L); 6 | void init_sha(lua_State *L); 7 | void init_zlib(lua_State *L); 8 | 9 | int luaopen_git_core(lua_State *L) 10 | { 11 | lua_createtable(L, 0, 0); 12 | init_bit(L); 13 | init_sha(L); 14 | init_zlib(L); 15 | return 1; 16 | } 17 | -------------------------------------------------------------------------------- /src/sha.c: -------------------------------------------------------------------------------- 1 | /* 2 | SHA-1 in C 3 | By Steve Reid 4 | 100% Public Domain 5 | 6 | ----------------- 7 | Modified 7/98 8 | By James H. Brown 9 | Still 100% Public Domain 10 | 11 | Corrected a problem which generated improper hash values on 16 bit machines 12 | Routine SHA1Update changed from 13 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int 14 | len) 15 | to 16 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned 17 | long len) 18 | 19 | The 'len' parameter was declared an int which works fine on 32 bit machines. 20 | However, on 16 bit machines an int is too small for the shifts being done 21 | against 22 | it. This caused the hash function to generate incorrect values if len was 23 | greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). 24 | 25 | Since the file IO in main() reads 16K at a time, any file 8K or larger would 26 | be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million 27 | "a"s). 28 | 29 | I also changed the declaration of variables i & j in SHA1Update to 30 | unsigned long from unsigned int for the same reason. 31 | 32 | These changes should make no difference to any 32 bit implementations since 33 | an 34 | int and a long are the same size in those environments. 35 | 36 | -- 37 | I also corrected a few compiler warnings generated by Borland C. 38 | 1. Added #include for exit() prototype 39 | 2. Removed unused variable 'j' in SHA1Final 40 | 3. Changed exit(0) to return(0) at end of main. 41 | 42 | ALL changes I made can be located by searching for comments containing 'JHB' 43 | ----------------- 44 | Modified 8/98 45 | By Steve Reid 46 | Still 100% public domain 47 | 48 | 1- Removed #include and used return() instead of exit() 49 | 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 50 | 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net 51 | 52 | ----------------- 53 | Modified 4/01 54 | By Saul Kravitz 55 | Still 100% PD 56 | Modified to run on Compaq Alpha hardware. 57 | 58 | ----------------- 59 | Modified 07/2002 60 | By Ralph Giles 61 | Still 100% public domain 62 | modified for use with stdint types, autoconf 63 | code cleanup, removed attribution comments 64 | switched SHA1Final() argument order for consistency 65 | use SHA1_ prefix for public api 66 | move public api to sha1.h 67 | */ 68 | 69 | /* 70 | Test Vectors (from FIPS PUB 180-1) 71 | "abc" 72 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 73 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 74 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 75 | A million repetitions of "a" 76 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 77 | */ 78 | 79 | /* #define SHA1HANDSOFF */ 80 | 81 | #include 82 | #include 83 | 84 | #ifdef _MSC_VER 85 | typedef char int8_t; 86 | typedef unsigned char uint8_t; 87 | typedef __int32 int32_t; 88 | typedef unsigned __int32 uint32_t; 89 | typedef __int64 int64_t; 90 | typedef unsigned __int64 uint64_t; 91 | #else 92 | # include 93 | #endif 94 | 95 | /* public api for steve reid's public domain SHA-1 implementation */ 96 | /* this file is in the public domain */ 97 | 98 | typedef struct { 99 | uint32_t state[5]; 100 | uint32_t count[2]; 101 | uint8_t buffer[64]; 102 | } SHA1_CTX; 103 | 104 | #define SHA1_DIGEST_SIZE 20 105 | 106 | void SHA1_Init(SHA1_CTX* context); 107 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len); 108 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]); 109 | 110 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); 111 | 112 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 113 | 114 | /* blk0() and blk() perform the initial expand. */ 115 | /* I got the idea of expanding during the round function from SSLeay */ 116 | /* FIXME: can we do this in an endian-proof way? */ 117 | #ifdef WORDS_BIGENDIAN 118 | #define blk0(i) block->l[i] 119 | #else 120 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 121 | |(rol(block->l[i],8)&0x00FF00FF)) 122 | #endif 123 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 124 | ^block->l[(i+2)&15]^block->l[i&15],1)) 125 | 126 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 127 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 128 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 129 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 130 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 131 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 132 | 133 | 134 | #ifdef VERBOSE /* SAK */ 135 | void SHAPrintContext(SHA1_CTX *context, char *msg){ 136 | printf("%s (%d,%d) %x %x %x %x %x\n", 137 | msg, 138 | context->count[0], context->count[1], 139 | context->state[0], 140 | context->state[1], 141 | context->state[2], 142 | context->state[3], 143 | context->state[4]); 144 | } 145 | #endif /* VERBOSE */ 146 | 147 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 148 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) 149 | { 150 | uint32_t a, b, c, d, e; 151 | typedef union { 152 | uint8_t c[64]; 153 | uint32_t l[16]; 154 | } CHAR64LONG16; 155 | CHAR64LONG16* block; 156 | 157 | #ifdef SHA1HANDSOFF 158 | static uint8_t workspace[64]; 159 | block = (CHAR64LONG16*)workspace; 160 | memcpy(block, buffer, 64); 161 | #else 162 | block = (CHAR64LONG16*)buffer; 163 | #endif 164 | 165 | /* Copy context->state[] to working vars */ 166 | a = state[0]; 167 | b = state[1]; 168 | c = state[2]; 169 | d = state[3]; 170 | e = state[4]; 171 | 172 | /* 4 rounds of 20 operations each. Loop unrolled. */ 173 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 174 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 175 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 176 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 177 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 178 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 179 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 180 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 181 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 182 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 183 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 184 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 185 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 186 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 187 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 188 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 189 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 190 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 191 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 192 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 193 | 194 | /* Add the working vars back into context.state[] */ 195 | state[0] += a; 196 | state[1] += b; 197 | state[2] += c; 198 | state[3] += d; 199 | state[4] += e; 200 | 201 | /* Wipe variables */ 202 | a = b = c = d = e = 0; 203 | } 204 | 205 | 206 | /* SHA1Init - Initialize new context */ 207 | void SHA1_Init(SHA1_CTX* context) 208 | { 209 | /* SHA1 initialization constants */ 210 | context->state[0] = 0x67452301; 211 | context->state[1] = 0xEFCDAB89; 212 | context->state[2] = 0x98BADCFE; 213 | context->state[3] = 0x10325476; 214 | context->state[4] = 0xC3D2E1F0; 215 | context->count[0] = context->count[1] = 0; 216 | } 217 | 218 | 219 | /* Run your data through this. */ 220 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) 221 | { 222 | size_t i, j; 223 | 224 | #ifdef VERBOSE 225 | SHAPrintContext(context, "before"); 226 | #endif 227 | 228 | j = (context->count[0] >> 3) & 63; 229 | if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; 230 | context->count[1] += (len >> 29); 231 | if ((j + len) > 63) { 232 | memcpy(&context->buffer[j], data, (i = 64-j)); 233 | SHA1_Transform(context->state, context->buffer); 234 | for ( ; i + 63 < len; i += 64) { 235 | SHA1_Transform(context->state, data + i); 236 | } 237 | j = 0; 238 | } 239 | else i = 0; 240 | memcpy(&context->buffer[j], &data[i], len - i); 241 | 242 | #ifdef VERBOSE 243 | SHAPrintContext(context, "after "); 244 | #endif 245 | } 246 | 247 | 248 | /* Add padding and return the message digest. */ 249 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) 250 | { 251 | uint32_t i; 252 | uint8_t finalcount[8]; 253 | 254 | for (i = 0; i < 8; i++) { 255 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 256 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 257 | } 258 | SHA1_Update(context, (uint8_t *)"\200", 1); 259 | while ((context->count[0] & 504) != 448) { 260 | SHA1_Update(context, (uint8_t *)"\0", 1); 261 | } 262 | SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ 263 | for (i = 0; i < SHA1_DIGEST_SIZE; i++) { 264 | digest[i] = (uint8_t) 265 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 266 | } 267 | 268 | /* Wipe variables */ 269 | i = 0; 270 | memset(context->buffer, 0, 64); 271 | memset(context->state, 0, 20); 272 | memset(context->count, 0, 8); 273 | memset(finalcount, 0, 8); /* SWR */ 274 | 275 | #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ 276 | SHA1_Transform(context->state, context->buffer); 277 | #endif 278 | } 279 | 280 | /* Lua binding */ 281 | 282 | #include 283 | #include 284 | #include 285 | 286 | int sha_digest(lua_State *L) 287 | { 288 | size_t len = 0; 289 | const char *data = luaL_checklstring(L, 1, &len); 290 | char digest[20] = {0}; 291 | SHA1_CTX sha; 292 | 293 | SHA1_Init(&sha); 294 | SHA1_Update(&sha, data, len); 295 | SHA1_Final(&sha, digest); 296 | 297 | lua_pushlstring(L, digest, 20); 298 | 299 | return 1; 300 | } 301 | 302 | void init_sha(lua_State *L) { 303 | lua_pushcfunction(L, sha_digest); 304 | lua_setfield(L, -2, "sha"); 305 | } 306 | -------------------------------------------------------------------------------- /src/zlib.c: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * Author : Brian Maher 3 | * Library : lua_zlib - Lua 5.1 interface to zlib 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2009 Brian Maher 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | **********************************************************************/ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | /* 36 | * ** compatibility with Lua 5.2 37 | * */ 38 | #if (LUA_VERSION_NUM == 502) 39 | #undef luaL_register 40 | #define luaL_register(L,n,f) \ 41 | { if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); } 42 | 43 | #endif 44 | 45 | 46 | static int lz_deflate(lua_State *L); 47 | static int lz_deflate_delete(lua_State *L); 48 | static int lz_inflate_delete(lua_State *L); 49 | static int lz_inflate(lua_State *L); 50 | 51 | static int lz_version(lua_State *L) { 52 | const char* version = zlibVersion(); 53 | int count = strlen(version) + 1; 54 | char* cur = (char*)memcpy(lua_newuserdata(L, count), 55 | version, count); 56 | 57 | count = 0; 58 | while ( *cur ) { 59 | char* begin = cur; 60 | /* Find all digits: */ 61 | while ( isdigit(*cur) ) cur++; 62 | if ( begin != cur ) { 63 | int is_end = *cur == '\0'; 64 | *cur = '\0'; 65 | lua_pushnumber(L, atoi(begin)); 66 | count++; 67 | if ( is_end ) break; 68 | cur++; 69 | } 70 | while ( *cur && ! isdigit(*cur) ) cur++; 71 | } 72 | 73 | return count; 74 | } 75 | 76 | static int lz_assert(lua_State *L, int result, const z_stream* stream, const char* file, int line) { 77 | /* Both of these are "normal" return codes: */ 78 | if ( result == Z_OK || result == Z_STREAM_END ) return result; 79 | switch ( result ) { 80 | case Z_NEED_DICT: 81 | lua_pushfstring(L, "RequiresDictionary: input stream requires a dictionary to be deflated (%s) at %s line %d", 82 | stream->msg, file, line); 83 | break; 84 | case Z_STREAM_ERROR: 85 | lua_pushfstring(L, "InternalError: inconsistent internal zlib stream (%s) at %s line %d", 86 | stream->msg, file, line); 87 | break; 88 | case Z_DATA_ERROR: 89 | lua_pushfstring(L, "InvalidInput: input string does not conform to zlib format or checksum failed at %s line %d", 90 | file, line); 91 | break; 92 | case Z_MEM_ERROR: 93 | lua_pushfstring(L, "OutOfMemory: not enough memory (%s) at %s line %d", 94 | stream->msg, file, line); 95 | break; 96 | case Z_BUF_ERROR: 97 | lua_pushfstring(L, "InternalError: no progress possible (%s) at %s line %d", 98 | stream->msg, file, line); 99 | break; 100 | case Z_VERSION_ERROR: 101 | lua_pushfstring(L, "IncompatibleLibrary: built with version %s, but dynamically linked with version %s (%s) at %s line %d", 102 | ZLIB_VERSION, zlibVersion(), stream->msg, file, line); 103 | break; 104 | default: 105 | lua_pushfstring(L, "ZLibError: unknown code %d (%s) at %s line %d", 106 | result, stream->msg, file, line); 107 | } 108 | lua_error(L); 109 | return result; 110 | } 111 | 112 | /** 113 | * @upvalue z_stream - Memory for the z_stream. 114 | * @upvalue remainder - Any remainder from the last deflate call. 115 | * 116 | * @param string - "print" to deflate stream. 117 | * @param int - flush output buffer? Z_SYNC_FLUSH, Z_FULL_FLUSH, or Z_FINISH. 118 | * 119 | * if no params, terminates the stream (as if we got empty string and Z_FINISH). 120 | */ 121 | static int lz_filter_impl(lua_State *L, int (*filter)(z_streamp, int), int (*end)(z_streamp), char* name) { 122 | int flush = Z_NO_FLUSH, result; 123 | z_stream* stream; 124 | luaL_Buffer buff; 125 | size_t avail_in; 126 | 127 | if ( filter == deflate ) { 128 | const char *const opts[] = { "none", "sync", "full", "finish", NULL }; 129 | flush = luaL_checkoption(L, 2, opts[0], opts); 130 | if ( flush ) flush++; 131 | /* Z_NO_FLUSH(0) Z_SYNC_FLUSH(2), Z_FULL_FLUSH(3), Z_FINISH (4) */ 132 | 133 | /* No arguments or nil, we are terminating the stream: */ 134 | if ( lua_gettop(L) == 0 || lua_isnil(L, 1) ) { 135 | flush = Z_FINISH; 136 | } 137 | } 138 | 139 | stream = (z_stream*)lua_touserdata(L, lua_upvalueindex(1)); 140 | if ( stream == NULL ) { 141 | if ( lua_gettop(L) >= 1 && lua_isstring(L, 1) ) { 142 | lua_pushfstring(L, "IllegalState: calling %s function when stream was previously closed", name); 143 | lua_error(L); 144 | } 145 | lua_pushstring(L, ""); 146 | lua_pushboolean(L, 1); 147 | return 2; /* Ignore duplicate calls to "close". */ 148 | } 149 | 150 | luaL_buffinit(L, &buff); 151 | 152 | if ( lua_gettop(L) > 1 ) lua_pushvalue(L, 1); 153 | 154 | if ( lua_isstring(L, lua_upvalueindex(2)) ) { 155 | lua_pushvalue(L, lua_upvalueindex(2)); 156 | if ( lua_gettop(L) > 1 && lua_isstring(L, -2) ) { 157 | lua_concat(L, 2); 158 | } 159 | } 160 | 161 | /* Do the actual deflate'ing: */ 162 | stream->next_in = (unsigned char*)lua_tolstring(L, -1, &avail_in); 163 | stream->avail_in = avail_in; 164 | if ( ! stream->avail_in && ! flush ) { 165 | /* Passed empty string, make it a noop instead of erroring out. */ 166 | lua_pushstring(L, ""); 167 | lua_pushboolean(L, 0); 168 | lua_pushinteger(L, stream->total_in); 169 | lua_pushinteger(L, stream->total_out); 170 | return 4; 171 | } 172 | 173 | do { 174 | stream->next_out = (unsigned char*)luaL_prepbuffer(&buff); 175 | stream->avail_out = LUAL_BUFFERSIZE; 176 | result = filter(stream, flush); 177 | if ( Z_BUF_ERROR != result ) { 178 | /* Ignore Z_BUF_ERROR since that just indicates that we 179 | * need a larger buffer in order to proceed. Thanks to 180 | * Tobias Markmann for finding this bug! 181 | */ 182 | lz_assert(L, result, stream, __FILE__, __LINE__); 183 | } 184 | luaL_addsize(&buff, LUAL_BUFFERSIZE - stream->avail_out); 185 | } while ( stream->avail_out == 0 ); 186 | 187 | /* Need to do this before we alter the stack: */ 188 | luaL_pushresult(&buff); 189 | 190 | /* Save remainder in lua_upvalueindex(2): */ 191 | if ( NULL != stream->next_in ) { 192 | lua_pushlstring(L, (char*)stream->next_in, stream->avail_in); 193 | lua_replace(L, lua_upvalueindex(2)); 194 | } 195 | 196 | /* "close" the stream/remove finalizer: */ 197 | if ( result == Z_STREAM_END ) { 198 | /* Clear-out the metatable so end is not called twice: */ 199 | lua_pushnil(L); 200 | lua_setmetatable(L, lua_upvalueindex(1)); 201 | 202 | /* nil the upvalue: */ 203 | lua_pushnil(L); 204 | lua_replace(L, lua_upvalueindex(1)); 205 | 206 | /* Close the stream: */ 207 | lz_assert(L, end(stream), stream, __FILE__, __LINE__); 208 | 209 | lua_pushboolean(L, 1); 210 | } else { 211 | lua_pushboolean(L, 0); 212 | } 213 | lua_pushinteger(L, stream->total_in); 214 | lua_pushinteger(L, stream->total_out); 215 | return 4; 216 | } 217 | 218 | static void lz_create_deflate_mt(lua_State *L) { 219 | luaL_newmetatable(L, "lz.deflate.meta"); /* {} */ 220 | 221 | lua_pushcfunction(L, lz_deflate_delete); 222 | lua_setfield(L, -2, "__gc"); 223 | 224 | lua_pop(L, 1); /* */ 225 | } 226 | 227 | static int lz_deflate_new(lua_State *L) { 228 | int level = luaL_optint(L, 1, Z_DEFAULT_COMPRESSION); 229 | 230 | /* Allocate the stream: */ 231 | z_stream* stream = (z_stream*)lua_newuserdata(L, sizeof(z_stream)); 232 | 233 | stream->zalloc = Z_NULL; 234 | stream->zfree = Z_NULL; 235 | lz_assert(L, deflateInit(stream, level), stream, __FILE__, __LINE__); 236 | 237 | /* Don't allow destructor to execute unless deflateInit was successful: */ 238 | luaL_getmetatable(L, "lz.deflate.meta"); 239 | lua_setmetatable(L, -2); 240 | 241 | lua_pushnil(L); 242 | lua_pushcclosure(L, lz_deflate, 2); 243 | return 1; 244 | } 245 | 246 | static int lz_deflate(lua_State *L) { 247 | return lz_filter_impl(L, deflate, deflateEnd, "deflate"); 248 | } 249 | 250 | static int lz_deflate_delete(lua_State *L) { 251 | z_stream* stream = (z_stream*)lua_touserdata(L, 1); 252 | 253 | /* Ignore errors. */ 254 | deflateEnd(stream); 255 | 256 | return 0; 257 | } 258 | 259 | 260 | static void lz_create_inflate_mt(lua_State *L) { 261 | luaL_newmetatable(L, "lz.inflate.meta"); /* {} */ 262 | 263 | lua_pushcfunction(L, lz_inflate_delete); 264 | lua_setfield(L, -2, "__gc"); 265 | 266 | lua_pop(L, 1); /* */ 267 | } 268 | 269 | static int lz_inflate_new(lua_State *L) { 270 | /* Allocate the stream */ 271 | z_stream* stream = (z_stream*)lua_newuserdata(L, sizeof(z_stream)); 272 | 273 | /* By default, we will do gzip header detection w/ max window size */ 274 | int window_size = lua_isnumber(L, 1) ? lua_tonumber(L, 1) : MAX_WBITS + 32; 275 | 276 | stream->zalloc = Z_NULL; 277 | stream->zfree = Z_NULL; 278 | stream->next_in = Z_NULL; 279 | stream->avail_in = 0; 280 | 281 | lz_assert(L, inflateInit2(stream, window_size), stream, __FILE__, __LINE__); 282 | 283 | /* Don't allow destructor to execute unless deflateInit was successful: */ 284 | luaL_getmetatable(L, "lz.inflate.meta"); 285 | lua_setmetatable(L, -2); 286 | 287 | lua_pushnil(L); 288 | lua_pushcclosure(L, lz_inflate, 2); 289 | return 1; 290 | } 291 | 292 | static int lz_inflate(lua_State *L) { 293 | return lz_filter_impl(L, inflate, inflateEnd, "inflate"); 294 | } 295 | 296 | static int lz_inflate_delete(lua_State *L) { 297 | z_stream* stream = (z_stream*)lua_touserdata(L, 1); 298 | 299 | /* Ignore errors: */ 300 | inflateEnd(stream); 301 | 302 | return 0; 303 | } 304 | 305 | static const luaL_Reg zlib_functions[] = { 306 | { "deflate", lz_deflate_new }, 307 | { "inflate", lz_inflate_new }, 308 | { "version", lz_version }, 309 | { NULL, NULL } 310 | }; 311 | 312 | #define SETLITERAL(n,v) (lua_pushliteral(L, n), lua_pushliteral(L, v), lua_settable(L, -3)) 313 | #define SETINT(n,v) (lua_pushliteral(L, n), lua_pushinteger(L, v), lua_settable(L, -3)) 314 | 315 | void init_zlib(lua_State * L) { 316 | lz_create_deflate_mt(L); 317 | lz_create_inflate_mt(L); 318 | luaL_register(L, NULL, zlib_functions); 319 | } 320 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';lua/?.lua' 2 | package.cpath = package.cpath .. ';b/?.so' 3 | 4 | require 'git' 5 | 6 | local r = git.repo.open('.') 7 | 8 | local c = r:head() 9 | 10 | print('Commit', c.id) 11 | print(c.author) 12 | print(c.committer) 13 | print(c.message) 14 | print() 15 | 16 | c:checkout('tst') 17 | 18 | local parent = c.parents[1] 19 | local pc = r:commit(parent) 20 | 21 | print('Commit', pc.id) 22 | print(pc.author) 23 | print(pc.committer) 24 | print(pc.message) 25 | print() 26 | 27 | local tree = pc:tree() 28 | tree:walk(function(entry, entry_path, type) 29 | print(type, entry_path) 30 | end) 31 | 32 | print() 33 | print(tree['git.lua']:content()) 34 | 35 | -- find the initial commit 36 | while #c.parents > 0 do 37 | c = r:commit(c.parents[1]) 38 | end 39 | 40 | print(c.message) 41 | 42 | c:tree():walk(function(entry, entry_path, type) 43 | print(type, entry_path) 44 | end) 45 | 46 | os.execute('rm -rf tst2') 47 | os.execute('mkdir tst2') 48 | os.execute('cd tst2 && git init') 49 | r = git.repo.open('tst2') 50 | git.protocol.fetch('git://github.com/mkottman/lua-git.git', r) -------------------------------------------------------------------------------- /test_create.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';lua/?.lua' 2 | package.cpath = package.cpath .. ';b/?.so' 3 | 4 | require 'git' 5 | 6 | os.execute('rm -rf /tmp/test') 7 | git.repo.create('/tmp/test') 8 | 9 | local R = git.repo.open('/tmp/test') 10 | local pack, sha = git.protocol.fetch('git://github.com/LuaDist/Repository.git', R, 'refs/heads/master') 11 | R:checkout(sha) 12 | -------------------------------------------------------------------------------- /test_fetch-win.lua: -------------------------------------------------------------------------------- 1 | require 'git' 2 | 3 | os.execute('rd /S /Q C:\\tmp\\test.git') 4 | os.execute('rd /S /Q C:\\tmp\\extracted') 5 | 6 | R = git.repo.create('C:\\tmp\\test.git') 7 | local pack, sha = git.protocol.fetch('git://github.com/mkottman/lua-git.git', R, 'refs/heads/master') 8 | R:checkout(sha, 'C:\\tmp\\extracted') 9 | -------------------------------------------------------------------------------- /test_fetch.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';lua/?.lua' 2 | package.cpath = package.cpath .. ';b/?.so' 3 | 4 | require 'git' 5 | 6 | local repoUrl = arg[1] or 'git://github.com/mkottman/lua-git.git' 7 | local ref = arg[2] or 'refs/heads/master' 8 | 9 | os.execute('rm -rf /tmp/test.git') 10 | os.execute('rm -rf /tmp/extracted') 11 | 12 | R = git.repo.create('/tmp/test.git') 13 | local pack, sha = git.protocol.fetch(repoUrl, R, ref) 14 | R:checkout(sha, '/tmp/extracted') 15 | -------------------------------------------------------------------------------- /test_fetch_stress.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';lua/?.lua' 2 | package.cpath = package.cpath .. ';b/?.so' 3 | 4 | require 'git' 5 | 6 | local n = arg[1] or 100 7 | local repoUrl = arg[2] or 'git://github.com/mkottman/lua-git.git' 8 | local ref = arg[3] or 'refs/heads/master' 9 | 10 | for i=1,n do 11 | os.execute('rm -rf /tmp/test.git') 12 | os.execute('rm -rf /tmp/extracted') 13 | 14 | R = git.repo.create('/tmp/test.git') 15 | local pack, sha = git.protocol.fetch(repoUrl, R, ref) 16 | R:checkout(sha, '/tmp/extracted') 17 | R:close() 18 | end 19 | -------------------------------------------------------------------------------- /test_remotes.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';lua/?.lua' 2 | package.cpath = package.cpath .. ';b/?.so' 3 | 4 | require 'git' 5 | 6 | local repoUrl = arg[1] or 'git://github.com/mkottman/lua-git.git' 7 | 8 | local refs = git.protocol.remotes(repoUrl) 9 | for name, sha in pairs(refs) do 10 | print(name, sha) 11 | end 12 | --------------------------------------------------------------------------------