@@ -11,7 +11,7 @@ use flate2::write::GzEncoder;
11
11
use flate2:: Compression ;
12
12
use futures:: prelude:: * ;
13
13
use indicatif:: ProgressBar ;
14
- use std:: collections:: HashSet ;
14
+ use std:: collections:: { HashSet , VecDeque } ;
15
15
16
16
use std:: io:: { BufReader , Read } ;
17
17
use std:: sync:: Arc ;
@@ -75,13 +75,6 @@ async fn validate_repo_is_pushable(
75
75
commit_reader : & CommitReader ,
76
76
head_commit : & Commit ,
77
77
) -> Result < bool , OxenError > {
78
- // Make sure the remote branch is not ahead of the local branch
79
- // log::debug!(
80
- // "validating repo is pushable for commit {:#?} and branch {:#?}",
81
- // head_commit,
82
- // branch
83
- // );
84
-
85
78
if remote_is_ahead_of_local ( head_commit, remote_repo, commit_reader, branch) . await ? {
86
79
log:: debug!( "remote is ahead of local for commit {:#?}" , head_commit) ;
87
80
if api:: remote:: commits:: can_push ( remote_repo, & branch. name , local_repo, head_commit)
@@ -94,17 +87,6 @@ async fn validate_repo_is_pushable(
94
87
return Err ( OxenError :: upstream_merge_conflict ( ) ) ;
95
88
}
96
89
}
97
- // } else {
98
- // log::debug!("remote is not ahead of local for commit {:#?}", head_commit);
99
- // }
100
-
101
- if cannot_push_incomplete_history ( local_repo, remote_repo, head_commit, branch) . await ? {
102
- log:: debug!(
103
- "cannot_push_incomplete_history is true for commit {:#?}" ,
104
- head_commit
105
- ) ;
106
- return Err ( OxenError :: incomplete_local_history ( ) ) ;
107
- }
108
90
109
91
Ok ( false )
110
92
}
@@ -200,18 +182,24 @@ pub async fn try_push_remote_repo(
200
182
mut head_commit : Commit ,
201
183
requires_merge : bool ,
202
184
) -> Result < ( ) , OxenError > {
203
- let commits_to_sync =
185
+ let commits_to_push =
204
186
get_commit_objects_to_sync ( local_repo, remote_repo, & head_commit, & branch) . await ?;
205
187
188
+ log:: debug!( "got these commits to push" ) ;
189
+
190
+ if !commits_to_push_are_synced ( local_repo, & commits_to_push) ? {
191
+ return Err ( OxenError :: incomplete_local_history ( ) ) ;
192
+ }
193
+
206
194
log:: debug!(
207
195
"push_remote_repo commit order after get_commit_objects_to_sync {:?}" ,
208
- commits_to_sync
196
+ commits_to_push
209
197
) ;
210
198
211
199
let maybe_remote_branch = api:: remote:: branches:: get_by_name ( remote_repo, & branch. name ) . await ?;
212
200
213
201
let ( unsynced_entries, _total_size) =
214
- push_missing_commit_objects ( local_repo, remote_repo, & commits_to_sync , & branch) . await ?;
202
+ push_missing_commit_objects ( local_repo, remote_repo, & commits_to_push , & branch) . await ?;
215
203
216
204
log:: debug!( "🐂 Identifying unsynced commits dbs..." ) ;
217
205
let unsynced_db_commits =
@@ -301,18 +289,16 @@ async fn get_commit_objects_to_sync(
301
289
branch : & Branch ,
302
290
) -> Result < Vec < Commit > , OxenError > {
303
291
let remote_branch = api:: remote:: branches:: get_by_name ( remote_repo, & branch. name ) . await ?;
304
-
292
+ let commit_reader = CommitReader :: new ( local_repo ) ? ;
305
293
let mut commits_to_sync: Vec < Commit > ;
306
- // TODO: If remote branch does not yet, recreates all commits regardless of shared history.
307
- // Not a huge deal performance-wise right now, but could be for very commit-heavy repos
308
294
if let Some ( remote_branch) = remote_branch {
309
295
log:: debug!(
310
296
"get_commit_objects_to_sync found remote branch {:?}, calculating missing commits between local and remote heads" , remote_branch
311
297
) ;
312
298
let remote_commit = api:: remote:: commits:: get_by_id ( remote_repo, & remote_branch. commit_id )
313
299
. await ?
314
300
. unwrap ( ) ;
315
- let commit_reader = CommitReader :: new ( local_repo ) ? ;
301
+
316
302
let merger = Merger :: new ( local_repo) ?;
317
303
commits_to_sync =
318
304
merger. list_commits_between_commits ( & commit_reader, & remote_commit, local_commit) ?;
@@ -333,9 +319,31 @@ async fn get_commit_objects_to_sync(
333
319
. any ( |remote_commit| remote_commit. id == commit. id )
334
320
} ) ;
335
321
} else {
336
- // Branch does not exist on remote yet - get all commits?
337
- log:: debug!( "get_commit_objects_to_sync remote branch does not exist, getting all commits from local head" ) ;
338
- commits_to_sync = api:: local:: commits:: list_from ( local_repo, & local_commit. id ) ?;
322
+ // Remote branch does not exist. Find commits to push with reference to whatever
323
+ // remote branch head comes first in the local newbranch history, aka what it was branched off of.
324
+
325
+ // Early return to avoid checking for remote commits: if full local history and no remote branch,
326
+ // push full local branch history.
327
+ if api:: local:: commits:: commit_history_is_complete ( local_repo, local_commit) {
328
+ return api:: local:: commits:: list_from ( local_repo, & local_commit. id ) ;
329
+ }
330
+
331
+ // Otherwise, find the remote commit that the local branch was branched off of and push everything since then.
332
+ let all_commits = api:: remote:: commits:: list_all ( remote_repo) . await ?;
333
+ log:: debug!( "got all remote commits as {:#?}" , all_commits) ;
334
+ let maybe_remote_commit =
335
+ find_latest_local_commit_synced ( local_repo, local_commit, & all_commits) ?;
336
+
337
+ if let Some ( remote_commit) = maybe_remote_commit {
338
+ let merger = Merger :: new ( local_repo) ?;
339
+ commits_to_sync = merger. list_commits_between_commits (
340
+ & commit_reader,
341
+ & remote_commit,
342
+ local_commit,
343
+ ) ?;
344
+ } else {
345
+ commits_to_sync = api:: local:: commits:: list_from ( local_repo, & local_commit. id ) ?;
346
+ }
339
347
}
340
348
341
349
// Order from BASE to HEAD
@@ -474,54 +482,47 @@ async fn remote_is_ahead_of_local(
474
482
Ok ( !local_head. has_ancestor ( & remote_commit. id , reader) ?)
475
483
}
476
484
477
- async fn cannot_push_incomplete_history (
485
+ fn find_latest_local_commit_synced (
478
486
local_repo : & LocalRepository ,
479
- remote_repo : & RemoteRepository ,
480
487
local_head : & Commit ,
481
- branch : & Branch ,
482
- ) -> Result < bool , OxenError > {
483
- log:: debug!( "Checking if we can push incomplete history." ) ;
484
- match api:: remote:: commits:: list_commit_history ( remote_repo, & branch. name ) . await {
485
- Err ( _) => {
486
- return Ok ( !api:: local:: commits:: commit_history_is_complete (
487
- local_repo, local_head,
488
- ) ) ;
488
+ remote_commits : & Vec < Commit > ,
489
+ ) -> Result < Option < Commit > , OxenError > {
490
+ let commit_reader = CommitReader :: new ( local_repo) . unwrap ( ) ;
491
+ let mut commits_set: HashSet < String > = HashSet :: new ( ) ;
492
+ for remote_commit in remote_commits {
493
+ commits_set. insert ( remote_commit. id . clone ( ) ) ;
494
+ }
495
+ // let mut current_commit = local_head.clone();
496
+ let mut queue: VecDeque < Commit > = VecDeque :: new ( ) ;
497
+ queue. push_back ( local_head. clone ( ) ) ;
498
+
499
+ while !queue. is_empty ( ) {
500
+ let current_commit = queue. pop_front ( ) . unwrap ( ) ;
501
+ if commits_set. contains ( & current_commit. id ) {
502
+ return Ok ( Some ( current_commit) ) ;
489
503
}
490
- Ok ( remote_history) => {
491
- let remote_head = remote_history. first ( ) . unwrap ( ) ;
492
- log:: debug!(
493
- "Checking between local head {:?} and remote head {:?} on branch {}" ,
494
- local_head,
495
- remote_head,
496
- branch. name
497
- ) ;
498
-
499
- let commit_reader = CommitReader :: new ( local_repo) ?;
500
- let merger = Merger :: new ( local_repo) ?;
501
-
502
- let commits_to_push =
503
- merger. list_commits_between_commits ( & commit_reader, remote_head, local_head) ?;
504
-
505
- let commits_to_push: Vec < Commit > = commits_to_push
506
- . into_iter ( )
507
- . filter ( |commit| {
508
- !remote_history
509
- . iter ( )
510
- . any ( |remote_commit| remote_commit. id == commit. id )
511
- } )
512
- . collect ( ) ;
513
-
514
- log:: debug!( "Found the following commits_to_push: {:?}" , commits_to_push) ;
515
- // Ensure all `commits_to_push` are synced
516
- for commit in commits_to_push {
517
- if !index:: commit_sync_status:: commit_is_synced ( local_repo, & commit) {
518
- return Ok ( true ) ;
519
- }
520
- }
504
+ for parent_id in current_commit. parent_ids . iter ( ) {
505
+ let parent_commit = commit_reader. get_commit_by_id ( parent_id) ?;
506
+ let Some ( parent_commit) = parent_commit else {
507
+ return Err ( OxenError :: local_parent_link_broken ( & current_commit. id ) ) ;
508
+ } ;
509
+ queue. push_back ( parent_commit) ;
521
510
}
522
511
}
512
+ Ok ( None )
513
+ }
523
514
524
- Ok ( false )
515
+ fn commits_to_push_are_synced (
516
+ local_repo : & LocalRepository ,
517
+ commits_to_push : & Vec < Commit > ,
518
+ ) -> Result < bool , OxenError > {
519
+ for commit in commits_to_push {
520
+ if !index:: commit_sync_status:: commit_is_synced ( local_repo, commit) {
521
+ log:: debug!( "commit is not synced {:?}" , commit) ;
522
+ return Ok ( false ) ;
523
+ }
524
+ }
525
+ Ok ( true )
525
526
}
526
527
527
528
async fn poll_until_synced (
0 commit comments