diff options
authorJunio C Hamano <>2017-04-26 06:39:05 (GMT)
committerJunio C Hamano <>2017-04-26 06:39:05 (GMT)
commit46bdfa3975665aaa9b7383466b529354d117e14d (patch)
parentfa86d5cebe4151b642f66d932b842814e984c70e (diff)
parentf17d642d3b0fa64879d59b311e596949f2a1f6d2 (diff)
Merge branch 'ab/push-cas-doc-n-test'
Doc update. * ab/push-cas-doc-n-test: push: document & test --force-with-lease with multiple remotes
2 files changed, 70 insertions, 0 deletions
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 1624a35..0a63966 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -217,6 +217,47 @@ with this feature.
"--no-force-with-lease" will cancel all the previous --force-with-lease on the
command line.
+A general note on safety: supplying this option without an expected
+value, i.e. as `--force-with-lease` or `--force-with-lease=<refname>`
+interacts very badly with anything that implicitly runs `git fetch` on
+the remote to be pushed to in the background, e.g. `git fetch origin`
+on your repository in a cronjob.
+The protection it offers over `--force` is ensuring that subsequent
+changes your work wasn't based on aren't clobbered, but this is
+trivially defeated if some background process is updating refs in the
+background. We don't have anything except the remote tracking info to
+go by as a heuristic for refs you're expected to have seen & are
+willing to clobber.
+If your editor or some other system is running `git fetch` in the
+background for you a way to mitigate this is to simply set up another
+ git remote add origin-push $(git config remote.origin.url)
+ git fetch origin-push
+Now when the background process runs `git fetch origin` the references
+on `origin-push` won't be updated, and thus commands like:
+ git push --force-with-lease origin-push
+Will fail unless you manually run `git fetch origin-push`. This method
+is of course entirely defeated by something that runs `git fetch
+--all`, in that case you'd need to either disable it or do something
+more tedious like:
+ git fetch # update 'master' from remote
+ git tag base master # mark our base point
+ git rebase -i master # rewrite some commits
+ git push --force-with-lease=master:base master:master
+I.e. create a `base` tag for versions of the upstream code that you've
+seen and are willing to overwrite, then rewrite history, and finally
+force push changes to `master` if the remote version is still at
+`base`, regardless of what your local `remotes/origin/master` has been
+updated to in the background.
diff --git a/t/ b/t/
index a2c9e74..d38ecee 100755
--- a/t/
+++ b/t/
@@ -229,4 +229,33 @@ test_expect_success 'new branch already exists' '
+test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' '
+ rm -rf src dst &&
+ git init --bare src.bare &&
+ test_when_finished "rm -rf src.bare" &&
+ git clone --no-local src.bare dst &&
+ test_when_finished "rm -rf dst" &&
+ (
+ cd dst &&
+ test_commit G &&
+ git remote add origin-push ../src.bare &&
+ git push origin-push master:master
+ ) &&
+ git clone --no-local src.bare dst2 &&
+ test_when_finished "rm -rf dst2" &&
+ (
+ cd dst2 &&
+ test_commit H &&
+ git push
+ ) &&
+ (
+ cd dst &&
+ test_commit I &&
+ git fetch origin &&
+ test_must_fail git push --force-with-lease origin-push &&
+ git fetch origin-push &&
+ git push --force-with-lease origin-push
+ )