@@ -17,7 +17,9 @@ static const char * const worktree_usage[] = {
1717 N_ ("git worktree add [<options>] <path> [<commit-ish>]" ),
1818 N_ ("git worktree list [<options>]" ),
1919 N_ ("git worktree lock [<options>] <path>" ),
20+ N_ ("git worktree move <worktree> <new-path>" ),
2021 N_ ("git worktree prune [<options>]" ),
22+ N_ ("git worktree remove [<options>] <worktree>" ),
2123 N_ ("git worktree unlock <path>" ),
2224 NULL
2325};
@@ -619,6 +621,220 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
619621 return ret ;
620622}
621623
624+ static void validate_no_submodules (const struct worktree * wt )
625+ {
626+ struct index_state istate = { NULL };
627+ int i , found_submodules = 0 ;
628+
629+ if (read_index_from (& istate , worktree_git_path (wt , "index" ),
630+ get_worktree_git_dir (wt )) > 0 ) {
631+ for (i = 0 ; i < istate .cache_nr ; i ++ ) {
632+ struct cache_entry * ce = istate .cache [i ];
633+
634+ if (S_ISGITLINK (ce -> ce_mode )) {
635+ found_submodules = 1 ;
636+ break ;
637+ }
638+ }
639+ }
640+ discard_index (& istate );
641+
642+ if (found_submodules )
643+ die (_ ("working trees containing submodules cannot be moved or removed" ));
644+ }
645+
646+ static int move_worktree (int ac , const char * * av , const char * prefix )
647+ {
648+ struct option options [] = {
649+ OPT_END ()
650+ };
651+ struct worktree * * worktrees , * wt ;
652+ struct strbuf dst = STRBUF_INIT ;
653+ struct strbuf errmsg = STRBUF_INIT ;
654+ const char * reason ;
655+ char * path ;
656+
657+ ac = parse_options (ac , av , prefix , options , worktree_usage , 0 );
658+ if (ac != 2 )
659+ usage_with_options (worktree_usage , options );
660+
661+ path = prefix_filename (prefix , av [1 ]);
662+ strbuf_addstr (& dst , path );
663+ free (path );
664+
665+ worktrees = get_worktrees (0 );
666+ wt = find_worktree (worktrees , prefix , av [0 ]);
667+ if (!wt )
668+ die (_ ("'%s' is not a working tree" ), av [0 ]);
669+ if (is_main_worktree (wt ))
670+ die (_ ("'%s' is a main working tree" ), av [0 ]);
671+ if (is_directory (dst .buf )) {
672+ const char * sep = find_last_dir_sep (wt -> path );
673+
674+ if (!sep )
675+ die (_ ("could not figure out destination name from '%s'" ),
676+ wt -> path );
677+ strbuf_trim_trailing_dir_sep (& dst );
678+ strbuf_addstr (& dst , sep );
679+ }
680+ if (file_exists (dst .buf ))
681+ die (_ ("target '%s' already exists" ), dst .buf );
682+
683+ validate_no_submodules (wt );
684+
685+ reason = is_worktree_locked (wt );
686+ if (reason ) {
687+ if (* reason )
688+ die (_ ("cannot move a locked working tree, lock reason: %s" ),
689+ reason );
690+ die (_ ("cannot move a locked working tree" ));
691+ }
692+ if (validate_worktree (wt , & errmsg , 0 ))
693+ die (_ ("validation failed, cannot move working tree: %s" ),
694+ errmsg .buf );
695+ strbuf_release (& errmsg );
696+
697+ if (rename (wt -> path , dst .buf ) == -1 )
698+ die_errno (_ ("failed to move '%s' to '%s'" ), wt -> path , dst .buf );
699+
700+ update_worktree_location (wt , dst .buf );
701+
702+ strbuf_release (& dst );
703+ free_worktrees (worktrees );
704+ return 0 ;
705+ }
706+
707+ /*
708+ * Note, "git status --porcelain" is used to determine if it's safe to
709+ * delete a whole worktree. "git status" does not ignore user
710+ * configuration, so if a normal "git status" shows "clean" for the
711+ * user, then it's ok to remove it.
712+ *
713+ * This assumption may be a bad one. We may want to ignore
714+ * (potentially bad) user settings and only delete a worktree when
715+ * it's absolutely safe to do so from _our_ point of view because we
716+ * know better.
717+ */
718+ static void check_clean_worktree (struct worktree * wt ,
719+ const char * original_path )
720+ {
721+ struct argv_array child_env = ARGV_ARRAY_INIT ;
722+ struct child_process cp ;
723+ char buf [1 ];
724+ int ret ;
725+
726+ /*
727+ * Until we sort this out, all submodules are "dirty" and
728+ * will abort this function.
729+ */
730+ validate_no_submodules (wt );
731+
732+ argv_array_pushf (& child_env , "%s=%s/.git" ,
733+ GIT_DIR_ENVIRONMENT , wt -> path );
734+ argv_array_pushf (& child_env , "%s=%s" ,
735+ GIT_WORK_TREE_ENVIRONMENT , wt -> path );
736+ memset (& cp , 0 , sizeof (cp ));
737+ argv_array_pushl (& cp .args , "status" ,
738+ "--porcelain" , "--ignore-submodules=none" ,
739+ NULL );
740+ cp .env = child_env .argv ;
741+ cp .git_cmd = 1 ;
742+ cp .dir = wt -> path ;
743+ cp .out = -1 ;
744+ ret = start_command (& cp );
745+ if (ret )
746+ die_errno (_ ("failed to run 'git status' on '%s'" ),
747+ original_path );
748+ ret = xread (cp .out , buf , sizeof (buf ));
749+ if (ret )
750+ die (_ ("'%s' is dirty, use --force to delete it" ),
751+ original_path );
752+ close (cp .out );
753+ ret = finish_command (& cp );
754+ if (ret )
755+ die_errno (_ ("failed to run 'git status' on '%s', code %d" ),
756+ original_path , ret );
757+ }
758+
759+ static int delete_git_work_tree (struct worktree * wt )
760+ {
761+ struct strbuf sb = STRBUF_INIT ;
762+ int ret = 0 ;
763+
764+ strbuf_addstr (& sb , wt -> path );
765+ if (remove_dir_recursively (& sb , 0 )) {
766+ error_errno (_ ("failed to delete '%s'" ), sb .buf );
767+ ret = -1 ;
768+ }
769+ strbuf_release (& sb );
770+ return ret ;
771+ }
772+
773+ static int delete_git_dir (struct worktree * wt )
774+ {
775+ struct strbuf sb = STRBUF_INIT ;
776+ int ret = 0 ;
777+
778+ strbuf_addstr (& sb , git_common_path ("worktrees/%s" , wt -> id ));
779+ if (remove_dir_recursively (& sb , 0 )) {
780+ error_errno (_ ("failed to delete '%s'" ), sb .buf );
781+ ret = -1 ;
782+ }
783+ strbuf_release (& sb );
784+ return ret ;
785+ }
786+
787+ static int remove_worktree (int ac , const char * * av , const char * prefix )
788+ {
789+ int force = 0 ;
790+ struct option options [] = {
791+ OPT_BOOL (0 , "force" , & force ,
792+ N_ ("force removing even if the worktree is dirty" )),
793+ OPT_END ()
794+ };
795+ struct worktree * * worktrees , * wt ;
796+ struct strbuf errmsg = STRBUF_INIT ;
797+ const char * reason ;
798+ int ret = 0 ;
799+
800+ ac = parse_options (ac , av , prefix , options , worktree_usage , 0 );
801+ if (ac != 1 )
802+ usage_with_options (worktree_usage , options );
803+
804+ worktrees = get_worktrees (0 );
805+ wt = find_worktree (worktrees , prefix , av [0 ]);
806+ if (!wt )
807+ die (_ ("'%s' is not a working tree" ), av [0 ]);
808+ if (is_main_worktree (wt ))
809+ die (_ ("'%s' is a main working tree" ), av [0 ]);
810+ reason = is_worktree_locked (wt );
811+ if (reason ) {
812+ if (* reason )
813+ die (_ ("cannot remove a locked working tree, lock reason: %s" ),
814+ reason );
815+ die (_ ("cannot remove a locked working tree" ));
816+ }
817+ if (validate_worktree (wt , & errmsg , WT_VALIDATE_WORKTREE_MISSING_OK ))
818+ die (_ ("validation failed, cannot remove working tree: %s" ),
819+ errmsg .buf );
820+ strbuf_release (& errmsg );
821+
822+ if (file_exists (wt -> path )) {
823+ if (!force )
824+ check_clean_worktree (wt , av [0 ]);
825+
826+ ret |= delete_git_work_tree (wt );
827+ }
828+ /*
829+ * continue on even if ret is non-zero, there's no going back
830+ * from here.
831+ */
832+ ret |= delete_git_dir (wt );
833+
834+ free_worktrees (worktrees );
835+ return ret ;
836+ }
837+
622838int cmd_worktree (int ac , const char * * av , const char * prefix )
623839{
624840 struct option options [] = {
@@ -641,5 +857,9 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
641857 return lock_worktree (ac - 1 , av + 1 , prefix );
642858 if (!strcmp (av [1 ], "unlock" ))
643859 return unlock_worktree (ac - 1 , av + 1 , prefix );
860+ if (!strcmp (av [1 ], "move" ))
861+ return move_worktree (ac - 1 , av + 1 , prefix );
862+ if (!strcmp (av [1 ], "remove" ))
863+ return remove_worktree (ac - 1 , av + 1 , prefix );
644864 usage_with_options (worktree_usage , options );
645865}
0 commit comments