git: add config to disable abandoning of unreachable commits

Some users prefer to have commits not get abandoned when importing
refs. This adds a config option for that.

Closes #2504.
This commit is contained in:
Martin von Zweigbergk 2023-11-04 22:15:41 -07:00 committed by Martin von Zweigbergk
parent 7bf8906f9c
commit 7c923514ee
6 changed files with 66 additions and 1 deletions

View file

@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
create a new workspace with its working-copy commit on top of all the parents,
as if you had run `jj new r1 r2 r3 ...`.
* You can now set `git.abandon-unreachable-commits = false` to disable the
usual behavior where commits that became unreachable in the Git repo are
abandoned ([#2504](https://github.com/martinvonz/jj/pull/2504)).
### Fixed bugs

View file

@ -225,6 +225,11 @@
"description": "Whether jj creates a local branch with the same name when it imports a remote-tracking branch from git. See https://github.com/martinvonz/jj/blob/main/docs/config.md#automatic-local-branch-creation",
"default": true
},
"abandon-unreachable-commits": {
"type": "boolean",
"description": "Whether jj should abandon commits that became unreachable in Git.",
"default": true
},
"push-branch-prefix": {
"type": "string",
"description": "Prefix used when pushing a change ID as a new branch",

View file

@ -525,6 +525,20 @@ jj branch delete gh-pages
jj branch untrack gh-pages@upstream
```
### Abandon commits that became unreachable in Git
By default, when `jj` imports refs from Git, it will look for commits that used
to be [reachable][reachable] but no longer are reachable. Those commits will
then be abandoned, and any descendant commits will be rebased off of them (as
usual when commits are abandoned). You can disable this behavior and instead
leave the Git-unreachable commits in your repo by setting:
```toml
git.abandon-unreachable-commits = false
```
[reachable]: https://git-scm.com/docs/gitglossary/#Documentation/gitglossary.txt-aiddefreachableareachable
### Prefix for generated branches on push
`jj git push --change` generates branch names with a prefix of "push-" by

View file

@ -313,7 +313,11 @@ pub fn import_some_refs(
}
}
let abandoned_commits = abandon_unreachable_commits(mut_repo, &changed_remote_refs);
let abandoned_commits = if git_settings.abandon_unreachable_commits {
abandon_unreachable_commits(mut_repo, &changed_remote_refs)
} else {
vec![]
};
let stats = GitImportStats { abandoned_commits };
Ok(stats)
}

View file

@ -40,12 +40,16 @@ pub struct RepoSettings {
#[derive(Debug, Clone)]
pub struct GitSettings {
pub auto_local_branch: bool,
pub abandon_unreachable_commits: bool,
}
impl GitSettings {
pub fn from_config(config: &config::Config) -> Self {
GitSettings {
auto_local_branch: config.get_bool("git.auto-local-branch").unwrap_or(true),
abandon_unreachable_commits: config
.get_bool("git.abandon-unreachable-commits")
.unwrap_or(true),
}
}
}
@ -54,6 +58,7 @@ impl Default for GitSettings {
fn default() -> Self {
GitSettings {
auto_local_branch: true,
abandon_unreachable_commits: true,
}
}
}

View file

@ -708,6 +708,7 @@ fn test_import_refs_reimport_with_moved_untracked_remote_ref() {
let settings = testutils::user_settings();
let git_settings = GitSettings {
auto_local_branch: false,
..Default::default()
};
let test_workspace = TestRepo::init_with_backend(TestRepoBackend::Git);
let repo = &test_workspace.repo;
@ -819,6 +820,37 @@ fn test_import_refs_reimport_all_from_root_removed() {
assert!(!tx.mut_repo().view().heads().contains(&jj_id(&commit)));
}
#[test]
fn test_import_refs_reimport_abandoning_disabled() {
// Test that we don't abandoned unreachable commits if configured not to
let settings = testutils::user_settings();
let git_settings = GitSettings {
abandon_unreachable_commits: false,
..Default::default()
};
let test_repo = TestRepo::init_with_backend(TestRepoBackend::Git);
let repo = &test_repo.repo;
let git_repo = get_git_repo(repo);
let commit1 = empty_git_commit(&git_repo, "refs/heads/main", &[]);
let commit2 = empty_git_commit(&git_repo, "refs/heads/delete-me", &[&commit1]);
let mut tx = repo.start_transaction(&settings, "test");
git::import_refs(tx.mut_repo(), &git_repo, &git_settings).unwrap();
tx.mut_repo().rebase_descendants(&settings).unwrap();
// Test the setup
assert!(tx.mut_repo().view().heads().contains(&jj_id(&commit2)));
// Remove the `delete-me` branch and re-import
git_repo
.find_reference("refs/heads/delete-me")
.unwrap()
.delete()
.unwrap();
git::import_refs(tx.mut_repo(), &git_repo, &git_settings).unwrap();
tx.mut_repo().rebase_descendants(&settings).unwrap();
assert!(tx.mut_repo().view().heads().contains(&jj_id(&commit2)));
}
#[test]
fn test_import_refs_reimport_conflicted_remote_branch() {
let settings = testutils::user_settings();
@ -1426,6 +1458,7 @@ fn test_import_export_non_tracking_branch() {
let test_data = GitRepoData::create();
let mut git_settings = GitSettings {
auto_local_branch: false,
..Default::default()
};
let git_repo = test_data.git_repo;
let commit_main_t0 = empty_git_commit(&git_repo, "refs/remotes/origin/main", &[]);