@@ -16,8 +16,6 @@ import {
1616 normalize ,
1717 virtualFs ,
1818} from '@angular-devkit/core' ;
19- import { ReadonlyHost } from '../../../core/src/virtual-fs/host' ;
20- import { CordHostRecord } from '../../../core/src/virtual-fs/host/record' ;
2119import {
2220 ContentHasMutatedException ,
2321 FileAlreadyExistException ,
@@ -95,9 +93,10 @@ export class HostDirEntry implements DirEntry {
9593
9694
9795export class HostTree implements Tree {
98- private _id = _uniqueId ++ ;
96+ private readonly _id = -- _uniqueId ;
9997 private _record : virtualFs . CordHost ;
10098 private _recordSync : virtualFs . SyncDelegateHost ;
99+ private _ancestry = new Set < number > ( ) ;
101100
102101 private _dirCache = new Map < Path , HostDirEntry > ( ) ;
103102
@@ -116,79 +115,28 @@ export class HostTree implements Tree {
116115 }
117116
118117 protected _willCreate ( path : Path ) {
119- let current : ReadonlyHost = this . _record ;
120- while ( current && current != this . _backend ) {
121- if ( ! ( current instanceof virtualFs . CordHost ) ) {
122- break ;
123- }
124-
125- if ( current . willCreate ( path ) ) {
126- return true ;
127- }
128-
129- current = current . backend ;
130- }
131-
132- return false ;
118+ return this . _record . willCreate ( path ) ;
133119 }
134- protected _willOverwrite ( path : Path ) {
135- let current : ReadonlyHost = this . _record ;
136- while ( current && current != this . _backend ) {
137- if ( ! ( current instanceof virtualFs . CordHost ) ) {
138- break ;
139- }
140120
141- if ( current . willOverwrite ( path ) ) {
142- return true ;
143- }
144-
145- current = current . backend ;
146- }
147-
148- return false ;
121+ protected _willOverwrite ( path : Path ) {
122+ return this . _record . willOverwrite ( path ) ;
149123 }
150- protected _willDelete ( path : Path ) {
151- let current : ReadonlyHost = this . _record ;
152- while ( current && current != this . _backend ) {
153- if ( ! ( current instanceof virtualFs . CordHost ) ) {
154- break ;
155- }
156124
157- if ( current . willDelete ( path ) ) {
158- return true ;
159- }
160-
161- current = current . backend ;
162- }
163-
164- return false ;
125+ protected _willDelete ( path : Path ) {
126+ return this . _record . willDelete ( path ) ;
165127 }
166- protected _willRename ( path : Path ) {
167- let current : ReadonlyHost = this . _record ;
168- while ( current && current != this . _backend ) {
169- if ( ! ( current instanceof virtualFs . CordHost ) ) {
170- break ;
171- }
172-
173- if ( current . willRename ( path ) ) {
174- return true ;
175- }
176128
177- current = current . backend ;
178- }
179-
180- return false ;
129+ protected _willRename ( path : Path ) {
130+ return this . _record . willRename ( path ) ;
181131 }
182132
183-
184133 branch ( ) : Tree {
185- // Freeze our own records, and swap. This is so the branch and this Tree don't share the same
186- // history anymore.
187- const record = this . _record ;
188- this . _record = new virtualFs . CordHost ( record ) ;
189- this . _recordSync = new virtualFs . SyncDelegateHost ( this . _record ) ;
134+ const branchedTree = new HostTree ( this . _backend ) ;
135+ branchedTree . _record = this . _record . clone ( ) ;
136+ branchedTree . _recordSync = new virtualFs . SyncDelegateHost ( branchedTree . _record ) ;
137+ branchedTree . _ancestry = new Set ( this . _ancestry ) . add ( this . _id ) ;
190138
191- return new HostTree ( record ) ;
139+ return branchedTree ;
192140 }
193141
194142 merge ( other : Tree , strategy : MergeStrategy = MergeStrategy . Default ) : void {
@@ -197,6 +145,12 @@ export class HostTree implements Tree {
197145 return ;
198146 }
199147
148+ if ( other instanceof HostTree && other . _ancestry . has ( this . _id ) ) {
149+ // Workaround for merging a branch back into one of its ancestors
150+ // More complete branch point tracking is required to avoid
151+ strategy |= MergeStrategy . Overwrite ;
152+ }
153+
200154 const creationConflictAllowed =
201155 ( strategy & MergeStrategy . AllowCreationConflict ) == MergeStrategy . AllowCreationConflict ;
202156 const overwriteConflictAllowed =
@@ -205,15 +159,17 @@ export class HostTree implements Tree {
205159 ( strategy & MergeStrategy . AllowOverwriteConflict ) == MergeStrategy . AllowDeleteConflict ;
206160
207161 other . actions . forEach ( action => {
208- if ( action . id === this . _id ) {
209- return ;
210- }
211-
212162 switch ( action . kind ) {
213163 case 'c' : {
214164 const { path, content } = action ;
215165
216166 if ( ( this . _willCreate ( path ) || this . _willOverwrite ( path ) ) ) {
167+ const existingContent = this . read ( path ) ;
168+ if ( existingContent && content . equals ( existingContent ) ) {
169+ // Identical outcome; no action required
170+ return ;
171+ }
172+
217173 if ( ! creationConflictAllowed ) {
218174 throw new MergeConflictException ( path ) ;
219175 }
@@ -228,21 +184,41 @@ export class HostTree implements Tree {
228184
229185 case 'o' : {
230186 const { path, content } = action ;
187+ if ( this . _willDelete ( path ) && ! overwriteConflictAllowed ) {
188+ throw new MergeConflictException ( path ) ;
189+ }
231190
232191 // Ignore if content is the same (considered the same change).
233- if ( this . _willOverwrite ( path ) && ! overwriteConflictAllowed ) {
234- throw new MergeConflictException ( path ) ;
192+ if ( this . _willOverwrite ( path ) ) {
193+ const existingContent = this . read ( path ) ;
194+ if ( existingContent && content . equals ( existingContent ) ) {
195+ // Identical outcome; no action required
196+ return ;
197+ }
198+
199+ if ( ! overwriteConflictAllowed ) {
200+ throw new MergeConflictException ( path ) ;
201+ }
235202 }
236203 // We use write here as merge validation has already been done, and we want to let
237204 // the CordHost do its job.
238- this . _record . overwrite ( path , content as { } as virtualFs . FileBuffer ) . subscribe ( ) ;
205+ this . _record . write ( path , content as { } as virtualFs . FileBuffer ) . subscribe ( ) ;
239206
240207 return ;
241208 }
242209
243210 case 'r' : {
244211 const { path, to } = action ;
212+ if ( this . _willDelete ( path ) ) {
213+ throw new MergeConflictException ( path ) ;
214+ }
215+
245216 if ( this . _willRename ( path ) ) {
217+ if ( this . _record . willRenameTo ( path , to ) ) {
218+ // Identical outcome; no action required
219+ return ;
220+ }
221+
246222 // No override possible for renaming.
247223 throw new MergeConflictException ( path ) ;
248224 }
@@ -253,9 +229,16 @@ export class HostTree implements Tree {
253229
254230 case 'd' : {
255231 const { path } = action ;
256- if ( this . _willDelete ( path ) && ! deleteConflictAllowed ) {
232+ if ( this . _willDelete ( path ) ) {
233+ // TODO: This should technically check the content (e.g., hash on delete)
234+ // Identical outcome; no action required
235+ return ;
236+ }
237+
238+ if ( ! this . exists ( path ) && ! deleteConflictAllowed ) {
257239 throw new MergeConflictException ( path ) ;
258240 }
241+
259242 this . _recordSync . delete ( path ) ;
260243
261244 return ;
@@ -372,16 +355,7 @@ export class HostTree implements Tree {
372355 get actions ( ) : Action [ ] {
373356 // Create a list of all records until we hit our original backend. This is to support branches
374357 // that diverge from each others.
375- const allRecords : CordHostRecord [ ] = [ ...this . _record . records ( ) ] ;
376- let current = this . _record . backend ;
377- while ( current != this . _backend ) {
378- if ( ! ( current instanceof virtualFs . CordHost ) ) {
379- break ;
380- }
381-
382- allRecords . unshift ( ...current . records ( ) ) ;
383- current = current . backend ;
384- }
358+ const allRecords = [ ...this . _record . records ( ) ] ;
385359
386360 return clean (
387361 allRecords
@@ -426,3 +400,17 @@ export class HostTree implements Tree {
426400 ) ;
427401 }
428402}
403+
404+ export class HostCreateTree extends HostTree {
405+ constructor ( host : virtualFs . ReadonlyHost ) {
406+ super ( ) ;
407+
408+ const tempHost = new HostTree ( host ) ;
409+ tempHost . visit ( path => {
410+ const content = tempHost . read ( path ) ;
411+ if ( content ) {
412+ this . create ( path , content ) ;
413+ }
414+ } ) ;
415+ }
416+ }
0 commit comments