Chinese README: README.zh.org
Inspired by vale981/overleaf.el.
The key difference from the original: overleaf.el provides a
single-buffer live editing workflow where each save syncs one file in
real time. overleaf-project instead treats the whole Overleaf
project as a Git repository — you commit locally at your own pace and
push/pull entire snapshots, which makes conflict resolution and history
management much more tractable.
overleaf-project provides project-level Overleaf integration for Emacs:
- clone a full Overleaf project to a local Git repository
- keep editing locally with normal Git commits
- push committed local snapshots to Overleaf and pull remote updates back
- resolve local/remote divergence with normal
git mergeon a dedicated branch
This package no longer provides the old single-buffer live editing workflow.
Load overleaf-project directly in both new and existing configs.
Add the package to your load path and require overleaf-project, or use
use-package:
(use-package overleaf-project
:custom
(overleaf-project-url "https://www.overleaf.com"))The package targets Emacs 29.4+ and depends on the Elisp packages
websocket and webdriver.
The package feature, interactive commands, and customization variables
use the overleaf-project- prefix to avoid colliding with
overleaf.el.
If you use a self-hosted Overleaf instance, set the server URL:
(setopt overleaf-project-url "https://latex.example.edu")Project push/pull expects these executables to be available:
gitcurlunzip
Optional authentication through the browser also needs geckodriver (https://github.com/mozilla/geckodriver).
For local validation, the repository includes a small Makefile:
make make help make clean
If your Emacs binary is not available on PATH as emacs, override it explicitly:
make EMACS=/path/to/Emacs
You need valid Overleaf session cookies before cloning, pushing, or pulling.
By default, overleaf-project-authenticate stores cookies in Emacs auth-source.
It uses the first plain file entry from auth-sources, falling back to
~/.authinfo. The package keeps one Overleaf-managed record per host,
using login overleaf-project and port overleaf-project-cookie. Re-authentication
replaces that managed record instead of appending stale copies.
If you prefer a plain file instead, set:
(setopt overleaf-project-cookie-storage "~/.overleaf-project-cookies")If you want cookies to stay only in memory for the current Emacs session, set:
(setopt overleaf-project-cookie-storage nil)For fully custom setups, you can still set overleaf-project-cookies and
overleaf-project-save-cookies directly, or use the file helpers
overleaf-project-read-cookies-from-file and overleaf-project-save-cookies-to-file.
Then run:
M-x overleaf-project-authenticate
If overleaf-project-clone, overleaf-project-init,
overleaf-project-push, overleaf-project-overwrite-remote, or
overleaf-project-pull starts without valid cookies, the command asks
in the minibuffer whether it should run overleaf-project-authenticate
immediately. For cookies saved by overleaf-project-authenticate, 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 overleaf-project-clone
This command:
- uses the configured
overleaf-project-urland prompts for the project - downloads the full project zip
- creates a local Git repository
- commits the imported snapshot
- stores Overleaf metadata in the repo’s local Git config
If you already have a local Git repository with the project files, run:
M-x overleaf-project-init
This command:
- prompts for the remote Overleaf project
- stores Overleaf metadata in the repo’s local Git config
- initializes the hidden base snapshot used by later push/pull operations
- does not automatically pull from or push to Overleaf
After cloning, work in the repo normally:
- edit files locally
- optionally stage only the changes you want to include
- run
M-x overleaf-project-pushto upload local commits - run
M-x overleaf-project-pullto bring remote Overleaf changes back
overleaf-project-push prepares the local branch before talking to
Overleaf:
- if there are staged changes, it commits them automatically
- if there are unstaged or untracked files, it asks whether to stage all changes first
- it then fetches the latest remote Overleaf snapshot and uploads
HEADonly when the remote has not diverged
overleaf-project-pull requires a clean working tree and merges the
latest Overleaf snapshot into the current branch.
When used interactively, the commands collect minibuffer input and
confirmation in the foreground, then run the long network, unzip, and
Git work in the background by default. This keeps Emacs responsive
while clone, init, push, pull, remote overwrite, authentication, and Magit
remote refreshes are waiting on Overleaf or external tools. Set
overleaf-project-async-commands to nil if you need the older fully
synchronous command behavior.
Both commands still have to run from a normal branch rather than a
detached HEAD.
By default, successful push operations also maintain a root-level
.overleaf-project-sync.json file on the Overleaf project. That file
records the last local Git commit/tree uploaded by this package. It is
removed from downloaded snapshots before Git comparisons, so it acts as
remote sync bookkeeping rather than normal project content.
The package also creates local safety refs under
refs/overleaf-project/backups/ before sync steps that can move the current
branch or complete pending sync state. These refs keep previous local
commits reachable independently of branch movement and reflog expiry.
Run:
M-x overleaf-project-push
The command compares three states:
- the last successfully synced Git commit
- the current local
HEAD - the latest project snapshot downloaded from Overleaf
Then it behaves as follows:
- if only local changed, it uploads
HEADto Overleaf - if remote already matches
HEAD, it only updates the base ref - if only remote changed, it stops and asks you to run
overleaf-project-pull - if both changed differently, it stops and asks you to run
overleaf-project-pullfirst
Run:
M-x overleaf-project-overwrite-remote
This command uses the same local auto-commit preparation as
overleaf-project-push, but always replaces the remote Overleaf project
contents with the local HEAD snapshot. Use it only as a recovery
command when you intentionally want local files to replace the remote
state.
Run:
M-x overleaf-project-pull
The command compares the same three states and then behaves as follows:
- if only remote changed, it fast-forwards your current branch locally
- if local already matches the remote snapshot, it only updates the base ref
- if there are no remote changes, it reports that nothing needs to be pulled
- if both changed differently, it merges the remote snapshot into the
current branch; after a clean merge, run
overleaf-project-pushto publish the merged result back to Overleaf
The clone, config, push, and pull commands store project metadata in the repository’s local Git config, so later pushes and pulls can run from anywhere inside that repo.
When both local and remote changed, run overleaf-project-pull first.
It merges the downloaded remote snapshot into the current branch with
normal Git merge machinery.
If the merge conflicts:
- the current branch is left in Git’s normal conflicted merge state
- you resolve conflicts with Magit or plain Git
- after creating the merge commit, run
overleaf-project-pushto upload the merged result
This keeps conflict resolution entirely inside Git instead of ediff.
If you commit from Emacs via Magit / git-commit, you can trigger push
automatically after each commit:
(with-eval-after-load 'git-commit
(add-hook 'git-commit-post-finish-hook
(lambda () (overleaf-project-push nil t))))The hook only pushes repositories that already contain Overleaf project
metadata. With the default overleaf-project-async-commands setting,
this hook starts the push in the background instead of blocking Emacs.
If you want an Overleaf section inside magit-status, load the optional
overleaf-project-magit integration and enable it once:
(with-eval-after-load 'magit
(require 'overleaf-project-magit)
(overleaf-project-magit-setup))For Overleaf-managed repositories, each magit-status refresh then also
may start an asynchronous refresh of the remote Overleaf snapshot. The
automatic refresh is throttled by
overleaf-project-magit-auto-refresh-remote-interval so ordinary Magit
refreshes do not repeatedly download snapshots and trigger Overleaf rate
limits. When the download finishes, the status buffer refreshes again
and shows the remote diff against the last synced base snapshot.
If you prefer to keep remote refresh manual, set
overleaf-project-magit-auto-refresh-remote to nil. You can still
run M-x overleaf-project-magit-refresh-remote manually.
The main interactive commands are:
overleaf-project-authenticateoverleaf-project-cloneoverleaf-project-initoverleaf-project-pushoverleaf-project-overwrite-remoteoverleaf-project-pulloverleaf-project-browse-remoteoverleaf-project-logoverleaf-project-log-clear
You can also bind the provided command map:
(global-set-key (kbd "C-c o") overleaf-project-command-map)The default bindings inside that map are:
a->overleaf-project-authenticateb->overleaf-project-browse-remotec->overleaf-project-clonel->overleaf-project-pullp->overleaf-project-pushs->overleaf-project-push(compatibility shortcut)
Useful options include:
overleaf-project-urlfor the Overleaf server URLoverleaf-project-cache-cookiesto force cookie reloads instead of cachingoverleaf-project-debugfor verbose loggingoverleaf-project-log-buffer-namefor the global log buffer nameoverleaf-project-log-echoto control whether log entries also appear in the minibufferoverleaf-project-log-time-formatto customize log entry timestampsoverleaf-project-sync-auto-commit-messagefor the automatic local checkpoint commit before pushingoverleaf-project-async-commandsto keep interactive commands and push hooks from blocking Emacs during network, unzip, and Git workoverleaf-project-magit-auto-refresh-remote-intervalto throttle automatic Magit remote snapshot downloadsoverleaf-project-sync-metadata-enabledto control whether push writes remote sync metadataoverleaf-project-sync-metadata-fileto rename the reserved remote metadata fileoverleaf-project-local-backups-enabledto control whether sync operations create local safety refsoverleaf-project-local-backup-ref-prefixto choose the backup ref namespaceoverleaf-project-socket-timeoutif project tree websocket fetches need longeroverleaf-project-curl-connect-timeoutandoverleaf-project-curl-max-timeto cap curl calls during Overleaf HTTP requestsoverleaf-project-git-executable,overleaf-project-curl-executableandoverleaf-project-unzip-executableif the tools are not on yourPATH
- Overleaf messages, warnings, and debug entries are written to the
global
*overleaf-project-log*buffer. Each entry includes timestamp, level, and the best available project, repository, or Overleaf URL context so logs from different projects can be distinguished. - Push currently recreates changed remote files instead of preserving old Overleaf document ids. That is acceptable for the new project-level workflow, but it is different from the old live buffer integration.
overleaf-project-overwrite-remoteoverwrites remote Overleaf files with the localHEADsnapshot..overleaf-project-sync.jsonis reserved for remote sync metadata when metadata support is enabled. It should not be tracked in the local Git repository.- Local safety refs can be inspected with:
git show-ref refs/overleaf-project/backupsTo recover one, create a normal branch from the backup ref:
git branch recover-overleaf refs/overleaf-project/backups/... - Empty directories are not tracked by Git and therefore are not preserved by push/pull.
- Verbose debug entries can be enabled by setting
overleaf-project-debugtot.