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

git: update jj git clone|fetch to use new GitFetch api directly. #5444

Open
wants to merge 2 commits into
base: bsdinis/push-zosznwvkmtnt
Choose a base branch
from

Conversation

bsdinis
Copy link
Contributor

@bsdinis bsdinis commented Jan 23, 2025

  • Make the new GitFetch api public.
  • Move git::fetch to lib/tests/test_git.rs as git_fetch, to minimize churn in the tests. Update test call sites to use git_fetch
  • Delete the git::fetch from lib/src/git.rs.
  • Update jj git clone and git_fetch in cli/src/git_utils.rs to use the new api directly. Removing one redundant layer of indirection.
  • This fixes jj git fetch with multiple remotes can abandon reachable commits (and rebase reachable descendants) #4920 as it first fetches from all remotes before import_refs() is called, so there is no race condition if the same commit is treated differently in different remotes specified in the same command.

Original commit by @essiene #4960

@bsdinis
Copy link
Contributor Author

bsdinis commented Jan 23, 2025

Created a new PR (essentially the same as #4960 ) since rebasing was too hard on the conflicts.
Addressed the comments on the original PR.

All credit of this patch to @essiene

Copy link
Contributor

@yuja yuja left a comment

Choose a reason for hiding this comment

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

Thanks.

Comment on lines +60 to +79
let signature =
git2::Signature::new("Some One", "[email protected]", &git2::Time::new(0, 0)).unwrap();
let mut tree_builder = git_repo.treebuilder(None).unwrap();
let file_oid = git_repo.blob(remote.as_bytes()).unwrap();
tree_builder
.insert("file", file_oid, git2::FileMode::Blob.into())
.unwrap();
let tree_oid = tree_builder.write().unwrap();
let tree = git_repo.find_tree(tree_oid).unwrap();
// our branch name is the same as the source branch name
git_repo
.commit(
Some(&format!("refs/heads/{src_remote}")),
&signature,
&signature,
"message",
&tree,
&[],
)
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: probably better to extract add_git_commit() function. I don't think clone + add_commit is common operation.


let bookmark_output1 = get_bookmark_output(&test_env1, &repo_path1);
let bookmark_output2 = get_bookmark_output(&test_env2, &repo_path2);
assert_eq!(bookmark_output1, bookmark_output2);
Copy link
Contributor

@yuja yuja Jan 24, 2025

Choose a reason for hiding this comment

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

It's not important that the conflicts are sorted by remote name.

A bigger problem reported in #4920 is that an abandoned commit in one remote becomes hidden (or abandoned) even if the commit is referenced by the other remote, and the descendants were rebased iirc. Can you add the test for this scenario?

lib/src/git.rs Show resolved Hide resolved
lib/src/git.rs Show resolved Hide resolved
lib/tests/test_git.rs Show resolved Hide resolved
@yuja
Copy link
Contributor

yuja commented Jan 25, 2025

Forgot to upload new version?

@bsdinis bsdinis force-pushed the bsdinis/push-urozxlpwnrmp branch from 7d63a2e to 496e6f9 Compare January 25, 2025 04:27
@bsdinis
Copy link
Contributor Author

bsdinis commented Jan 25, 2025

updated now. was trying to understand the test case you were referring too, need more time to understand it

@yuja
Copy link
Contributor

yuja commented Jan 25, 2025

was trying to understand the test case you were referring too, need more time to understand it

It's something that a ref is rewritten (or abandoned) by one remote, and the same ref is moved forward by the other remote.

rm -fR remote1 remote2 local

jj git init remote1
jj -R remote1 desc -m 'remote 1'
jj -R remote1 bookmark create foo
jj -R remote1 git export

jj git clone remote1/.jj/repo/store/git remote2
jj -R remote2 bookmark track glob:'foo@*'
jj -R remote2 git export

jj git clone remote1/.jj/repo/store/git --remote=remote1 local
jj -R local git remote add remote2 remote2/.jj/repo/store/git
jj -R local git fetch --remote=remote2
jj -R local bookmark track glob:'foo@*'
jj -R local log -r '::(visible_heads() | remote_bookmarks())'

# rewrite bookmarked commit
jj -R remote1 desc -m 'remote 1+'
jj -R remote1 git export
jj -R remote1 log -r'::(visible_heads() | remote_bookmarks())'

# move bookmark forward
jj -R remote2 new foo -m 'remote 2+'
jj -R remote2 bookmark move --to=@ foo
jj -R remote2 git export
jj -R remote2 log -r'::(visible_heads() | remote_bookmarks())'

# the result should be roughly equivalent to
#  jj -R local git fetch --remote=remote1
#  jj -R local git fetch --remote=remote2
jj -R local git fetch --remote=remote1 --remote=remote2
jj -R local log -r '::(visible_heads() | remote_bookmarks())'

Currently, the subprocessing code paths in the library were relying on
git2 to figure out if a remote exists. This is because when git errors
out for a remote not existing it outputs the same error as when the
repository is not found.

As the cli tool makes a distinction between the two (e.g., after
cloning, the remote must exist and as such we cannot send out an error
that is shared by `no remote found` and `no repository found`), this was a
hack put in place to sort out the difference.

It calls out to `git remote` to list out the remotes.
* Make the new `GitFetch` api public.
* Move `git::fetch` to `lib/tests/test_git.rs` as `git_fetch`, to minimize
  churn in the tests. Update test call sites to use `git_fetch`
* Delete the `git::fetch` from `lib/src/git.rs`.
* Update `jj git clone` and `git_fetch` in `cli/src/git_utils.rs` to use
  the new api directly. Removing one redundant layer of indirection.
* This fixes #4920 as it first fetches from all remotes before `import_refs()`
  is called, so there is no race condition if the same commit is treated
  differently in different remotes specified in the same command.

Original commit by @essiene
@bsdinis bsdinis force-pushed the bsdinis/push-urozxlpwnrmp branch from 496e6f9 to 3e24640 Compare January 25, 2025 23:12
@bsdinis bsdinis force-pushed the bsdinis/push-zosznwvkmtnt branch 3 times, most recently from f7fbd21 to 1857065 Compare January 26, 2025 04:23
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.

2 participants