diff --git a/cli/src/commands/git.rs b/cli/src/commands/git.rs
index 9122399631..f014617df4 100644
--- a/cli/src/commands/git.rs
+++ b/cli/src/commands/git.rs
@@ -189,6 +189,28 @@ pub struct GitCloneArgs {
 /// `remote_branches(remote=<remote>)..@`. Use `--branch` to push specific
 /// branches. Use `--all` to push all branches. Use `--change` to generate
 /// branch names based on the change IDs of specific commits.
+///
+/// Before the command actually moves or creates a remote branch, it makes
+/// several safety checks.
+///
+/// 1. `jj` will contact the remote and check that the actual state of the
+///    remote branch matches `jj`'s record of its last known position. If they
+///    do not match, `jj` will refuse to push the branch. In this case, you need
+///    to `jj git fetch --remote <remote name>`. This may create some branch
+///    conflicts, which you'll need to resolve before trying `jj git push`
+///    again.
+///
+///    If you are familiar with Git, this makes `jj git push` similar to `git
+///    push --force-with-lease`.
+///
+/// 2. The local branch must not be [conflicted]. If it is, you would need to
+///    use `jj branch set`, for example, to resolve the conflict.
+///
+/// 3. If the remote branch already exists on the remote, it must be [tracked].
+///
+/// [conflicted]: https://martinvonz.github.io/jj/latest/branches/#conflicts
+/// [tracked]: https://martinvonz.github.io/jj/latest/branches/#remotes-and-tracked-branches
+
 #[derive(clap::Args, Clone, Debug)]
 #[command(group(ArgGroup::new("specific").args(&["branch", "change", "revisions"]).multiple(true)))]
 #[command(group(ArgGroup::new("what").args(&["all", "deleted", "tracked"]).conflicts_with("specific")))]
diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap
index 6b0a01137a..ce45fa8994 100644
--- a/cli/tests/cli-reference@.md.snap
+++ b/cli/tests/cli-reference@.md.snap
@@ -905,6 +905,18 @@ Push to a Git remote
 
 By default, pushes any branches pointing to `remote_branches(remote=<remote>)..@`. Use `--branch` to push specific branches. Use `--all` to push all branches. Use `--change` to generate branch names based on the change IDs of specific commits.
 
+Before the command actually moves or creates a remote branch, it makes several safety checks.
+
+1. `jj` will contact the remote and check that the actual state of the remote branch matches `jj`'s record of its last known position. If they do not match, `jj` will refuse to push the branch. In this case, you need to `jj git fetch --remote <remote name>`. This may create some branch conflicts, which you'll need to resolve before trying `jj git push` again.
+
+If you are familiar with Git, this makes `jj git push` similar to `git push --force-with-lease`.
+
+2. The local branch must not be [conflicted]. If it is, you would need to use `jj branch set`, for example, to resolve the conflict.
+
+3. If the remote branch already exists on the remote, it must be [tracked].
+
+[conflicted]: https://martinvonz.github.io/jj/latest/branches/#conflicts [tracked]: https://martinvonz.github.io/jj/latest/branches/#remotes-and-tracked-branches
+
 **Usage:** `jj git push [OPTIONS]`
 
 ###### **Options:**