@@ -32,6 +32,7 @@ class Context(BaseModel):
3232
3333class Operation (Enum ):
3434 MOVE = "move"
35+ SORT = "sort"
3536 COPY = "copy"
3637 DELETE = "delete"
3738
@@ -231,6 +232,18 @@ def move(ctx: Context, src, dst, dry_run):
231232 return operate (ctx , src , dst , operation_mode , dry_run )
232233
233234
235+ @cli .command ()
236+ @click .argument ("src" )
237+ @click .option ("--dry-run" , is_flag = True )
238+ @click .pass_obj
239+ def sort (ctx : Context , src , dry_run ):
240+ """
241+ Move files. In case of duplicates will take the shortest name.
242+ """
243+ operation_mode = Operation .SORT
244+ return operate (ctx , src , None , operation_mode , dry_run )
245+
246+
234247@cli .command ()
235248@click .argument ("src" )
236249@click .argument ("dst" , required = False )
@@ -346,7 +359,7 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
346359 f'Error "{ dst } " cannot be a subdirectory of { src } '
347360 )
348361
349- if operation_mode in (Operation .MOVE , Operation .COPY ):
362+ if operation_mode in (Operation .MOVE , Operation .COPY , Operation . SORT ):
350363 format_pattern = ctx .global_settings .format_pattern
351364 if dst_data and isinstance (dst_data , Catalogue ):
352365 format_pattern = format_pattern or dst_data .format_pattern
@@ -368,7 +381,7 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
368381 duplicated_discarded_files ,
369382 files_to_operate ,
370383 ) = extract_files (src_data )
371-
384+
372385 if operation_mode == Operation .DELETE :
373386 if dst_data :
374387 duplicate_files_across_directories = [
@@ -400,6 +413,9 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
400413 duplicated_files = duplicated_list_of_different_filenames
401414 )
402415
416+ elif operation_mode == Operation .SORT :
417+ files_to_process = files_to_operate
418+ dst_data = src_data
403419 else : # dst_data can only be Directory or Catalogue
404420 duplicate_files_across_directories = [
405421 file
@@ -477,6 +493,10 @@ def process_files(
477493 if isinstance (dst_data , Catalogue ):
478494 path_format = path_format or dst_data .format_pattern
479495 unknown_format_pattern = unknown_format_pattern or dst_data .unknown_format_pattern
496+ if operation_mode .SORT and isinstance (src_data , Catalogue ):
497+ path_format = path_format or src_data .format_pattern
498+ unknown_format_pattern = unknown_format_pattern or src_data .unknown_format_pattern
499+
480500
481501 tree = DirectoryTree ()
482502 skipped_tree = DirectoryTree ()
@@ -527,6 +547,9 @@ def process_file(file, dst_file_path, operation, dst_directory, dry_run):
527547
528548 path_available = dst_directory .is_path_available (dst_file_path )
529549 if not path_available :
550+ if operation .SORT :
551+ # TODO: check if it is same file, for now just avoid moving it
552+ return file
530553 logging .debug (f"Path { dst_file_path } not available, renaming file" )
531554 dst_file_path = dst_directory .find_new_path (dst_file_path )
532555
@@ -543,6 +566,10 @@ def process_file(file, dst_file_path, operation, dst_directory, dry_run):
543566 dst_directory .add_file (file )
544567 return file
545568
569+ if operation == Operation .SORT :
570+ file .move_file (dst_file_path )
571+ return file
572+
546573 if operation == Operation .COPY :
547574 new_file = file .clone_file (dst_file_path )
548575 dst_directory .add_file (new_file )
0 commit comments