Repository: pydantic/pydantic

Issue:
Unnecessary rebuilds occur when `model_rebuild` is performed.
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

`pip install pydantic==2.3.0`
```python
from pydantic import BaseModel
from typing import Optional

class Foo(BaseModel):
    value: Optional["LazyValue"]

class Bar(BaseModel):
    a: Foo

Bar.model_rebuild()
```
```
NameError: name 'LazyValue' is not defined
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
    ...
pydantic.errors.PydanticUndefinedAnnotation: name 'LazyValue' is not defined
For further information visit https://errors.pydantic.dev/2.3/u/undefined-annotation
```

If you try to rebuild the bar, `Foo` will be rebuilt as well.
`LazyValue` is not defined, so an error occurs.

----

`pip install pydantic==1.10.12`
```python
from pydantic import BaseModel
from typing import Optional

class Foo(BaseModel):
    value: Optional["LazyValue"]

class Bar(BaseModel):
    a: Foo

Bar.update_forward_refs()
```
In this case, only `Bar` will be rebuilt and no error will occur.

----

Which is normal operation? Also, is this change documented?
The above bug occurred when building complex objects with circular references.

### Example Code

_No response_

### Python, Pydantic & OS Version

```Text
pydantic version: 2.0
        pydantic-core version: 2.0.1 release build profile
                 install path: C:\Project\Python\pydantic_self_referencing_models\.venv\Lib\site-packages\pydantic
               python version: 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
                     platform: Windows-10-10.0.19045-SP0
     optional deps. installed: ['typing-extensions']
```



Discussion:
Test A
```python
from pydantic import BaseModel
import time

class A(BaseModel):
    value: "B"
class B(BaseModel):
    value: "C"
class C(BaseModel):
    value: "D"
...
class X(BaseModel):
    value: "Y"
class Y(BaseModel):
    value: "Z"
class Z(BaseModel):
    value: "A"

start = time.time()
for i in range(10000):
    A.update_forward_refs()
print(time.time() - start)
```

Test B
```python
from pydantic import BaseModel
import time

class A(BaseModel):
    value: "B"
class B(BaseModel):
    value: "A"

start = time.time()
for i in range(10000):
    A.update_forward_refs()
print(time.time() - start)
```

| version | Test A | Test B |
| --- | --- | --- |
| `pydantic==1.10.12` | 0.010499238967895508 | 0.010999679565429688 |
| `pydantic==2.4.0` | 32.9549994468689 | 3.026500701904297 |

---

using A.model_rebuild()

| version | Test A | Test B |
| --- | --- | --- |
| `pydantic==2.4.0` | 0.0065000057220458984 | 0.0030012130737304688 |

Probably the second and subsequent rebuilds will be skipped.

---

Results differ significantly between Test A and Test B.
Perhaps a recursive rebuild is occurring in v2.

If you are having similar problems during the migration process from pydantic v1 to pydantic v2, replacing `update_forward_refs()` with `model_rebuild(raise_errors=False)` may help.
I know there is some resistance to using arguments that do not generate errors, but I think that is handled optimally in pydantic v2.
Hi @fa0311,

Thanks for reporting this and for the thorough testing you've done. I definitely think that more documentation could be of help here, specifically re changes between V1 and V2. I'd be happy to add some info to the migration guide / docs next week 😄.

Resolving patch:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -82,7 +82,7 @@ class BaseModel(metaclass=_model_construction.ModelMetaclass):
 
         __pydantic_extra__: An instance attribute with the values of extra fields from validation when
             `model_config['extra'] == 'allow'`.
-        __pydantic_fields_set__: An instance attribute with the names of fields explicitly specified during validation.
+        __pydantic_fields_set__: An instance attribute with the names of fields explicitly set.
         __pydantic_private__: Instance attribute with the values of private attributes set on the model instance.
     """
 
@@ -186,7 +186,7 @@ def model_extra(self) -> dict[str, Any] | None:
 
     @property
     def model_fields_set(self) -> set[str]:
-        """Returns the set of fields that have been set on this model instance.
+        """Returns the set of fields that have been explicitly set on this model instance.
 
         Returns:
             A set of strings representing the fields that have been set,
@@ -296,7 +296,7 @@ def model_dump(
             include: A list of fields to include in the output.
             exclude: A list of fields to exclude from the output.
             by_alias: Whether to use the field's alias in the dictionary key if defined.
-            exclude_unset: Whether to exclude fields that are unset or None from the output.
+            exclude_unset: Whether to exclude fields that have not been explicitly set.
             exclude_defaults: Whether to exclude fields that are set to their default value from the output.
             exclude_none: Whether to exclude fields that have a value of `None` from the output.
             round_trip: Whether to enable serialization and deserialization round-trip support.


Test patch: