Native C CLI for git-overleaf. The current version is the initial
v0.1.0 release.
auth: save a raw Overleaf Cookie header or import one from Firefoxlist: list projects visible to the current cookieclone: download a project snapshot into a new Git repoinit: bind an existing Git repo to an Overleaf projectstatus: inspect cached synchronization and recovery statefetch: refresh the cached remote snapshot without touching local workpull: merge the latest Overleaf snapshot into the current branchpush: upload committedHEAD, optionally replacing remote changesreset: move the branch to the cached remote snapshot
Not implemented yet:
- webdriver/browser authentication
Build and runtime dependencies:
- C11 compiler
cmakepkg-configlibcurljanssonsqlite3gitunzip
Additional dependencies for building and running the tests:
- C++17 compiler
- GoogleTest (
googletest/libgtest-dev)
Coverage additionally requires gcovr.
Install dependencies:
The commands below install the complete build-and-test toolchain. For a CLI-only build, GoogleTest and a separate C++ compiler are not required.
macOS (Homebrew):
xcode-select --install # C/C++ compiler (Clang), if not already installed
brew install cmake pkgconf curl googletest jansson sqlite git unzipFedora:
sudo dnf install -y \
gcc \
gcc-c++ \
make \
cmake \
pkgconf-pkg-config \
libcurl-devel \
gtest-devel \
jansson-devel \
sqlite-devel \
git \
unzipUbuntu:
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
pkg-config \
libcurl4-openssl-dev \
libgtest-dev \
libjansson-dev \
libsqlite3-dev \
git \
unzipBuild the CLI without tests (no C++ compiler or GoogleTest required):
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF
cmake --build buildRun:
./build/git-overleaf --helpBuild and run the tests (requires a C++17 compiler and GoogleTest):
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failureCoverage:
cmake -S . -B build-coverage \
-DCMAKE_BUILD_TYPE=Debug \
-DGIT_OVERLEAF_ENABLE_COVERAGE=ON
cmake --build build-coverage --target coverageSave cookies:
./build/git-overleaf auth \
--cookie 'connect.sid=...; overleaf_session=...' \
--cookie-file ~/.git-overleaf-cookiesImport cookies from Firefox:
./build/git-overleaf auth --from-firefoxTarget a self-hosted Overleaf instance:
./build/git-overleaf --url https://latex.example.edu listClone a project:
./build/git-overleaf clone --project-id PROJECT_ID --project-name 'Project Name'Bind and synchronize an existing repo:
./build/git-overleaf init --project-id PROJECT_ID --repo /path/to/repo
./build/git-overleaf status --repo /path/to/repo
./build/git-overleaf fetch --repo /path/to/repo
./build/git-overleaf pull --repo /path/to/repo
./build/git-overleaf push --repo /path/to/repostatus uses only local Git refs and config, so it needs neither a cookie nor
network access. It reports the relationship among the synchronization base,
committed HEAD, and the last fetched remote snapshot, plus worktree and
pending recovery state.
fetch downloads the current Overleaf snapshot and updates only the
last-observed remote ref. It never changes HEAD, the index, the worktree, the
synchronization base, or pending recovery state. fetch --force and fetch -f
are accepted for Git command-line familiarity but have the same non-destructive
behavior.
Like git pull, pull allows staged, unstaged, and untracked local changes
when Git can merge without overwriting them. Non-overlapping changes remain
in the worktree. If incoming changes would overwrite local changes, Git
rejects the merge and leaves them intact; the remote snapshot is fetched, but
no pending pull is recorded. Commit, stash, or move the overlapping changes
and retry.
If a pull produces merge conflicts, resolve and commit them before running
push. Use pull --abort to abort the active merge and clear the pending pull.
Stale, uncommitted pending-pull metadata can also be cleared this way; an
already committed merge must instead be pushed, updated with another pull, or
discarded explicitly with reset.
Clone and init register a branchless logical remote named overleaf by
default. Choose another name at creation time with --remote NAME. For a
repository created by an older version, migrate it without network access:
./build/git-overleaf register-remote --repo /path/to/repo
# If a real remote named overleaf already exists:
./build/git-overleaf register-remote --remote overleaf-project \
--repo /path/to/repoThe logical remote has no URL, push URL, branch, or refspec. Normal remotes
such as origin remain ordinary Git remotes. git remote rename and
git remote remove work normally, and a removed logical remote is not
automatically recreated.
Reset the current branch to the last fetched snapshot without network access:
./build/git-overleaf reset --repo /path/to/repo
./build/git-overleaf reset --hard --repo /path/to/repoLike git reset, the default mode is mixed: it moves the branch and resets the
index while leaving tracked worktree content as unstaged changes. --hard also
resets tracked worktree files. Both modes preserve untracked and ignored files,
save the previous HEAD under refs/git-overleaf/backups/, and clear pending
sync state only after the reset succeeds.
Push or force-push Overleaf from a bound repo:
./build/git-overleaf push --repo /path/to/repo
./build/git-overleaf push --force-with-lease --repo /path/to/repo
./build/git-overleaf push --force --repo /path/to/repoPush always uploads the tree of committed HEAD; it never stages or commits
worktree changes. Normal push and --force-with-lease both compare the current
Overleaf content snapshot with the recorded synchronization base and reject
independent remote changes. This is a content-snapshot lease, not an atomic Git
ref lease. --force (or -f) replaces remote content with committed HEAD
even when the snapshots have diverged.
Before uploading, push journals the remote base and exact target commit. If an
upload is interrupted, another normal push resumes only when HEAD is still
the target and every remote path matches either the old base or the target.
Use pull to reconcile an independently changed or partially uploaded remote,
or push --force to replace it intentionally. Pending state is cleared only
after a completed push, successful reconciliation, abort, or reset.
The Firefox importer reads profiles.ini, copies cookies.sqlite plus any
readable -wal / -shm sidecar files to a temporary directory, and imports
only valid Overleaf session cookies for the configured host.
Repository metadata lives in local Git config:
git-overleaf.projectIdgit-overleaf.projectNamegit-overleaf.urlgit-overleaf.baseRefgit-overleaf.remoteRefgit-overleaf.pendingActiongit-overleaf.pendingRemoteCommitgit-overleaf.pendingTargetCommitgit-overleaf.remoteFetchedAt
The logical remote uses this local-only Git config schema:
[remote "overleaf"]
gitOverleafProjectId = PROJECT_ID
skipFetchAll = true
skipDefaultUpdate = true
[git-overleaf]
remoteRef = refs/git-overleaf/remoteThe reserved synchronization base is refs/git-overleaf/base; the latest
successfully observed snapshot is refs/git-overleaf/remote. The internal
metadata file .git-overleaf-sync.json is removed from downloaded snapshots
before Git comparisons.
The cookie file contains live Overleaf session credentials. auth writes it
with mode 0600, but it should still stay outside Git repositories and logs.