| 801 |
801 |
|
},
|
| 802 |
802 |
|
}
|
| 803 |
803 |
|
|
|
804 |
+ |
/// What a write does when it loses the ref race and finds the note it was
|
|
805 |
+ |
/// editing already changed by somebody else.
|
|
806 |
+ |
///
|
|
807 |
+ |
/// Only reachable on a retry: winning the race means there was nothing to
|
|
808 |
+ |
/// reconcile.
|
|
809 |
+ |
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
810 |
+ |
pub enum LostRace {
|
|
811 |
+ |
/// Keep both sides (see [`merge_edit`]). Two people who annotated the same
|
|
812 |
+ |
/// commit each meant something, and dropping either is losing a note.
|
|
813 |
+ |
Merge,
|
|
814 |
+ |
/// Take ours and discard theirs. The right answer for a note that states a
|
|
815 |
+ |
/// current fact rather than an opinion: a second build result for one
|
|
816 |
+ |
/// commit supersedes the first, and merging them would produce a note that
|
|
817 |
+ |
/// claims the build both passed and failed.
|
|
818 |
+ |
Overwrite,
|
|
819 |
+ |
}
|
|
820 |
+ |
|
| 804 |
821 |
|
/// Write, edit or remove one note, publishing it on `refs/notes/<namespace>`.
|
| 805 |
822 |
|
///
|
|
823 |
+ |
/// Reconciles a lost race by merging, which is what a note somebody typed
|
|
824 |
+ |
/// wants. [`write_note_with`] is the same thing with the choice exposed.
|
|
825 |
+ |
pub fn write_note<E: NoteObjects + NoteWrites>(
|
|
826 |
+ |
engine: &E,
|
|
827 |
+ |
namespace: &str,
|
|
828 |
+ |
target: Oid,
|
|
829 |
+ |
content: Option<&[u8]>,
|
|
830 |
+ |
who: &Signature,
|
|
831 |
+ |
) -> Result<Written, NotesError> {
|
|
832 |
+ |
write_note_with(engine, namespace, target, content, who, LostRace::Merge)
|
|
833 |
+ |
}
|
|
834 |
+ |
|
|
835 |
+ |
/// Write, edit or remove one note, choosing what a lost race does.
|
|
836 |
+ |
///
|
| 806 |
837 |
|
/// `content` of `None` removes the note. `who` is both author and committer:
|
| 807 |
838 |
|
/// the person doing it is the person who did it, and a notes commit has no
|
| 808 |
839 |
|
/// distinct patch author to credit.
|
| 810 |
841 |
|
/// The ref moves by compare-and-swap against the tip this write was built on,
|
| 811 |
842 |
|
/// so a concurrent writer is never overwritten — the loser reloads and tries
|
| 812 |
843 |
|
/// again against what the winner left. Two people annotating different commits
|
| 813 |
|
- |
/// both land. Two people annotating the *same* commit get their notes merged
|
| 814 |
|
- |
/// (see [`merge_edit`]) rather than one silently replacing the other.
|
| 815 |
|
- |
pub fn write_note<E: NoteObjects + NoteWrites>(
|
|
844 |
+ |
/// both land whichever `on_lost_race` says; it decides only what happens when
|
|
845 |
+ |
/// two writes touch the *same* note.
|
|
846 |
+ |
pub fn write_note_with<E: NoteObjects + NoteWrites>(
|
| 816 |
847 |
|
engine: &E,
|
| 817 |
848 |
|
namespace: &str,
|
| 818 |
849 |
|
target: Oid,
|
| 819 |
850 |
|
content: Option<&[u8]>,
|
| 820 |
851 |
|
who: &Signature,
|
|
852 |
+ |
on_lost_race: LostRace,
|
| 821 |
853 |
|
) -> Result<Written, NotesError> {
|
| 822 |
854 |
|
let full_ref = format!("{NOTES_REF_PREFIX}{namespace}");
|
| 823 |
855 |
|
// What the note said when this write was composed. A retry compares
|
| 842 |
874 |
|
}
|
| 843 |
875 |
|
|
| 844 |
876 |
|
// The note moved under us and we are setting content, so what the
|
| 845 |
|
- |
// writer typed is no longer the whole story.
|
| 846 |
|
- |
let merged = attempt > 0 && current != base && content.is_some();
|
|
877 |
+ |
// writer typed is no longer the whole story. Unless the caller said
|
|
878 |
+ |
// theirs does not survive contact with ours, in which case there is
|
|
879 |
+ |
// nothing to reconcile and the retry simply rebuilds on the new tip.
|
|
880 |
+ |
let merged =
|
|
881 |
+ |
on_lost_race == LostRace::Merge && attempt > 0 && current != base && content.is_some();
|
| 847 |
882 |
|
let mut buffer;
|
| 848 |
883 |
|
let content = match (merged, content, current) {
|
| 849 |
884 |
|
(true, Some(ours), Some(theirs)) => {
|