|
| 1 | +********************************************************************** |
| 2 | +Reference Transactions |
| 3 | +********************************************************************** |
| 4 | + |
| 5 | +Reference transactions allow you to update multiple references atomically. |
| 6 | +All reference updates within a transaction either succeed together or fail |
| 7 | +together, ensuring repository consistency. |
| 8 | + |
| 9 | +Basic Usage |
| 10 | +=========== |
| 11 | + |
| 12 | +Use the :meth:`Repository.transaction` method as a context manager. The |
| 13 | +transaction commits automatically when the context exits successfully, or |
| 14 | +rolls back if an exception is raised:: |
| 15 | + |
| 16 | + with repo.transaction() as txn: |
| 17 | + txn.lock_ref('refs/heads/master') |
| 18 | + txn.set_target('refs/heads/master', new_oid, message='Update master') |
| 19 | + |
| 20 | +Atomic Multi-Reference Updates |
| 21 | +=============================== |
| 22 | + |
| 23 | +Transactions are useful when you need to update multiple references |
| 24 | +atomically:: |
| 25 | + |
| 26 | + # Swap two branches atomically |
| 27 | + with repo.transaction() as txn: |
| 28 | + txn.lock_ref('refs/heads/branch-a') |
| 29 | + txn.lock_ref('refs/heads/branch-b') |
| 30 | + |
| 31 | + # Get current targets |
| 32 | + ref_a = repo.lookup_reference('refs/heads/branch-a') |
| 33 | + ref_b = repo.lookup_reference('refs/heads/branch-b') |
| 34 | + |
| 35 | + # Swap them |
| 36 | + txn.set_target('refs/heads/branch-a', ref_b.target, message='Swap') |
| 37 | + txn.set_target('refs/heads/branch-b', ref_a.target, message='Swap') |
| 38 | + |
| 39 | +Automatic Rollback |
| 40 | +================== |
| 41 | + |
| 42 | +If an exception occurs during the transaction, changes are automatically |
| 43 | +rolled back:: |
| 44 | + |
| 45 | + try: |
| 46 | + with repo.transaction() as txn: |
| 47 | + txn.lock_ref('refs/heads/master') |
| 48 | + txn.set_target('refs/heads/master', new_oid) |
| 49 | + |
| 50 | + # If this raises an exception, the ref update is rolled back |
| 51 | + validate_commit(new_oid) |
| 52 | + except ValidationError: |
| 53 | + # Master still points to its original target |
| 54 | + pass |
| 55 | + |
| 56 | +Manual Commit |
| 57 | +============= |
| 58 | + |
| 59 | +While the context manager is recommended, you can manually manage |
| 60 | +transactions:: |
| 61 | + |
| 62 | + from pygit2 import ReferenceTransaction |
| 63 | + |
| 64 | + txn = ReferenceTransaction(repo) |
| 65 | + try: |
| 66 | + txn.lock_ref('refs/heads/master') |
| 67 | + txn.set_target('refs/heads/master', new_oid, message='Update') |
| 68 | + txn.commit() |
| 69 | + finally: |
| 70 | + del txn # Ensure transaction is freed |
| 71 | + |
| 72 | +API Reference |
| 73 | +============= |
| 74 | + |
| 75 | +Repository Methods |
| 76 | +------------------ |
| 77 | + |
| 78 | +.. automethod:: pygit2.Repository.transaction |
| 79 | + |
| 80 | +The ReferenceTransaction Type |
| 81 | +------------------------------ |
| 82 | + |
| 83 | +.. autoclass:: pygit2.ReferenceTransaction |
| 84 | + :members: |
| 85 | + :special-members: __enter__, __exit__ |
| 86 | + |
| 87 | +Usage Notes |
| 88 | +=========== |
| 89 | + |
| 90 | +- Always lock a reference with :meth:`~ReferenceTransaction.lock_ref` before |
| 91 | + modifying it |
| 92 | +- Transactions operate on reference names, not Reference objects |
| 93 | +- Symbolic references can be updated with |
| 94 | + :meth:`~ReferenceTransaction.set_symbolic_target` |
| 95 | +- References can be deleted with :meth:`~ReferenceTransaction.remove` |
| 96 | +- The signature parameter defaults to the repository's configured identity |
| 97 | + |
| 98 | +Thread Safety |
| 99 | +============= |
| 100 | + |
| 101 | +Transactions are thread-local and must be used from the thread that created |
| 102 | +them. Attempting to use a transaction from a different thread raises |
| 103 | +:exc:`RuntimeError`:: |
| 104 | + |
| 105 | + # This is safe - each thread has its own transaction |
| 106 | + def thread1(): |
| 107 | + with repo.transaction() as txn: |
| 108 | + txn.lock_ref('refs/heads/branch1') |
| 109 | + txn.set_target('refs/heads/branch1', oid1) |
| 110 | + |
| 111 | + def thread2(): |
| 112 | + with repo.transaction() as txn: |
| 113 | + txn.lock_ref('refs/heads/branch2') |
| 114 | + txn.set_target('refs/heads/branch2', oid2) |
| 115 | + |
| 116 | + # Both threads can run concurrently without conflicts |
| 117 | + |
| 118 | +Different threads can hold transactions simultaneously as long as they don't |
| 119 | +attempt to lock the same references. If two threads try to acquire locks in |
| 120 | +different orders, libgit2 will detect potential deadlocks and raise an error. |
0 commit comments