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.
- 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-statusintegration 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.
git-overleaf requires Emacs 29.4 or later and the following Elisp
packages:
websocket1.15 or laterwebdriver0.1 or later
Project synchronization also requires these external executables:
gitcurlunzip
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.
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")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.
- Authenticate with Overleaf:
M-x git-overleaf-authenticate - Clone an Overleaf project:
M-x git-overleaf-clone - Edit locally and commit changes with Git.
- Push local commits to Overleaf:
M-x git-overleaf-push - 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.
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.
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.
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")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.
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.
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.
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-pushto upload local commits; - run
git-overleaf-pullto 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.
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:
| State | Result |
|---|---|
| Only local changed | Upload local HEAD to Overleaf. |
Remote already matches HEAD | Update the base ref only. |
| Only remote changed | Stop and ask you to run git-overleaf-pull. |
| Local and remote both changed differently | Stop 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.
Run:
M-x git-overleaf-pull
The command compares the same three states. Its behavior is:
| State | Result |
|---|---|
| Only remote changed | Fast-forward the current branch locally. |
| Local already matches the remote snapshot | Update the base ref only. |
| No remote changes | Report that nothing needs to be pulled. |
| Local and remote both changed differently | Merge 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.
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.
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-pushto upload the merged result.
Conflict resolution is intentionally handled by Git rather than by ediff.
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.
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.
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
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.
| Command | Purpose |
|---|---|
git-overleaf-authenticate | Authenticate and store Overleaf cookies. |
git-overleaf-clone | Clone an Overleaf project into a new local Git repository. |
git-overleaf-init | Bind the current Git repository to an Overleaf project. |
git-overleaf-push | Upload local HEAD to Overleaf when the remote has not diverged. |
git-overleaf-overwrite-remote | Replace the remote Overleaf project with local HEAD. |
git-overleaf-pull | Pull and merge the latest Overleaf snapshot. |
git-overleaf-browse-remote | Open the bound Overleaf project in a browser. |
git-overleaf-force-stop | Cancel tracked background Overleaf operations. |
git-overleaf-log | Display the global log buffer. |
git-overleaf-log-clear | Clear 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:
| Key | Command |
|---|---|
a | git-overleaf-authenticate |
b | git-overleaf-browse-remote |
c | git-overleaf-clone |
k | git-overleaf-force-stop |
l | git-overleaf-pull |
p | git-overleaf-push |
s | git-overleaf-push |
The s binding is retained as a compatibility shortcut.
Important user options include:
| Option | Description |
|---|---|
git-overleaf-url | Overleaf server URL. |
git-overleaf-auth-backend | Authentication backend: webdriver or firefox-cookies. |
git-overleaf-cookie-storage | Cookie storage backend: authinfo, a file path, or nil. |
git-overleaf-firefox-profile | Explicit Firefox profile path for cookie import. |
git-overleaf-enable-async | Run long interactive operations in background threads. |
git-overleaf-sync-auto-commit-message | Commit message for automatic local checkpoint commits before push. |
git-overleaf-sync-metadata-enabled | Enable the remote sync metadata file. |
git-overleaf-sync-metadata-file | Name of the reserved remote metadata file. |
git-overleaf-local-backups-enabled | Create local safety refs during sync operations. |
git-overleaf-local-backup-ref-prefix | Ref namespace for local safety refs. |
git-overleaf-socket-timeout | Timeout for websocket project tree fetches. |
git-overleaf-git-executable | Git executable path. |
git-overleaf-curl-executable | Curl executable path. |
git-overleaf-unzip-executable | Unzip executable path. |
git-overleaf-debug | Enable verbose debug logging. |
git-overleaf-log-echo | Echo log entries in the minibuffer. |
git-overleaf-magit-auto-refresh-remote | Automatically refresh remote snapshots from Magit. |
Use M-x customize-group RET git-overleaf RET to inspect the full
customization group.
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)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.
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.
git-overleaf was inspired by
vale981/overleaf.el, but it uses a project-wide Git snapshot workflow
instead of single-buffer live editing.
git-overleaf is distributed under the terms of the GNU General
Public License, version 3 or later. See LICENSE.