Skip to content

Jamie-Cui/git-overleaf

Repository files navigation

git-overleaf

https://github.com/Jamie-Cui/git-overleaf/actions/workflows/test.yml/badge.svg https://github.com/Jamie-Cui/git-overleaf/actions/workflows/coverage.yml/badge.svg

Chinese README: README.zh.org

git-overleaf integrates Emacs with Overleaf at the project level. It clones an Overleaf project into a local Git repository, records the Overleaf project metadata in that repository, and synchronizes complete project snapshots between local Git history and Overleaf.

This package is designed for users who prefer local LaTeX editing, ordinary Git commits, and Git-based conflict resolution while still collaborating with people who use the Overleaf web editor.

git-overleaf is explicitly inspired by overleaf.el and builds on that idea with a project-wide Git snapshot workflow.

Features

  • Clone a complete Overleaf project into a local Git repository.
  • Bind an existing Git repository to an Overleaf project.
  • Push local Git snapshots to Overleaf and pull remote Overleaf changes back into the current branch.
  • Detect local/remote divergence by comparing the last synchronized commit, local HEAD, and the latest Overleaf snapshot.
  • Resolve conflicts with normal Git merge tools, including Magit.
  • Preserve existing Overleaf text document ids when possible by updating documents through Overleaf’s real-time text OT channel.
  • Store authentication cookies in auth-source, a plain cookie file, or the current Emacs session.
  • Optionally run network, unzip, and Git work in background Emacs threads.
  • Provide optional magit-status integration for Overleaf-managed repositories.

git-overleaf does not provide the single-buffer live editing workflow of overleaf.el. New and existing configurations should load git-overleaf directly.

Requirements

git-overleaf requires Emacs 29.4 or later and the following Elisp packages:

  • websocket 1.15 or later
  • webdriver 0.1 or later

Project synchronization also requires these external executables:

  • git
  • curl
  • unzip

The default authentication backend uses Firefox through Selenium webdriver and requires geckodriver. The Firefox cookie import backend does not require geckodriver, but it requires an Emacs build with SQLite support.

Installation

VC package install

git-overleaf is not yet available from MELPA. Install it directly from the Git repository with use-package:

(use-package git-overleaf
  :vc (:url "https://github.com/Jamie-Cui/git-overleaf" :rev "main"))

Without use-package, use Emacs’ package manager directly:

M-x package-vc-install RET https://github.com/Jamie-Cui/git-overleaf.git RET

For a self-hosted Overleaf instance, set the server URL before using the interactive commands:

(setopt git-overleaf-url "https://latex.example.edu")

Manual checkout

If using a local checkout, install the Elisp dependencies separately and add this repository to load-path:

(add-to-list 'load-path "/path/to/git-overleaf")
(require 'git-overleaf)

The package feature, interactive commands, and customization variables use the git-overleaf- prefix.

Quick Start

  1. Authenticate with Overleaf:
    M-x git-overleaf-authenticate
        
  2. Clone an Overleaf project:
    M-x git-overleaf-clone
        
  3. Edit locally and commit changes with Git.
  4. Push local commits to Overleaf:
    M-x git-overleaf-push
        
  5. Pull remote Overleaf changes when collaborators edit in the web editor:
    M-x git-overleaf-pull
        

If the project already exists as a local Git repository, run git-overleaf-init instead of git-overleaf-clone to bind it to an Overleaf project.

Authentication

Valid Overleaf session cookies are required before cloning, pushing, or pulling. The primary command is:

M-x git-overleaf-authenticate

If git-overleaf-clone, git-overleaf-init, git-overleaf-push, git-overleaf-overwrite-remote, or git-overleaf-pull starts without valid cookies, it prompts in the minibuffer to run git-overleaf-authenticate first.

Webdriver backend

The default backend is webdriver:

(setopt git-overleaf-auth-backend 'webdriver)

It starts geckodriver on a free local port, opens Firefox, and asks you to log in to Overleaf. A stale geckodriver process listening on the default port 4444 should not prevent re-authentication.

By default, authenticated cookies are stored with Emacs auth-source. The package uses the first plain-file entry in auth-sources, falling back to ~/.authinfo. It manages one entry per Overleaf host using login git-overleaf and port git-overleaf-cookie. Re-authentication replaces that managed entry instead of appending stale copies.

Firefox cookie import

If Firefox is already logged in to Overleaf, cookies can be imported from the default Firefox profile:

(setopt git-overleaf-auth-backend 'firefox-cookies)

This backend reads profiles.ini, imports only cookies for the current git-overleaf-url, and fails with an explicit message if no valid Overleaf session cookie is found. To bypass profile discovery:

(setopt git-overleaf-firefox-profile "/path/to/firefox/profile")

Cookie storage

To store cookies in a plain file:

(setopt git-overleaf-cookie-storage "~/.git-overleaf-cookies")

To keep cookies only in memory for the current Emacs session:

(setopt git-overleaf-cookie-storage nil)

Advanced configurations may set git-overleaf-cookies and git-overleaf-save-cookies directly.

For cookies saved by git-overleaf-authenticate, session expiry is checked from the locally saved session-cookie expiry before any network request. Short-lived analytics cookies are ignored for that check.

Workflow

Clone a project

Run:

M-x git-overleaf-clone

The command prompts for an Overleaf project, downloads the complete project zip, creates a local Git repository, commits the imported snapshot, and stores Overleaf metadata in the repository’s local Git config.

Bind an existing repository

Run this command from an existing Git repository:

M-x git-overleaf-init

The command prompts for the remote Overleaf project, stores project metadata in local Git config, and initializes the hidden base snapshot used by later push and pull operations. It does not automatically pull from or push to Overleaf.

Edit locally

After cloning or initializing, use the repository as an ordinary Git working tree:

  • edit files locally;
  • stage and commit changes as needed;
  • run git-overleaf-push to upload local commits;
  • run git-overleaf-pull to incorporate remote Overleaf changes.

git-overleaf-push prepares the local branch before contacting Overleaf. If staged changes exist, it commits them automatically. If unstaged or untracked files exist, it asks whether to stage all changes first. It then fetches the latest remote snapshot and uploads local HEAD only when the remote state has not diverged.

git-overleaf-pull requires a clean working tree and must run from a normal branch, not a detached HEAD.

Push to Overleaf

Run:

M-x git-overleaf-push

The command compares three states:

  • the last successfully synchronized Git commit;
  • the current local HEAD;
  • the latest project snapshot downloaded from Overleaf.

Its behavior is:

StateResult
Only local changedUpload local HEAD to Overleaf.
Remote already matches HEADUpdate the base ref only.
Only remote changedStop and ask you to run git-overleaf-pull.
Local and remote both changed differentlyStop and ask you to pull first.

For existing remote Overleaf text documents, changed UTF-8 text is updated through Overleaf’s real-time ShareJS text OT path. This preserves the remote document id and produces normal Overleaf web history entries. Changed binary files, newly added files, type conflicts, and deleted entries use Overleaf’s upload and delete APIs.

If a remote document cannot be updated through supported text OT, for example because it is not valid UTF-8, uses Overleaf’s newer history-ot type, or changes again after the snapshot is downloaded, the push fails instead of silently deleting and recreating that document.

Pull from Overleaf

Run:

M-x git-overleaf-pull

The command compares the same three states. Its behavior is:

StateResult
Only remote changedFast-forward the current branch locally.
Local already matches the remote snapshotUpdate the base ref only.
No remote changesReport that nothing needs to be pulled.
Local and remote both changed differentlyMerge the remote snapshot into the current branch.

After a clean merge caused by remote divergence, run git-overleaf-push to publish the merged result back to Overleaf.

Overwrite the Overleaf remote

Run:

M-x git-overleaf-overwrite-remote

This recovery command uses the same local preparation as git-overleaf-push, but always replaces the remote Overleaf project contents with the local HEAD snapshot. Existing remote text documents are still updated through text OT when supported; other entries use Overleaf upload and delete APIs. Use this command only when the local state is intended to replace the remote state.

Conflict handling

When local and remote snapshots diverge, run git-overleaf-pull first. The command merges the downloaded remote snapshot into the current branch with normal Git merge machinery.

If the merge conflicts:

  • the current branch remains in Git’s normal conflicted merge state;
  • resolve conflicts with Magit or plain Git;
  • create the merge commit;
  • run git-overleaf-push to upload the merged result.

Conflict resolution is intentionally handled by Git rather than by ediff.

Synchronization Details

The clone, init, push, and pull commands store project metadata in the repository’s local Git config. After a repository is bound, later pushes and pulls can run from anywhere inside that repository.

Successful push operations maintain a root-level .git-overleaf-sync.json file on the Overleaf project when git-overleaf-sync-metadata-enabled is non-nil. The file records the last local Git commit and tree uploaded by this package. It is removed from downloaded snapshots before Git comparisons, so it acts as sync bookkeeping rather than normal project content. It should not be tracked in the local Git repository.

Before sync steps that can move the current branch or complete pending sync state, the package creates local safety refs under refs/git-overleaf/backups/ when git-overleaf-local-backups-enabled is non-nil. These refs keep previous local commits reachable independently of branch movement and reflog expiry.

Inspect backup refs with:

git show-ref refs/git-overleaf/backups

Recover one by creating a normal branch from the backup ref:

git branch recover-overleaf refs/git-overleaf/backups/...

Empty directories are not tracked by Git and are therefore not preserved by push or pull.

Asynchronous Operation

By default, commands run synchronously. To keep Emacs responsive while long network, unzip, and Git work is running:

(setopt git-overleaf-enable-async t)

Interactive commands still collect minibuffer input and confirmation in the foreground, then run the expensive work in the background. This applies to clone, init, push, pull, remote overwrite, authentication, and Magit remote refreshes.

To cancel currently tracked background Overleaf operations:

M-x git-overleaf-force-stop

Cancellation interrupts external processes where possible, clears async locks, and drops pending foreground callbacks. Work already completed locally or remotely is not rolled back.

Magit Integration

To display an Overleaf section in magit-status, load the optional integration:

(with-eval-after-load 'magit
  (require 'git-overleaf-magit)
  (git-overleaf-magit-setup))

For Overleaf-managed repositories, each magit-status refresh may also start a background refresh of the remote Overleaf snapshot when git-overleaf-enable-async is non-nil. Automatic refresh is internally throttled so ordinary Magit refreshes do not repeatedly download snapshots.

To keep remote refresh manual:

(setopt git-overleaf-magit-auto-refresh-remote nil)

Remote state can still be refreshed with:

M-x git-overleaf-magit-refresh-remote

Commit Hook

To push automatically after commits created from Emacs through Magit or git-commit:

(with-eval-after-load 'git-commit
  (add-hook 'git-commit-post-finish-hook
            (lambda () (git-overleaf-push nil t))))

The hook only pushes repositories that already contain Overleaf project metadata. With synchronous commands, this hook blocks Emacs while the push runs. Enable git-overleaf-enable-async for background hook-driven pushes.

Commands

CommandPurpose
git-overleaf-authenticateAuthenticate and store Overleaf cookies.
git-overleaf-cloneClone an Overleaf project into a new local Git repository.
git-overleaf-initBind the current Git repository to an Overleaf project.
git-overleaf-pushUpload local HEAD to Overleaf when the remote has not diverged.
git-overleaf-overwrite-remoteReplace the remote Overleaf project with local HEAD.
git-overleaf-pullPull and merge the latest Overleaf snapshot.
git-overleaf-browse-remoteOpen the bound Overleaf project in a browser.
git-overleaf-force-stopCancel tracked background Overleaf operations.
git-overleaf-logDisplay the global log buffer.
git-overleaf-log-clearClear the global log buffer.

To bind the provided command map:

(global-set-key (kbd "C-c o") git-overleaf-command-map)

Default bindings in git-overleaf-command-map:

KeyCommand
agit-overleaf-authenticate
bgit-overleaf-browse-remote
cgit-overleaf-clone
kgit-overleaf-force-stop
lgit-overleaf-pull
pgit-overleaf-push
sgit-overleaf-push

The s binding is retained as a compatibility shortcut.

Customization

Important user options include:

OptionDescription
git-overleaf-urlOverleaf server URL.
git-overleaf-auth-backendAuthentication backend: webdriver or firefox-cookies.
git-overleaf-cookie-storageCookie storage backend: authinfo, a file path, or nil.
git-overleaf-firefox-profileExplicit Firefox profile path for cookie import.
git-overleaf-enable-asyncRun long interactive operations in background threads.
git-overleaf-sync-auto-commit-messageCommit message for automatic local checkpoint commits before push.
git-overleaf-sync-metadata-enabledEnable the remote sync metadata file.
git-overleaf-sync-metadata-fileName of the reserved remote metadata file.
git-overleaf-local-backups-enabledCreate local safety refs during sync operations.
git-overleaf-local-backup-ref-prefixRef namespace for local safety refs.
git-overleaf-socket-timeoutTimeout for websocket project tree fetches.
git-overleaf-git-executableGit executable path.
git-overleaf-curl-executableCurl executable path.
git-overleaf-unzip-executableUnzip executable path.
git-overleaf-debugEnable verbose debug logging.
git-overleaf-log-echoEcho log entries in the minibuffer.
git-overleaf-magit-auto-refresh-remoteAutomatically refresh remote snapshots from Magit.

Use M-x customize-group RET git-overleaf RET to inspect the full customization group.

Logging

Overleaf messages, warnings, and debug entries are written to the global *git-overleaf-log* buffer. Each entry includes a timestamp, log level, and the best available project, repository, or Overleaf URL context, which helps distinguish logs from multiple projects.

Verbose debug entries can be enabled with:

(setopt git-overleaf-debug t)

Security Notes

Overleaf cookies grant access to your Overleaf account. Keep auth-source files and custom cookie files private, and do not commit real cookies, project identifiers, ancestor backup files, or debug logs that contain session data.

Development

The repository includes a small Makefile for local validation:

make
make test
make coverage
make help
make clean

If the Emacs executable is not available on PATH as emacs:

make EMACS=/path/to/Emacs

Equivalent batch validation commands:

emacs -Q --batch -L . --eval "(progn (require 'package) (package-initialize))" -f batch-byte-compile git-overleaf.el
emacs -Q --batch -L . --eval "(progn (require 'package) (package-initialize))" --eval "(require 'git-overleaf)"

The automated test suite uses ERT and is designed to run offline:

make test

It covers pure helpers, cookie and HTTP boundary logic, local Git snapshot/state transitions, remote tree diffing with stubbed Overleaf API calls, command dispatch, async state handling, and small Magit integration helpers. It intentionally does not contact Overleaf, start webdriver, or open a browser.

For changes that affect the real service boundary, also validate clone, init, push, pull, pending-state recovery, and Git conflict resolution against a real Overleaf session. For authentication changes, validate both webdriver authentication and Firefox cookie import when possible.

Coverage is not currently wired into the default build. Emacs includes the built-in testcover library. To run the offline ERT suite under coverage instrumentation:

make coverage
make coverage COVERAGE_MIN=50

The target writes a tab-separated summary to coverage/testcover-summary.tsv. The optional COVERAGE_MIN variable turns the report into a threshold check. GitHub Actions also exposes a manual workflow_dispatch trigger that runs make coverage and uploads the summary artifact. External packages such as undercover.el can report ERT coverage to hosted coverage services, but that is not part of the default CI path.

Acknowledgements

git-overleaf was inspired by vale981/overleaf.el, but it uses a project-wide Git snapshot workflow instead of single-buffer live editing.

License

git-overleaf is distributed under the terms of the GNU General Public License, version 3 or later. See LICENSE.

About

Project-level Overleaf integration for Emacs

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors