Skip to content

Commit f279612

Browse files
gustavoavenafacebook-github-bot
authored andcommitted
Use correct submodule action during backsync
Summary: ## This stack See D57204338. ## This diff As mentioned in D56826268 and D56881703, there's currently a bug in `get_git_submodule_action_by_version` (just renamed from `get_strip_git_submodules_by_version`) that leads using the default submodule action (i.e. STRIP) when it's called during backsyncing (large to small). Luckily this led to a behaviour very close to what we're looking for **now** while backsyncing submodule changes is not supported: 1. If the commit doesn't touch submodule expansions, everything works normally. 2. If it does, it will sync it but the synced commit will be "broken". It will fail to derive fsnodes and other manifests. We still want to keep #1, but we don't want #2. What we want right now is to **explicitly fail sync if someone tries to backsync any changes modifying submodule expansions**. This diff implements this. Reviewed By: mitrandir77 Differential Revision: D57151944 fbshipit-source-id: b4931d3cc58815b2beaeaae90de99dda15b5fbb9
1 parent cae707e commit f279612

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

eden/mononoke/commit_rewriting/cross_repo_sync/src/commit_sync_config_utils.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,20 @@ pub async fn get_git_submodule_action_by_version(
5757
.submodule_config
5858
.git_submodules_action
5959
.clone();
60-
// TODO(T179530927): return small repo action instead of default one
61-
if small_repo_action != GitSubmodulesChangesAction::default() {
62-
log_warning(
63-
ctx,
64-
format!(
65-
"Using default submodule action for backsyncing. Actual submodule action configured for small repo is {0:#?}",
66-
small_repo_action
67-
),
68-
);
69-
};
70-
};
7160

72-
// TODO(T179530927): get the correct submodule action from small repo
73-
// when call is made during backsyncing.
74-
// Currently, when this is called for backsyncing, we look for the large
75-
// repo in the small repo configs, don't find it and return the default
76-
// action of STRIP.
61+
return Ok(small_repo_action);
62+
};
7763

64+
// If the action if not found above, there might be something wrong. We
65+
// should still fallback to the default action to avoid ever syncing submodule
66+
// file changes to the large repo, but let's at least log a warning.
67+
log_warning(
68+
ctx,
69+
format!(
70+
"Couldn't find git submodule action for source repo id {}, target repo id {} and commit sync version {}",
71+
source_repo_id, target_repo_id, version,
72+
),
73+
);
7874
Ok(GitSubmodulesChangesAction::default())
7975
}
8076

eden/mononoke/commit_rewriting/cross_repo_sync/src/git_submodules/expand.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use either::Either::*;
2828
use futures::stream;
2929
use futures::stream::StreamExt;
3030
use futures::stream::TryStreamExt;
31+
use itertools::Itertools;
3132
use manifest::BonsaiDiffFileChange;
3233
use maplit::hashmap;
3334
use mononoke_types::BlobstoreValue;
@@ -47,6 +48,7 @@ use crate::commit_syncers_lib::mover_to_multi_mover;
4748
use crate::git_submodules::in_memory_repo::InMemoryRepo;
4849
use crate::git_submodules::utils::build_recursive_submodule_deps;
4950
use crate::git_submodules::utils::get_git_hash_from_submodule_file;
51+
use crate::git_submodules::utils::get_submodule_expansions_affected;
5052
use crate::git_submodules::utils::get_submodule_file_content_id;
5153
use crate::git_submodules::utils::get_submodule_repo;
5254
use crate::git_submodules::utils::get_x_repo_submodule_metadata_file_path;
@@ -120,10 +122,35 @@ pub async fn rewrite_commit_with_submodule_expansion<'a, R: Repo>(
120122
remapped_parents: &'a HashMap<ChangesetId, ChangesetId>,
121123
rewrite_opts: RewriteOpts,
122124
) -> Result<Option<BonsaiChangesetMut>> {
123-
ensure!(
124-
source_repo.repo_identity().id() != *sm_exp_data.large_repo_id,
125-
"Can't sync changes from large to small repo if small repo has submodule expansion enabled"
126-
);
125+
let is_forward_sync = source_repo.repo_identity().id() != *sm_exp_data.large_repo_id;
126+
if !is_forward_sync {
127+
// Backsyncing, so ensure that submodule expansions are not being modified
128+
let submodules_affected =
129+
get_submodule_expansions_affected(&sm_exp_data, &bonsai, mover.clone())?;
130+
131+
ensure!(
132+
submodules_affected.is_empty(),
133+
"Changeset can't be synced from large to small repo because it modifies the expansion of submodules: {0:#?}",
134+
submodules_affected
135+
.into_iter()
136+
.map(|p| p.to_string())
137+
.sorted()
138+
.collect::<Vec<_>>(),
139+
);
140+
141+
// No submodule expansions are being modified, so it's safe to backsync
142+
return rewrite_commit(
143+
ctx,
144+
bonsai,
145+
remapped_parents,
146+
mover_to_multi_mover(mover.clone()),
147+
source_repo,
148+
None,
149+
rewrite_opts,
150+
)
151+
.await
152+
.context("Failed to create small repo bonsai");
153+
};
127154

128155
let new_bonsai = run_and_log_stats_to_scuba(
129156
ctx,

eden/mononoke/commit_rewriting/cross_repo_sync/src/git_submodules/utils.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,25 @@ use futures::stream::Stream;
2222
use futures::stream::StreamExt;
2323
use futures::stream::TryStreamExt;
2424
use git_types::ObjectKind;
25+
use itertools::Itertools;
2526
use manifest::bonsai_diff;
2627
use manifest::BonsaiDiffFileChange;
2728
use manifest::Entry;
2829
use manifest::ManifestOps;
2930
use mononoke_types::hash::GitSha1;
3031
use mononoke_types::hash::RichGitSha1;
32+
use mononoke_types::BonsaiChangesetMut;
3133
use mononoke_types::ChangesetId;
3234
use mononoke_types::ContentId;
3335
use mononoke_types::FileType;
3436
use mononoke_types::FsnodeId;
3537
use mononoke_types::MPathElement;
3638
use mononoke_types::NonRootMPath;
39+
use movers::Mover;
3740
use repo_blobstore::RepoBlobstoreArc;
3841
use repo_derived_data::RepoDerivedDataRef;
3942

43+
use crate::git_submodules::expand::SubmoduleExpansionData;
4044
use crate::git_submodules::expand::SubmodulePath;
4145
use crate::git_submodules::in_memory_repo::InMemoryRepo;
4246
use crate::types::Repo;
@@ -308,3 +312,49 @@ pub(crate) fn build_recursive_submodule_deps<R: Repo>(
308312

309313
rec_small_repo_deps
310314
}
315+
316+
/// Returns the submodule expansions affected by a large repo changeset.
317+
///
318+
/// This could happen by directly modifying the submodule's expansion or its
319+
/// metadata file.
320+
pub(crate) fn get_submodule_expansions_affected<'a, R: Repo>(
321+
sm_exp_data: &SubmoduleExpansionData<'a, R>,
322+
// Bonsai from the large repo
323+
bonsai: &BonsaiChangesetMut,
324+
mover: Mover,
325+
) -> Result<Vec<NonRootMPath>> {
326+
let submodules_affected = sm_exp_data
327+
.submodule_deps
328+
.iter()
329+
.map(|(submodule_path, _)| {
330+
// Get the submodule's metadata file path
331+
let metadata_file_path = get_x_repo_submodule_metadata_file_path(
332+
&SubmodulePath(submodule_path.clone()),
333+
sm_exp_data.x_repo_submodule_metadata_file_prefix,
334+
)?;
335+
336+
let submodule_expansion_changed = bonsai
337+
.file_changes
338+
.iter()
339+
.map(|(p, _)| mover(p))
340+
.filter_map(Result::transpose)
341+
.process_results(|mut iter| {
342+
iter.any(|small_repo_path| {
343+
// File modified expansion directly
344+
submodule_path.is_prefix_of(&small_repo_path)
345+
// or the submodule's metadata file
346+
|| small_repo_path == metadata_file_path
347+
})
348+
})?;
349+
350+
if submodule_expansion_changed {
351+
return anyhow::Ok(Some(submodule_path.clone()));
352+
};
353+
354+
Ok(None)
355+
})
356+
.filter_map(Result::transpose)
357+
.collect::<Result<Vec<_>>>()?;
358+
359+
Ok(submodules_affected)
360+
}

0 commit comments

Comments
 (0)