@@ -24,9 +24,9 @@ import {
2424 readFileMetadata ,
2525 throwOnConflictingBranches ,
2626} from 'decap-cms-lib-util' ;
27- import { dirname } from 'path' ;
2827import { oneLine } from 'common-tags' ;
2928import { parse } from 'what-the-diff' ;
29+ import { dirname } from 'path' ;
3030
3131import type {
3232 ApiRequest ,
@@ -432,45 +432,63 @@ export default class API {
432432 commitMessage,
433433 branch,
434434 parentSha,
435- } : { commitMessage : string ; branch : string ; parentSha ?: string } ,
435+ hasSubfolders = true ,
436+ } : { commitMessage : string ; branch : string ; parentSha ?: string ; hasSubfolders ?: boolean } ,
436437 ) {
437438 const formData = new FormData ( ) ;
438- const toMove : { from : string ; to : string ; contentBlob : Blob } [ ] = [ ] ;
439+ const toMove : { from : string ; to : string ; contentBlob : Blob ; hasSubfolders : boolean } [ ] = [ ] ;
439440 files . forEach ( file => {
440441 if ( file . delete ) {
441442 // delete the file
442443 formData . append ( 'files' , file . path ) ;
443444 } else if ( file . newPath ) {
444445 const contentBlob = get ( file , 'fileObj' , new Blob ( [ ( file as DataFile ) . raw ] ) ) ;
445- toMove . push ( { from : file . path , to : file . newPath , contentBlob } ) ;
446+ toMove . push ( { from : file . path , to : file . newPath , contentBlob, hasSubfolders } ) ;
446447 } else {
447448 // add/modify the file
448449 const contentBlob = get ( file , 'fileObj' , new Blob ( [ ( file as DataFile ) . raw ] ) ) ;
449450 // Third param is filename header, in case path is `message`, `branch`, etc.
450451 formData . append ( file . path , contentBlob , basename ( file . path ) ) ;
451452 }
452453 } ) ;
453- for ( const { from, to, contentBlob } of toMove ) {
454- const sourceDir = dirname ( from ) ;
455- const destDir = dirname ( to ) ;
456- const filesBranch = parentSha ? this . branch : branch ;
457- const files = await this . listAllFiles ( sourceDir , 100 , filesBranch ) ;
458- for ( const file of files ) {
454+ for ( const { from, to, contentBlob, hasSubfolders } of toMove ) {
455+ if ( ! hasSubfolders ) {
456+ // New behavior (subfolders: false): Only move the specific file
459457 // to move a file in Bitbucket we need to delete the old path
460458 // and upload the file content to the new path
461459 // NOTE: this is very wasteful, and also the Bitbucket `diff` API
462460 // reports these files as deleted+added instead of renamed
463461 // delete current path
464- formData . append ( 'files' , file . path ) ;
462+ formData . append ( 'files' , from ) ;
465463 // create in new path
466- const content =
467- file . path === from
468- ? contentBlob
469- : await this . readFile ( file . path , null , {
470- branch : filesBranch ,
471- parseText : false ,
472- } ) ;
473- formData . append ( file . path . replace ( sourceDir , destDir ) , content , basename ( file . path ) ) ;
464+ formData . append ( to , contentBlob , basename ( to ) ) ;
465+ } else {
466+ // Legacy behavior (subfolders: true, default): Move all files in the directory
467+ const sourceDir = dirname ( from ) ;
468+ const destDir = dirname ( to ) ;
469+ const filesBranch = parentSha ? this . branch : branch ;
470+ const files = await this . listAllFiles ( sourceDir , 100 , filesBranch ) ;
471+ for ( const file of files ) {
472+ // to move a file in Bitbucket we need to delete the old path
473+ // and upload the file content to the new path
474+ // NOTE: this is very wasteful, and also the Bitbucket `diff` API
475+ // reports these files as deleted+added instead of renamed
476+ // delete current path
477+ formData . append ( 'files' , file . path ) ;
478+ // create in new path
479+ const content =
480+ file . path === from
481+ ? contentBlob
482+ : await this . readFile ( file . path , null , {
483+ branch : filesBranch ,
484+ parseText : false ,
485+ } ) ;
486+ formData . append (
487+ file . path . replace ( sourceDir , destDir ) ,
488+ content as Blob ,
489+ basename ( file . path ) ,
490+ ) ;
491+ }
474492 }
475493 }
476494
@@ -508,11 +526,16 @@ export default class API {
508526
509527 async persistFiles ( dataFiles : DataFile [ ] , mediaFiles : AssetProxy [ ] , options : PersistOptions ) {
510528 const files = [ ...dataFiles , ...mediaFiles ] ;
529+ const hasSubfolders = options . hasSubfolders !== false ; // default to true
511530 if ( options . useWorkflow ) {
512531 const slug = dataFiles [ 0 ] . slug ;
513532 return this . editorialWorkflowGit ( files , slug , options ) ;
514533 } else {
515- return this . uploadFiles ( files , { commitMessage : options . commitMessage , branch : this . branch } ) ;
534+ return this . uploadFiles ( files , {
535+ commitMessage : options . commitMessage ,
536+ branch : this . branch ,
537+ hasSubfolders,
538+ } ) ;
516539 }
517540 }
518541
@@ -599,12 +622,14 @@ export default class API {
599622 const contentKey = generateContentKey ( options . collectionName as string , slug ) ;
600623 const branch = branchFromContentKey ( contentKey ) ;
601624 const unpublished = options . unpublished || false ;
625+ const hasSubfolders = options . hasSubfolders !== false ; // default to true
602626 if ( ! unpublished ) {
603627 const defaultBranchSha = await this . branchCommitSha ( this . branch ) ;
604628 await this . uploadFiles ( files , {
605629 commitMessage : options . commitMessage ,
606630 branch,
607631 parentSha : defaultBranchSha ,
632+ hasSubfolders,
608633 } ) ;
609634 await this . createPullRequest (
610635 branch ,
@@ -624,6 +649,7 @@ export default class API {
624649 await this . uploadFiles ( [ ...files , ...toDelete ] , {
625650 commitMessage : options . commitMessage ,
626651 branch,
652+ hasSubfolders,
627653 } ) ;
628654 }
629655 }
0 commit comments