diff --git a/src/history.rs b/src/history.rs index caeeccb24..54ca3679b 100644 --- a/src/history.rs +++ b/src/history.rs @@ -78,9 +78,11 @@ fn find_unapply_base( filtered: git2::Oid, ) -> super::JoshResult { if contained_in == git2::Oid::zero() { + tracing::info!("contained in zero",); return Ok(git2::Oid::zero()); } if let Some(original) = bm.get(&filtered) { + tracing::info!("Found in bm",); return Ok(*original); } let contained_in_commit = transaction.repo().find_commit(contained_in)?; @@ -96,10 +98,12 @@ fn find_unapply_base( let original = transaction.repo().find_commit(original?)?; if filtered == filter::apply_to_commit(filter, &original, transaction)? { bm.insert(filtered, original.id()); + tracing::info!("found original properly",); return Ok(original.id()); } } + tracing::info!("Didn't find original",); Ok(git2::Oid::zero()) } @@ -199,6 +203,71 @@ fn all_equal(a: git2::Parents, b: &[&git2::Commit]) -> bool { true } +fn find_oldest_similar_commit( + transaction: &cache::Transaction, + filter: filter::Filter, + unfiltered: git2::Oid, +) -> super::JoshResult { + let walk = { + let mut walk = transaction.repo().revwalk()?; + walk.set_sorting(git2::Sort::TOPOLOGICAL)?; + walk.push(unfiltered)?; + walk + }; + tracing::info!("oldest similar?"); + let unfiltered_commit = transaction.repo().find_commit(unfiltered)?; + let filtered = filter::apply_to_commit(filter, &unfiltered_commit, transaction)?; + let mut prev_rev = unfiltered; + for rev in walk { + let rev = rev?; + tracing::info!("next"); + let rev_commit = transaction.repo().find_commit(rev)?; + if filtered != filter::apply_to_commit(filter, &rev_commit, transaction)? { + tracing::info!("diff! {}", prev_rev); + return Ok(prev_rev); + } + prev_rev = rev; + } + tracing::info!("bottom"); + return Ok(unfiltered); +} + +fn find_new_branch_base( + transaction: &cache::Transaction, + bm: &mut std::collections::HashMap, + filter: filter::Filter, + contained_in: git2::Oid, + filtered: git2::Oid, +) -> super::JoshResult { + let walk = { + let mut walk = transaction.repo().revwalk()?; + walk.set_sorting(git2::Sort::TOPOLOGICAL)?; + walk.push(filtered)?; + walk + }; + tracing::info!("new branch base?"); + + for rev in walk { + let rev = rev?; + if let Ok(base) = find_unapply_base(transaction, bm, filter, contained_in, rev) { + if base != git2::Oid::zero() { + tracing::info!("new branch base: {:?}", base); + let base = + if let Ok(new_base) = find_oldest_similar_commit(transaction, filter, base) { + new_base + } else { + base + }; + tracing::info!("inserting in bm {}, {}", rev, base); + bm.insert(rev, base); + return Ok(rev); + } + } + } + tracing::info!("new branch base not found"); + return Ok(git2::Oid::zero()); +} + #[tracing::instrument(skip(transaction))] pub fn unapply_filter( transaction: &cache::Transaction, @@ -213,12 +282,30 @@ pub fn unapply_filter( let mut bm = std::collections::HashMap::new(); let mut ret = original_target; + let old = if old == git2::Oid::zero() { + match find_new_branch_base(transaction, &mut bm, filterobj, original_target, new) { + Ok(res) => { + tracing::info!("No error, branch base {} ", res); + res + } + Err(_) => { + tracing::info!("Error in new branch base"); + old + } + } + } else { + tracing::info!("Old not zero"); + old + }; + + tracing::info!("before walk"); + let walk = { let mut walk = transaction.repo().revwalk()?; walk.set_sorting(git2::Sort::REVERSE | git2::Sort::TOPOLOGICAL)?; walk.push(new)?; if walk.hide(old).is_ok() { - tracing::trace!("walk: hidden {}", old); + tracing::info!("walk: hidden {}", old); } else { tracing::warn!("walk: can't hide"); } @@ -230,6 +317,7 @@ pub fn unapply_filter( let s = tracing::span!(tracing::Level::TRACE, "walk commit", ?rev); let _e = s.enter(); + tracing::info!("walk commit: {:?}", rev); let module_commit = transaction.repo().find_commit(rev)?; diff --git a/tests/proxy/push_new_branch.t b/tests/proxy/push_new_branch.t new file mode 100644 index 000000000..987174a06 --- /dev/null +++ b/tests/proxy/push_new_branch.t @@ -0,0 +1,112 @@ +Setup + + $ . ${TESTDIR}/setup_test_env.sh + $ cd ${TESTTMP} + +Clone an empty repo + + $ git clone -q http://localhost:8001/real_repo.git >/dev/null 2>&1 + $ cd real_repo + +Commit a file in a root folder + + $ echo contents1 > file1 + $ git add file1 + $ git commit -m "add file1" + [master (root-commit) 0b4cf6c] add file1 + 1 file changed, 1 insertion(+) + create mode 100644 file1 + +Commit a file in a subfolder and push + + $ mkdir sub + $ echo contents2 > sub/file2 + $ git add sub + $ git commit -m "add file2" 1> /dev/null + $ git push 1> /dev/null + To http://localhost:8001/real_repo.git + * [new branch] master -> master + +Check commit SHA1 + $ SHA1=$(git log --max-count=1 --format="%H") + $ echo "${SHA1}" + 37c3f9a18f21fe53e0be9ea657220ba4537dbca7 + +Clone subfolder as a workspace + + $ cd ${TESTTMP} + $ git clone -q http://localhost:8002/real_repo.git:/sub.git + $ cd sub + +Check workspace contents + + $ ls + file2 + +Create a new branch and push it + + $ git switch -c new-branch + Switched to a new branch 'new-branch' + $ git push origin new-branch -o base=refs/heads/master 1> /dev/null + remote: josh-proxy + remote: response from upstream: + remote: To http://localhost:8001/real_repo.git + remote: * [new branch] JOSH_PUSH -> new-branch + remote: + remote: + To http://localhost:8002/real_repo.git:/sub.git + * [new branch] new-branch -> new-branch +Check the branch pushed + $ cd ${TESTTMP}/real_repo + $ git fetch + From http://localhost:8001/real_repo + * [new branch] new-branch -> origin/new-branch + $ [ "${SHA1}" = "$(git log --max-count=1 --format='%H' origin/new-branch)" ] || echo "SHA1 differs after push!" + +Add one more commit in the workspace + + $ cd ${TESTTMP}/sub + $ echo test > test.txt + $ git add test.txt + $ git commit -m "test commit" + [new-branch 751ef45] test commit + 1 file changed, 1 insertion(+) + create mode 100644 test.txt + $ git push origin new-branch -o base=refs/heads/master + remote: josh-proxy + remote: response from upstream: + remote: To http://localhost:8001/real_repo.git + remote: 37c3f9a..56dc1f7 JOSH_PUSH -> new-branch + remote: + remote: + To http://localhost:8002/real_repo.git:/sub.git + 28d2085..751ef45 new-branch -> new-branch + +Check the branch again + + $ cd ${TESTTMP}/real_repo + $ git fetch + From http://localhost:8001/real_repo + 37c3f9a..56dc1f7 new-branch -> origin/new-branch + $ [ "${SHA1}" = "$(git log --max-count=1 --skip=1 --format='%H' origin/new-branch)" ] || echo "SHA1 differs after push!" + + $ bash ${TESTDIR}/destroy_test_env.sh + "real_repo.git" = [':/sub'] + refs + |-- heads + |-- josh + | |-- filtered + | | `-- real_repo.git + | | `-- %3A%2Fsub + | | `-- HEAD + | `-- upstream + | `-- real_repo.git + | |-- HEAD + | `-- refs + | `-- heads + | |-- master + | `-- new-branch + |-- namespaces + `-- tags + + 11 directories, 4 files diff --git a/tests/proxy/unrelated_leak.t b/tests/proxy/unrelated_leak.t index e9e908596..4789bb088 100644 --- a/tests/proxy/unrelated_leak.t +++ b/tests/proxy/unrelated_leak.t @@ -41,6 +41,10 @@ Flushed credential cache $ echo contents2 > file4 $ git add . $ git commit -m "add file4" 1> /dev/null + + $ echo contents3 > file4 + $ git add . + $ git commit -m "edit file4" 1> /dev/null $ git push -o base=refs/heads/master origin master:refs/heads/from_filtered 2>&1 >/dev/null | sed -e 's/[ ]*$//g' remote: josh-proxy remote: response from upstream: @@ -55,27 +59,72 @@ Flushed credential cache remote: josh-proxy remote: response from upstream: remote: To http://localhost:8001/real_repo.git - remote: db0fd21..3f7ab67 JOSH_PUSH -> master + remote: db0fd21..e170e96 JOSH_PUSH -> master remote: remote: To http://localhost:8002/real_repo.git:/sub1.git - 0b4cf6c..37fad4a master -> master + 0b4cf6c..da0d1f3 master -> master $ cd ${TESTTMP}/real_repo $ git fetch From http://localhost:8001/real_repo - db0fd21..3f7ab67 master -> origin/master + db0fd21..e170e96 master -> origin/master * [new branch] from_filtered -> origin/from_filtered - $ git log --graph --pretty=%s origin/master - * add file4 - * unrelated on master - * add file1 - * initial - $ git log --graph --pretty=%s origin/from_filtered - * add file4 - * add file1 - + $ git log origin/master + commit e170e962d0fb4b94a491a176a7f39a6207ada3e8 + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + edit file4 + + commit 3f7ab67d01db03914916161b51dbda1a4635f8d2 + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + add file4 + + commit db0fd21be0dea377057285e6119361753587f667 + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + unrelated on master + + commit a11885ec53fe483199d9515bf4662e5cf94d9a9e + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + add file1 + + commit 66472b80301b889cf27a92d43fc2c2d8fbf4729d + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + initial + $ git log origin/from_filtered + commit 865c34e9a2c40198324cdc2fc796827653cb11df + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + edit file4 + + commit 42e0161c1ad82c05895e0f2caeae95925ac5ae6a + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + add file4 + + commit a11885ec53fe483199d9515bf4662e5cf94d9a9e + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + add file1 + + commit 66472b80301b889cf27a92d43fc2c2d8fbf4729d + Author: Josh + Date: Thu Apr 7 22:13:13 2005 +0000 + + initial $ . ${TESTDIR}/destroy_test_env.sh "real_repo.git" = [ ':/sub1',