Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jj git push: safety checks when pushing branch sideways, similar to git push --force-with-lease #3522

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

ilyagr
Copy link
Collaborator

@ilyagr ilyagr commented Apr 17, 2024

Previously, jj git push only made sure that the branch is in the expected
location on the remote server when pushing a branch forward (as opposed to
sideways or backwards). Now, jj git push makes a safety check in all cases
and fails whenever jj git fetch would have introduced a conflict.

In other words, previously branches that moved sideways or backward were
pushed similarly to Git's git push --force; now they have protections
similar to git push --force-with-lease (though not identical to it, to match
the behavior of jj git fetch). Note also that because of the way jj git fetch works, jj does not suffer from the same problems as Git's git push --force-with-lease in situations when git fetch is run in the background.


There are a few TODOs that may be added later (in this or separate PRs):

  • Document this behavior in jj help git push, possibly elsewhere.
  • Adjustments to user-facing UI
  • Additional tests

See TODOs in the code for more details. It's possible I forgot some, but it's probably time for others to look at the general form of this PR in any case.

Checklist

If applicable:

  • I have updated CHANGELOG.md
  • I have updated the documentation (README.md, docs/, demos/)
  • (n/a) I have updated the config schema (cli/src/config-schema.json)
  • I have added tests to cover my changes

lib/src/git.rs Outdated Show resolved Hide resolved
lib/src/git.rs Outdated Show resolved Hide resolved
@ilyagr ilyagr force-pushed the force-with-lease branch 6 times, most recently from 81b11aa to f7cec4f Compare April 20, 2024 06:51
@ilyagr ilyagr force-pushed the force-with-lease branch 13 times, most recently from 8b7e930 to bb9547c Compare May 12, 2024 00:06
@ilyagr
Copy link
Collaborator Author

ilyagr commented May 15, 2024

Thanks, there is no rush at all.

cli/tests/test_git_push.rs Outdated Show resolved Hide resolved
cli/tests/test_git_push.rs Outdated Show resolved Hide resolved
lib/src/git.rs Show resolved Hide resolved
lib/src/git.rs Outdated Show resolved Hide resolved
lib/src/git.rs Outdated Show resolved Hide resolved
lib/src/git.rs Outdated Show resolved Hide resolved
// information. See https://github.com/rust-lang/git2-rs/issues/1042.
assert!(push_result.is_err());
Err(GitPushError::RefInUnexpectedLocation(
failed_push_negotiations,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: sort here to stabilize error representation?

@@ -19,6 +19,7 @@ use std::{fs, iter, thread};

use assert_matches::assert_matches;
use git2::Oid;
use insta::{assert_debug_snapshot, assert_snapshot};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we usually spell insta::assert_*() at call sites (I don't know why, though.)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I wouldn't mind a PR that consistently imports the symbols.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with either convention. Explicit insta:: might remind new people this is related to cargo insta.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind either, but I think most of the insta assertions in test_git.rs can be removed.

),
],
)
"###
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppose these assertions are sanity checks, it's probably better to just assert!(...has_conflicts()). Insta snapshots are verbose.

"###
);
let result = attempt_push_expecting_sideways(None);
assert_snapshot!(format!("{result:?}"), @r###"Err(RefInUnexpectedLocation(["refs/heads/main"]))"###);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it can be insta::assert_debug_snapshot!(), but I would probably use assert_matches!(.., Err(RefInUnexpectedLocation(_))) since the failed ref names aren't important.

@ilyagr ilyagr force-pushed the force-with-lease branch 6 times, most recently from 8cd786a to bb9739c Compare May 21, 2024 00:23
@ilyagr ilyagr marked this pull request as draft May 24, 2024 18:00
@ilyagr ilyagr force-pushed the force-with-lease branch 7 times, most recently from 181bd7d to af5b193 Compare May 25, 2024 04:45
lib/src/git.rs Outdated Show resolved Hide resolved
lib/src/git.rs Show resolved Hide resolved
lib/src/git.rs Outdated
// Conveniently, this also means that the push will be
// successful **if and only if** `jj git fetch` is a no-op
// for this branch (and, in particular, would not create a
// conflict).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The allow_push() function looks unnecessarily verbose, but I think it'll be cleaned up. Overall, I like it.

Adds two tests where pushing should fail, but currently succeeds.
This tests `git push` attempting to create a branch when the branch
already unexpectedly exists on the remote. This should (and does)
fail.

Also changes another test to use `jj_cmd_failure`.
The new names are a bit clearer. Soem tests already use a
`sideways_commit`, `parent_of_main_commit` will be needed in
an upcoming test.
Hopefully, splitting the no-op portion will make the following commits
easier to review.
As explained in the commit, our logic is a bit more complicated than
that of `git push --force-with-lease`. This is to match the behavior of
`jj git fetch` and branch conflict resolution rules.
This should be a no-op, though that is not necessarily obvious in corner
cases.

Note that libgit2 already performs the push negotiation even when
pushing without force (without `+` in the refspec).
Now that we always force push, it should not occur in practice.
"Move forward" instead of "Move", "Move sideways" or "Move backward"
instead of (now misleading) "Force...".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants