@@ -610,6 +610,29 @@ static int remove_empty_directories(char *file)
610
610
return remove_empty_dir_recursive (path , len );
611
611
}
612
612
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
+
613
636
static struct ref_lock * lock_ref_sha1_basic (const char * ref , const unsigned char * old_sha1 , int * flag )
614
637
{
615
638
char * ref_file ;
@@ -643,29 +666,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
643
666
orig_ref , strerror (errno ));
644
667
goto error_return ;
645
668
}
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 ;
669
677
670
678
lock -> lk = xcalloc (1 , sizeof (struct lock_file ));
671
679
@@ -776,6 +784,121 @@ int delete_ref(const char *refname, unsigned char *sha1)
776
784
return ret ;
777
785
}
778
786
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
+
779
902
void unlock_ref (struct ref_lock * lock )
780
903
{
781
904
if (lock -> lock_fd >= 0 ) {
0 commit comments