@@ -610,6 +610,29 @@ static int remove_empty_directories(char *file)
610610 return remove_empty_dir_recursive (path , len );
611611}
612612
613+ static int is_refname_available (const char * ref , const char * oldref ,
614+ struct ref_list * list , int quiet )
615+ {
616+ int namlen = strlen (ref ); /* e.g. 'foo/bar' */
617+ while (list ) {
618+ /* list->name could be 'foo' or 'foo/bar/baz' */
619+ if (!oldref || strcmp (oldref , list -> name )) {
620+ int len = strlen (list -> name );
621+ int cmplen = (namlen < len ) ? namlen : len ;
622+ const char * lead = (namlen < len ) ? list -> name : ref ;
623+ if (!strncmp (ref , list -> name , cmplen ) &&
624+ lead [cmplen ] == '/' ) {
625+ if (!quiet )
626+ error ("'%s' exists; cannot create '%s'" ,
627+ list -> name , ref );
628+ return 0 ;
629+ }
630+ }
631+ list = list -> next ;
632+ }
633+ return 1 ;
634+ }
635+
613636static struct ref_lock * lock_ref_sha1_basic (const char * ref , const unsigned char * old_sha1 , int * flag )
614637{
615638 char * ref_file ;
@@ -643,29 +666,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
643666 orig_ref , strerror (errno ));
644667 goto error_return ;
645668 }
646- if (is_null_sha1 (lock -> old_sha1 )) {
647- /* The ref did not exist and we are creating it.
648- * Make sure there is no existing ref that is packed
649- * whose name begins with our refname, nor a ref whose
650- * name is a proper prefix of our refname.
651- */
652- int namlen = strlen (ref ); /* e.g. 'foo/bar' */
653- struct ref_list * list = get_packed_refs ();
654- while (list ) {
655- /* list->name could be 'foo' or 'foo/bar/baz' */
656- int len = strlen (list -> name );
657- int cmplen = (namlen < len ) ? namlen : len ;
658- const char * lead = (namlen < len ) ? list -> name : ref ;
659-
660- if (!strncmp (ref , list -> name , cmplen ) &&
661- lead [cmplen ] == '/' ) {
662- error ("'%s' exists; cannot create '%s'" ,
663- list -> name , ref );
664- goto error_return ;
665- }
666- list = list -> next ;
667- }
668- }
669+ /* When the ref did not exist and we are creating it,
670+ * make sure there is no existing ref that is packed
671+ * whose name begins with our refname, nor a ref whose
672+ * name is a proper prefix of our refname.
673+ */
674+ if (is_null_sha1 (lock -> old_sha1 ) &&
675+ !is_refname_available (ref , NULL , get_packed_refs (), 0 ))
676+ goto error_return ;
669677
670678 lock -> lk = xcalloc (1 , sizeof (struct lock_file ));
671679
@@ -776,6 +784,121 @@ int delete_ref(const char *refname, unsigned char *sha1)
776784 return ret ;
777785}
778786
787+ int rename_ref (const char * oldref , const char * newref )
788+ {
789+ static const char renamed_ref [] = "RENAMED-REF" ;
790+ unsigned char sha1 [20 ], orig_sha1 [20 ];
791+ int flag = 0 , logmoved = 0 ;
792+ struct ref_lock * lock ;
793+ char msg [PATH_MAX * 2 + 100 ];
794+ struct stat loginfo ;
795+ int log = !stat (git_path ("logs/%s" , oldref ), & loginfo );
796+
797+ if (S_ISLNK (loginfo .st_mode ))
798+ return error ("reflog for %s is a symlink" , oldref );
799+
800+ if (!resolve_ref (oldref , orig_sha1 , 1 , & flag ))
801+ return error ("refname %s not found" , oldref );
802+
803+ if (!is_refname_available (newref , oldref , get_packed_refs (), 0 ))
804+ return 1 ;
805+
806+ if (!is_refname_available (newref , oldref , get_loose_refs (), 0 ))
807+ return 1 ;
808+
809+ if (snprintf (msg , sizeof (msg ), "renamed %s to %s" , oldref , newref ) > sizeof (msg ))
810+ return error ("Refnames to long" );
811+
812+ lock = lock_ref_sha1_basic (renamed_ref , NULL , NULL );
813+ if (!lock )
814+ return error ("unable to lock %s" , renamed_ref );
815+ lock -> force_write = 1 ;
816+ if (write_ref_sha1 (lock , orig_sha1 , msg ))
817+ return error ("unable to save current sha1 in %s" , renamed_ref );
818+
819+ if (log && rename (git_path ("logs/%s" , oldref ), git_path ("tmp-renamed-log" )))
820+ return error ("unable to move logfile logs/%s to tmp-renamed-log: %s" ,
821+ oldref , strerror (errno ));
822+
823+ if (delete_ref (oldref , orig_sha1 )) {
824+ error ("unable to delete old %s" , oldref );
825+ goto rollback ;
826+ }
827+
828+ if (resolve_ref (newref , sha1 , 1 , & flag ) && delete_ref (newref , sha1 )) {
829+ if (errno == EISDIR ) {
830+ if (remove_empty_directories (git_path ("%s" , newref ))) {
831+ error ("Directory not empty: %s" , newref );
832+ goto rollback ;
833+ }
834+ } else {
835+ error ("unable to delete existing %s" , newref );
836+ goto rollback ;
837+ }
838+ }
839+
840+ if (log && safe_create_leading_directories (git_path ("logs/%s" , newref ))) {
841+ error ("unable to create directory for %s" , newref );
842+ goto rollback ;
843+ }
844+
845+ retry :
846+ if (log && rename (git_path ("tmp-renamed-log" ), git_path ("logs/%s" , newref ))) {
847+ if (errno == EISDIR ) {
848+ if (remove_empty_directories (git_path ("logs/%s" , newref ))) {
849+ error ("Directory not empty: logs/%s" , newref );
850+ goto rollback ;
851+ }
852+ goto retry ;
853+ } else {
854+ error ("unable to move logfile tmp-renamed-log to logs/%s: %s" ,
855+ newref , strerror (errno ));
856+ goto rollback ;
857+ }
858+ }
859+ logmoved = log ;
860+
861+ lock = lock_ref_sha1_basic (newref , NULL , NULL );
862+ if (!lock ) {
863+ error ("unable to lock %s for update" , newref );
864+ goto rollback ;
865+ }
866+
867+ lock -> force_write = 1 ;
868+ hashcpy (lock -> old_sha1 , orig_sha1 );
869+ if (write_ref_sha1 (lock , orig_sha1 , msg )) {
870+ error ("unable to write current sha1 into %s" , newref );
871+ goto rollback ;
872+ }
873+
874+ return 0 ;
875+
876+ rollback :
877+ lock = lock_ref_sha1_basic (oldref , NULL , NULL );
878+ if (!lock ) {
879+ error ("unable to lock %s for rollback" , oldref );
880+ goto rollbacklog ;
881+ }
882+
883+ lock -> force_write = 1 ;
884+ flag = log_all_ref_updates ;
885+ log_all_ref_updates = 0 ;
886+ if (write_ref_sha1 (lock , orig_sha1 , NULL ))
887+ error ("unable to write current sha1 into %s" , oldref );
888+ log_all_ref_updates = flag ;
889+
890+ rollbacklog :
891+ if (logmoved && rename (git_path ("logs/%s" , newref ), git_path ("logs/%s" , oldref )))
892+ error ("unable to restore logfile %s from %s: %s" ,
893+ oldref , newref , strerror (errno ));
894+ if (!logmoved && log &&
895+ rename (git_path ("tmp-renamed-log" ), git_path ("logs/%s" , oldref )))
896+ error ("unable to restore logfile %s from tmp-renamed-log: %s" ,
897+ oldref , strerror (errno ));
898+
899+ return 1 ;
900+ }
901+
779902void unlock_ref (struct ref_lock * lock )
780903{
781904 if (lock -> lock_fd >= 0 ) {
0 commit comments