@@ -1428,56 +1428,72 @@ static int refdb_fs_backend__rename(
14281428 const char * message )
14291429{
14301430 refdb_fs_backend * backend = (refdb_fs_backend * )_backend ;
1431- git_reference * old , * new ;
1432- git_filebuf file = GIT_FILEBUF_INIT ;
1431+ git_reference * old , * new = NULL ;
1432+ git_filebuf old_lock = GIT_FILEBUF_INIT ;
1433+ git_filebuf new_lock = GIT_FILEBUF_INIT ;
14331434 int error ;
1435+ git__refdb_flags flags ;
14341436
14351437 assert (backend );
14361438
1437- if ((error = reference_path_available (
1438- backend , new_name , old_name , force )) < 0 ||
1439+ if ((error = reference_path_available (backend , new_name , old_name , force )) < 0 ||
14391440 (error = refdb_fs_backend__lookup (& old , _backend , old_name )) < 0 )
14401441 return error ;
14411442
1442- if ((error = refdb_fs_backend__delete (_backend , old_name , who , message , NULL , NULL )) < 0 ) {
1443- git_reference_free (old );
1444- return error ;
1445- }
1443+ /* We delete the old ref first so we don't cause a failure later if the new
1444+ * name is the suffix or prefix of the old. Also, we skip the reflog, because
1445+ * we want to rename it */
1446+ if ((error = refdb_fs_backend__delete_tail (& old_lock , _backend , old , NULL , NULL , GIT_REFDB__SKIP_REFLOG , who , message )) < 0 )
1447+ goto cleanup ;
1448+ git_filebuf_cleanup (& old_lock );
14461449
1447- new = git_reference__set_name (old , new_name );
1448- if (!new ) {
1449- git_reference_free (old );
1450- return -1 ;
1450+ /* We need to create a reference object to write */
1451+ if (old -> type == GIT_REF_OID ) {
1452+ new = git_reference__alloc (new_name , & old -> target .oid , NULL );
1453+ } else if (old -> type == GIT_REF_SYMBOLIC ) {
1454+ new = git_reference__alloc_symbolic (new_name , old -> target .symbolic );
1455+ } else {
1456+ assert (false);
14511457 }
14521458
1453- if ((error = loose_lock (& file , backend , new -> name )) < 0 ) {
1454- git_reference_free (new );
1455- return error ;
1456- }
1459+ if (new == NULL ) goto cleanup ;
14571460
1458- /* Try to rename the refog; it's ok if the old doesn't exist */
1461+ /* Save the new ref on disk; we skip the reflog because the old ref might be
1462+ * sharing a suffix/prefix with us.
1463+ */
1464+ flags = GIT_REFDB__SKIP_REFLOG ;
1465+ if (force ) flags |= GIT_REFDB__FORCE_WRITE ;
1466+ if ((error = refdb_fs_backend__write_tail (& new_lock , _backend , new , NULL , NULL , flags , who , message )) < 0 )
1467+ goto cleanup ;
1468+
1469+ /* Try to rename the reflog; it's ok if the old doesn't exist */
14591470 error = refdb_reflog_fs__rename (_backend , old_name , new_name );
1460- if (((error == 0 ) || (error == GIT_ENOTFOUND )) &&
1461- ((error = reflog_append (backend , new , git_reference_target (new ), NULL , who , message )) < 0 )) {
1462- git_reference_free (new );
1463- git_filebuf_cleanup (& file );
1464- return error ;
1465- }
1471+ if (error < 0 && error != GIT_ENOTFOUND )
1472+ goto cleanup ;
14661473
1467- if (error < 0 ) {
1468- git_reference_free (new );
1469- git_filebuf_cleanup (& file );
1470- return error ;
1471- }
1474+ if ((error = reflog_append (backend , new , git_reference_target (new ), NULL , who , message )) < 0 )
1475+ goto cleanup ;
14721476
1477+ if ((error = loose_commit (& new_lock , new )) < 0 )
1478+ goto cleanup ;
14731479
1474- if ((error = loose_commit (& file , new )) < 0 || out == NULL ) {
1480+
1481+ goto success ;
1482+
1483+ cleanup :
1484+ git_filebuf_cleanup (& new_lock );
1485+ git_reference_free (new );
1486+ git_reference_free (old );
1487+ new = NULL ;
1488+
1489+ success :
1490+
1491+ if (out != NULL )
1492+ * out = new ;
1493+ else
14751494 git_reference_free (new );
1476- return error ;
1477- }
14781495
1479- * out = new ;
1480- return 0 ;
1496+ return error ;
14811497}
14821498
14831499static int refdb_fs_backend__compress (git_refdb_backend * _backend )
0 commit comments