diff --git a/README.org b/README.org index 3d25c87..71eab0d 100644 --- a/README.org +++ b/README.org @@ -372,7 +372,8 @@ 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. The interactive command -always asks for confirmation. +always asks for confirmation. This is git-overleaf's semantic equivalent +of ~git push --force~. A successful overwrite also abandons and clears a pending Overleaf pull, so this command is the recovery path after a pull was manually aborted or @@ -546,11 +547,15 @@ then fetch, pull, or push. Selecting it asks for confirmation, registers the default name (or prompts for another name after a collision), and continues the requested operation. These actions are branchless: fetch updates only ~refs/git-overleaf/remote~, pull runs ~git-overleaf-pull~, and -push runs ~git-overleaf-push~. Active Git-only transient arguments are -rejected. Selecting the logical remote from Magit's ordinary "fetch +push normally runs ~git-overleaf-push~. The push action honors Magit's +force arguments: =-f= (force with lease) uses the same guarded push because +git-overleaf already rejects a remote snapshot that differs from its +recorded base, while =-F= (force) asks for confirmation and runs +~git-overleaf-overwrite-remote~. Other active Git-only transient arguments +are rejected. Selecting the logical remote from Magit's ordinary "fetch elsewhere" action also routes a no-argument fetch through git-overleaf; branches and refspecs are unsupported. All other Git remotes retain -Magit's normal behavior. Explicit =O= fetch bypasses the automatic +Magit's normal behavior. Explicit =O= fetch bypasses the automatic remote refresh throttle. On the Overleaf section, =G= refreshes the remote and =RET= opens the @@ -563,7 +568,7 @@ project in a browser. =C-c C-c= is a command prefix with these keys: | =l= | Pull from Overleaf. | | =p= | Push to Overleaf. | | =r= | Register the logical Overleaf remote. | -| =O= | Overwrite the remote after confirmation. | +| =O= | Force-push to the remote after confirmation. | | =L= | Show the git-overleaf log. | | =k= | Stop background Overleaf operations. | diff --git a/README.zh.org b/README.zh.org index 3c54d86..329ff83 100644 --- a/README.zh.org +++ b/README.zh.org @@ -334,7 +334,8 @@ M-x git-overleaf-overwrite-remote 这是一个恢复命令。它使用与 ~git-overleaf-push~ 相同的本地准备流程,但 总是用本地 ~HEAD~ 快照替换远端 Overleaf 项目内容。已有远端文本文档在支持 时仍会通过文本 OT 更新;其他条目使用 Overleaf 上传和删除 API。只有在确实 -希望本地状态替换远端状态时才应使用该命令。交互式运行时总会要求确认。 +希望本地状态替换远端状态时才应使用该命令。交互式运行时总会要求确认。它在 +语义上等价于 ~git push --force~。 成功覆盖还会放弃并清除 pending Overleaf pull,因此手动中止 pull 的 merge 或 pending 元数据过期时,可以用该命令恢复。命令不会自动中止 Git merge, @@ -481,10 +482,13 @@ Magit 的 fetch、pull 和 push transient 都会增加一个大写 =O= 动作, fetch/pull/push”;选择后先请求确认,使用默认名字注册(若同名 remote 已存在, 则提示输入另一个名字),再继续原操作。它们不接受分支:fetch 只更新 ~refs/git-overleaf/remote~,pull 调用 ~git-overleaf-pull~,push 调用 -~git-overleaf-push~。已启用的 Git 专用 transient 参数会被拒绝。从 Magit -普通的 “fetch elsewhere” 选择该逻辑 remote 且不提供参数时,也会转交给 -git-overleaf;分支和 refspec 不受支持。其他 Git remote 仍保持 Magit 原始 -行为。显式 =O= fetch 不受自动刷新节流限制。 +~git-overleaf-push~。push 动作支持 Magit 的 force 参数:=-f=(force with +lease)仍走安全 push,因为 git-overleaf 已经会拒绝与已记录 base 不同的远端 +快照;=-F=(force)则先请求确认,再调用 +~git-overleaf-overwrite-remote~。其他已启用的 Git 专用 transient 参数会被 +拒绝。从 Magit 普通的 “fetch elsewhere” 选择该逻辑 remote 且不提供参数时, +也会转交给 git-overleaf;分支和 refspec 不受支持。其他 Git remote 仍保持 +Magit 原始行为。显式 =O= fetch 不受自动刷新节流限制。 在 Overleaf section 上,=G= 刷新远端,=RET= 在浏览器中打开项目。 =C-c C-c= 是命令前缀,包含: @@ -496,7 +500,7 @@ git-overleaf;分支和 refspec 不受支持。其他 Git remote 仍保持 Magi | =l= | 从 Overleaf 拉取。 | | =p= | 推送到 Overleaf。 | | =r= | 注册逻辑 Overleaf remote。 | -| =O= | 确认后覆盖远端。 | +| =O= | 确认后 force-push 到远端。 | | =L= | 显示 git-overleaf 日志。 | | =k= | 停止后台 Overleaf 操作。 | diff --git a/git-overleaf-magit.el b/git-overleaf-magit.el index 061a32e..9d5c243 100644 --- a/git-overleaf-magit.el +++ b/git-overleaf-magit.el @@ -503,6 +503,24 @@ runs from status insertion or automatic refresh paths." "Git arguments do not apply to Overleaf: %s" (mapconcat #'identity args " "))))) +(defun git-overleaf-magit--push-mode (args) + "Return the Overleaf push mode selected by Magit ARGS. +No arguments and `--force-with-lease' select the normal guarded push. +The Git `-f' and `--force' arguments select a remote overwrite. Signal +when ARGS contains any other argument or combines the two force modes." + (cond + ((null args) 'normal) + ((equal args '("--force-with-lease")) 'normal) + ((and (null (cdr args)) + (member (car args) '("-f" "--force"))) + 'force) + (t + (user-error + (concat + "Only --force-with-lease and --force apply to Overleaf pushes; " + "got: %s") + (mapconcat #'identity args " "))))) + (transient-define-suffix git-overleaf-magit-fetch () "Fetch the branchless logical Overleaf remote." :description #'git-overleaf-magit--fetch-description @@ -524,17 +542,20 @@ runs from status insertion or automatic refresh paths." (git-overleaf--async-supported-p))) (call-interactively #'git-overleaf-pull)))) -(transient-define-suffix git-overleaf-magit-push () +(transient-define-suffix git-overleaf-magit-push (args) "Push the branchless logical Overleaf remote." :description #'git-overleaf-magit--push-description - (interactive) - (git-overleaf-magit--reject-arguments 'magit-push) - (let ((repo (git-overleaf-magit--require-managed-repo))) + (interactive (list (magit-push-arguments))) + (let ((mode (git-overleaf-magit--push-mode args)) + (repo (git-overleaf-magit--require-managed-repo))) (git-overleaf-magit--ensure-registered-remote repo "push") (let ((default-directory repo) (git-overleaf-enable-async (git-overleaf--async-supported-p))) - (call-interactively #'git-overleaf-push)))) + (call-interactively + (if (eq mode 'force) + #'git-overleaf-overwrite-remote + #'git-overleaf-push))))) (defun git-overleaf-magit--around-git-fetch (function remote args) "Route a branchless fetch of logical REMOTE through git-overleaf. diff --git a/test/git-overleaf-magit-test.el b/test/git-overleaf-magit-test.el index 9c76521..f02c109 100644 --- a/test/git-overleaf-magit-test.el +++ b/test/git-overleaf-magit-test.el @@ -108,6 +108,44 @@ (equal (git-overleaf-magit--operation-description "fetch") "Overleaf")))) +(ert-deftest git-overleaf-magit-test-push-mode () + (should (eq (git-overleaf-magit--push-mode nil) 'normal)) + (should + (eq (git-overleaf-magit--push-mode '("--force-with-lease")) + 'normal)) + (should (eq (git-overleaf-magit--push-mode '("-f")) 'force)) + (should (eq (git-overleaf-magit--push-mode '("--force")) 'force)) + (should-error + (git-overleaf-magit--push-mode '("--dry-run")) + :type 'user-error) + (should-error + (git-overleaf-magit--push-mode '("--force-with-lease" "--force")) + :type 'user-error)) + +(ert-deftest git-overleaf-magit-test-push-dispatches-force-modes () + (let (commands) + (cl-letf (((symbol-function 'git-overleaf-magit--require-managed-repo) + (lambda () "/repo")) + ((symbol-function + 'git-overleaf-magit--ensure-registered-remote) + (lambda (_repo _operation) "overleaf")) + ((symbol-function 'git-overleaf--async-supported-p) + (lambda () t)) + ((symbol-function 'call-interactively) + (lambda (command &rest _args) + (push command commands)))) + (git-overleaf-magit-push nil) + (git-overleaf-magit-push '("--force-with-lease")) + (git-overleaf-magit-push '("-f")) + (git-overleaf-magit-push '("--force")) + (should + (equal + (nreverse commands) + '(git-overleaf-push + git-overleaf-push + git-overleaf-overwrite-remote + git-overleaf-overwrite-remote)))))) + (ert-deftest git-overleaf-magit-test-ensure-remote-reuses-existing () (cl-letf (((symbol-function 'git-overleaf--remote-name) (lambda (_repo) "paper"))