The lg2
package is a binding to the libgit2 library. It
is only intended as a demonstration of using CFFI to wrap a fairly substantial
shared library (more than 800 functions) and thus lacks a comprehensive test suite
though some basic sanity checks are in the tests directory. Use in
production will require client applications to write their own.
The source repository is at https://github.com/apnadkarni/tcl-libgit2.
The package distribution is available from https://sourceforge.net/projects/magicsplat/files/lg2/.
The commands in official git implementation can be divided into two categories:
-
The plumbing commands like
hash-object
,write-tree
etc. which implement the low level operations. -
The porcelain commands, like
git-clone
,git-commit
etc. that are invoked by the user in daily usage and are built on top of the plumbing commands.
The libgit2
API implements the equivalent of the plumbing commands and
accordingly so does the lg2
package. Be warned that using the package requires
understanding the libgit2
API which in turn requires understanding git and its
internal structures. The examples
directory contains implementations of
simpler versions of the high level git
porcelain commands that illustrate the
use of the plumbing commands and can be used as a starting point for more
complete implementations.
The lg2
package has two prerequisites.
-
The Tcl
cffi
extension, version 1.2.0 or later, must be present somewhere in the Tcl package search path. -
The
libgit2
shared library. Supported versions are 1.3.x, 1.4.x and 1.5.x.
See later sections about obtaining these.
Install the distribution into a directory present in Tcl's auto_path
variable.
To use the lg2
package, it must be loaded and then initialized with the
path to the libgit2
shared library. For example,
package require lg2
lg2::lg2_init /lib/x86_64-linux-gnu/libgit2.so
If no path is supplied to lg2_init
it will try to locate libgit2.dll
or
git2.dll
on Windows and libgit2.so
on other platforms under
architecture-specific directories under the package directory. If not found
there, it will just attempt to load using the unqualified shared library name
assuming the library is present in a standard system directory.
The scripts in the examples
directory are standalone scripts that
mimic the porcelain git commands. All support the --help
option to
display some basic help on syntax and options. For example,
> tclsh git-init.tcl --help
Usage: tclsh.exe git-init.tcl [OPTION]... DIRECTORY
Demo of cffi libgit extension. Poor man's git init emulation from libgit2
Translated to Tcl from libgit2/examples/init.c
Mandatory arguments to long options are also mandatory for short options.
-q, --quiet Only print error and warning messages;
all other output will be suppressed.
--bare Create a bare repository.
--initial-commit Create an empty initial commit
--shared=PERMS Set the sharing permissions. PERMS should be
"umask" (default), "group", "all" or an
integer umask value.
--template=DIRECTORY Uses the templates from DIRECTORY.
--help display this help and exit
There is no separate documentation for the lg2
package commands as it (almost)
directly maps the libgit2
API into Tcl. The following libgit2
links
serve as documentation. Make sure you use the libgit2 documentation for
the appropriate version.
A few differences in useage from the libgit2
C API are listed below. Also, the
samples in the examples
directory may be useful as a tutorial for command
usage.
-
All commands are placed in the
lg2
namespace. -
libgit2
prefixes all its functions withgit_
. Thelg2
package adds a few utility commands. These are prefixed withlg2_
. It is thus safe to put thelg2
namespace in the application namespace path as they are unlikely to clash with commands from other packages. -
Most
libgit2
functions return an error code on failure. The corresponding wrapped Tcl commands raise a Tcl exception instead with the error message retrieved fromlibgit2
. -
Since
libgit2
uses function return values to indicate success and failure, it returns the actual function result through an output parameter. Because the wrapped commands use Tcl's exception mechanism, the command result is not needed to indicate success or failure. The commands thus return the output parameter value as the command result. -
Handling of
git_strarray
structs can be slightly tricky because the internal buffers may be allocated bylibgit2
or the application. Thus a "shadow" structlg2_strarray
is defined to distinguish the two use cases. See the comments instrarray.tcl
more information and some utility commands to deal with these. -
Along similar lines, the
git_signature
structure may be returned by alibgit2
function. Thelg2
package defines the script level struct of the same name. Now a pointer to agit_signature
may come either fromlibgit2
function or by from the CFFI allocating commandsgit_signature allocate
orgit_signature new
. It is crucial that the former is freed by a call to thelibgit2
(wrapped) functiongit_signature_free
while the latter must be freed through thegit_signature free
(note one is a wrapped function, other is a call to thefree
method for the CFFIgit_signature
struct command instance). -
libgit2
uses utf-8 string encoding by default. Correspondingly,lg2
defines theSTRING
CFFI alias that is used by most declarations. Some commands allow for strings in arbitrary encodings. These have to be passed as encoded binary strings with the encoding name in a separate parameter. The encoding names are from IANA, not those used by Tcl'sencoding
command. The package therefore provides some utility commandslg2_encoding convertto
andlg2_encoding convertfrom
to help with such conversions. They work like Tcl'sencoding
equivalents except they accept the encoding names used bylibgit2
instead of Tcl encoding names. -
One note to keep in mind with
libgit2
(this is independent of thelg2
package) is that many functions that take file paths as arguments expect/
to be used as the path separator and will not work correctly with\
. Moreover, some expect paths to be relative to top of the working directory.
All of the above are illustrated by the samples in the examples
directory.
The documentation for the cffi
Tcl extension is at https://cffi.magicsplat.com.
Binaries for some platforms and source distributions are downloadable from
https://sourceforge.net/projects/magicsplat/files/cffi. To build from source,
see the file BUILD.md
in the distribution.
The lg2
distribution includes libgit2
DLL's for Windows platforms. See
instructions below to build the DLL's yourself.
On most Unix/Linux systems libgit2
can be installed using the system's package
manager. However, system provided libgit2
packages are often out of date.
Currently the lg2
package supports libgit2
versions 1.3 and 1.4. If the
system package manager does not include libgit2
or includes a different
version, see build instruction below.
Important: Only use supported *release versions of libgit2
as it does
not guarantee ABI compatibility even between minor releases. Moreover, binaries built
from repository sources may not work even if version numbers are the same since
structures may change between releases.
On systems supported by vcpkg
(Windows, Linux, MacOS), it may be used to build libgit2
. For example,
on Windows, to build libgit2
with ssh
support,
vcpkg install "libssh2[zlib]" --triplet x64-windows --recurse
vcpkg install "libgit2[core,ssh]" --triplet x64-windows --recurse
Note to load libgit2
, its dependencies like libssh2
, zlib
, libcrypto
etc. must also be on the path. The vcpkg
commands will automatically download
and build these as required.
You may need to build libgit2
from source if binaries are not available
by one of the previous mentioned means or if you wish for a standalone
libgit2
shared library with all dependencies statically bound.
*Note: the instructions below pertain to libgit2
version 1.4.2. Other versions
may need some tweaks as the build system has some differences between versions.
The sources for libgit2
can be downloaded from
the repository. Make sure to
only download an official release, not a repository snapshot.
Instructions for building libgit2
are given in the README.md
file in the
libgit2
sources. Below are some examples and workarounds for some potential
issues.
The cmake
program is required to do builds.
On Unix-like systems, first ensure the zlib
and libssh2
libraries are
installed using the system's package manager. Then execute the following from a
shell in the top-level libgit2
source directory to build the shared library
and run the test suite.
cmake -S . -B build/ubuntu -DCMAKE_BUILD_TYPE=Release -DDEPRECATE_HARD=ON -DUSE_SSH=ON
cmake --build build/ubuntu
cd build/ubuntu
./libgit2_tests
On Windows, the libgit2
DLL may be build with either the MinGW/GCC tool chain
or Visual Studio.
The corresponding steps for Windows given below are a little more involved
because (a) dependencies need to be installed and (b) as a preference, the build
is configured to statically link the dependencies into the libgit2
DLL so no
additional DLLs need to be distributed.
To build with MinGW-W64/gcc, commands below must be run from a MING64 shell (not the MSYS shell).
First install the dependencies using pacboy
(or the pacman
equivalents)
pacboy sync libssh2-wincng
Note The libssh2
package may be installed in lieu of libssh2-wincng
.
However, that requires the additional openssl libraries while libssh2-wincng
uses native Win32 crypto functions and is preferred for that reason.
Then run the following in the MINGW64 shell to build and test.
cmake -S . -B build/mingw64 -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DDEPRECATE_HARD=ON -DUSE_SSH=ON -DCMAKE_C_STANDARD_LIBRARIES="-lbcrypt -lcrypt32 -lws2_32" -DLIBSSH2_LIBRARIES=libssh2.a -DUSE_BUNDLED_ZLIB=ON -DHAVE_LIBSSH2_MEMORY_CREDENTIALS=1
cmake --build build/mingw64
cd build/mingw64
./libgit2_tests
Note the options used:
-DLIBSSH2_LIBRARIES=libssh2.a
forces static linking tolibssh2
-DUSE_BUNDLED_ZIP=ON
uses the zlib library withinlibgit2
and eliminate the external dependency- The
CMAKE_C_STANDARD_LIBRARIES
andHAVE_LIBSSH2_MEMORY_CREDENTIALS
work around some configuration bugs inlibgit2
that manifest themselves with static linking.
Building a 32-bit version is similar except that the commands must be run
in the MINGW32
shell and not in the MINGW64
one.
cmake -S . -B build/mingw32 -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DDEPRECATE_HARD=ON -DUSE_SSH=ON -DCMAKE_C_STANDARD_LIBRARIES="-lbcrypt -lcrypt32 -lws2_32" -DLIBSSH2_LIBRARIES=libssh2.a -DUSE_BUNDLED_ZLIB=ON -DHAVE_LIBSSH2_MEMORY_CREDENTIALS=1
cmake --build build/mingw32
cd build/mingw32
./libgit2_tests
Since there is no bundled libssh2
with Visual Studio, download its
source distribution and extract it
to a local directory. No need to build it.
Important Comment the line include(SelectSSH)
in src/CMakeLists.txt in the
libgit2
distribution. See Bug 6254.
Then from a Visual Studio 64-bit prompt, run the following commands in the top
level directory of the libgit2
source distribution.
cmake -S . -B build\vs64 -A x64 -DEMBED_SSH_PATH="D:/src/AAThirdparty/C,C++/libssh2-1.10.0" -DUSE_BUNDLED_ZLIB=ON -DDEPRECATE_HARD=ON
cmake --build build/vs64 --config Release
cd build\vs64
libgit2_tests
NOTE: Use forward slashes in -D definitions even for Visual Studio builds.
If your directory is not on the C: drive, you may see a few test failures.
The 32-bit build is similar except that you need to run the commands from a Visual
Studio 32-bit prompt and the -A
option should be left out or take the value
Win32
instead of x64
.
I can only (attempt to) answer questions related to the use of CFFI in this package.
For questions about libgit2
itself, see one of
-
The Git Internals chapter from the official free online Pro Git book.
-
The Git man pages, particularly those in sections 5 and 7, and gitcore-tutorial a tutorial about using the plumbing commands.
-
The github discussions section