X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=topbloke-formulae.git;a=blobdiff_plain;f=article.tex;h=d85a022ff8fb8388f380325f4ffd4ed9e4330d5d;hp=3f8bbff36113fc34d2434a6eb3e54505a51db462;hb=400772c417bd54383994df5a227df88adc769171;hpb=e825f9ef4709f95709d1c1ead218d64675bc2fea diff --git a/article.tex b/article.tex index 3f8bbff..d85a022 100644 --- a/article.tex +++ b/article.tex @@ -38,13 +38,17 @@ \renewcommand{\land}{\wedge} \renewcommand{\lor}{\vee} -\newcommand{\pancs}[2]{{\mathcal A} ( #1 , #2 ) } -\newcommand{\pends}[2]{{\mathcal E} ( #1 , #2 ) } +\newcommand{\pancs}{{\mathcal A}} +\newcommand{\pends}{{\mathcal E}} + +\newcommand{\pancsof}[2]{\pancs ( #1 , #2 ) } +\newcommand{\pendsof}[2]{\pends ( #1 , #2 ) } \newcommand{\patchof}[1]{{\mathcal P} ( #1 ) } \newcommand{\baseof}[1]{{\mathcal B} ( #1 ) } -\newcommand{\eqn}[2]{ #2 \tag*{\mbox{#1}} } +\newcommand{\eqn}[2]{ #2 \tag*{\mbox{\bf #1}} } +\newcommand{\corrolary}[1]{ #1 \tag*{\mbox{\it Corrolary.}} } %\newcommand{\bigforall}{\mathop{\hbox{\huge$\forall$}}} \newcommand{\bigforall}{% @@ -55,6 +59,16 @@ {\hbox{\scriptsize$\forall$}}}% } + +\newcommand{\qed}{\square} +\newcommand{\proof}[1]{{\it Proof.} #1 $\qed$} + +\newcommand{\gathbegin}{\begin{gather} \tag*{}} +\newcommand{\gathnext}{\\ \tag*{}} + +\newcommand{\true}{t} +\newcommand{\false}{f} + \begin{document} \section{Notation} @@ -75,11 +89,11 @@ $ D \in \set X $ where $ C \hasparents \set X $. \item[ $ C \has D $ ] Informally, the tree at commit $C$ contains the change made in commit $D$. Does not take account of deliberate reversions by -the user or in non-Topbloke-controlled branches; these are considered -normal, forward, commits. For merges and Topbloke-generated -anticommits, the ``change made'' is only to be thought of as any -conflict resolution. This is not a partial order because it is not -transitive. +the user or reversion, rebasing or rewinding in +non-Topbloke-controlled branches. For merges and Topbloke-generated +anticommits or re-commits, the ``change made'' is only to be thought +of as any conflict resolution. This is not a partial order because it +is not transitive. \item[ $ \p, \py, \pn $ ] A patch $\p$ consists of two sets of commits $\pn$ and $\py$, which @@ -90,23 +104,23 @@ All these sets are distinct. Hence: \item[ $ \patchof{ C } $ ] Either $\p$ s.t. $ C \in \p $, or $\bot$. -A function from commits to sets $\p$. +A function from commits to patches' sets $\p$. -\item[ $ \pancs{C}{\set P} $ ] +\item[ $ \pancsof{C}{\set P} $ ] $ \{ A \; | \; A \le C \land A \in \set P \} $ i.e. all the ancestors of $C$ which are in $\set P$. -\item[ $ \pends{C}{\set P} $ ] -$ \{ E \; | \; E \in \pancs{C}{\set P} - \land \mathop{\not\exists}_{A \in \pancs{C}{\set P}} +\item[ $ \pendsof{C}{\set P} $ ] +$ \{ E \; | \; E \in \pancsof{C}{\set P} + \land \mathop{\not\exists}_{A \in \pancsof{C}{\set P}} A \neq E \land E \le A \} $ -i.e. all $\le$-maximal commits in $\pancs{C}{\set P}$. +i.e. all $\le$-maximal commits in $\pancsof{C}{\set P}$. \item[ $ \baseof{C} $ ] -$ \pends{C}{\pn} = \{ \baseof{C} \} $ where $ C \in \py $. +$ \pendsof{C}{\pn} = \{ \baseof{C} \} $ where $ C \in \py $. A partial function from commits to commits. -See ``unique base'', below. +See Unique Base, below. \item[ $ C \haspatch \p $ ] $\displaystyle \bigforall_{D \in \py} D \isin C \equiv D \le C $. @@ -116,39 +130,219 @@ $\displaystyle \bigforall_{D \in \py} D \isin C \equiv D \le C $. $\displaystyle \bigforall_{D \in \py} D \not\isin C $. ~ Informally, $C$ has none of the contents of $\p$. -Non-Topbloke commits are $\nothaspatch \p$ for all $\p$; if -a patch is merged into a non-Topbloke branch and we inherit -it, we hope that git's merge algorithm will DTRT. +Non-Topbloke commits are $\nothaspatch \p$ for all $\p$; if a Topbloke +patch is applied to a non-Topbloke branch and then bubbles back to +the Topbloke patch itself, we hope that git's merge algorithm will +DTRT or that the user will no longer care about the Topbloke patch. \end{basedescript} - +\newpage \section{Invariants} -\[ \eqn{No replay:}{ +We maintain these each time we construct a new commit. \\ +\[ \eqn{No Replay:}{ C \has D \implies C \ge D }\] -\[\eqn{Unique base:}{ - \bigforall_{C \in \py} \pends{C}{\pn} = \{ B \} +\[\eqn{Unique Base:}{ + \bigforall_{C \in \py} \pendsof{C}{\pn} = \{ B \} }\] -\[\eqn{Tip contents:}{ +\[\eqn{Tip Contents:}{ \bigforall_{C \in \py} D \isin C \equiv { D \isin \baseof{C} \lor \atop (D \in \py \land D \le C) } }\] -\[\eqn{Base acyclic:}{ +\[\eqn{Base Acyclic:}{ \bigforall_{B \in \pn} D \isin B \implies D \notin \py }\] +\[\eqn{Coherence:}{ + \bigforall_{C,\p} C \haspatch \p \lor C \nothaspatch \p +}\] +\[\eqn{Foreign Inclusion:}{ + \bigforall_{D \text{ s.t. } \patchof{D} = \bot} D \isin C \equiv D \leq C +}\] + +\section{Some lemmas} + +\[ \eqn{Exclusive Tip Contents:}{ + \bigforall_{C \in \py} + \neg \Bigl[ D \isin \baseof{C} \land ( D \in \py \land D \le C ) + \Bigr] +}\] +Ie, the two limbs of the RHS of Tip Contents are mutually exclusive. + +\proof{ +Let $B = \baseof{C}$ in $D \isin \baseof{C}$. Now $B \in \pn$. +So by Base Acyclic $D \isin B \implies D \notin \py$. +} +\[ \corrolary{ + \bigforall_{C \in \py} D \isin C \equiv + \begin{cases} + D \in \py : & D \le C \\ + D \not\in \py : & D \isin \baseof{C} + \end{cases} +}\] + +\[ \eqn{Tip Self Inpatch:}{ + \bigforall_{C \in \py} C \haspatch \p +}\] +Ie, tip commits contain their own patch. + +\proof{ +Apply Exclusive Tip Contents to some $D \in \py$: +$ \bigforall_{C \in \py}\bigforall_{D \in \py} + D \isin C \equiv D \le C $ +} + +\[ \eqn{Exact Ancestors:}{ + \bigforall_{ C \hasparents \set{R} } + D \le C \equiv + ( \mathop{\hbox{\huge{$\vee$}}}_{R \in \set R} D \le R ) + \lor D = C +}\] + +\[ \eqn{Transitive Ancestors:}{ + \left[ \bigforall_{ E \in \pendsof{C}{\set P} } E \le M \right] \equiv + \left[ \bigforall_{ A \in \pancsof{C}{\set P} } A \le M \right] +}\] + +\proof{ +The implication from right to left is trivial because +$ \pends() \subset \pancs() $. +For the implication from left to right: +by the definition of $\mathcal E$, +for every such $A$, either $A \in \pends()$ which implies +$A \le M$ by the LHS directly, +or $\exists_{A' \in \pancs()} \; A' \neq A \land A \le A' $ +in which case we repeat for $A'$. Since there are finitely many +commits, this terminates with $A'' \in \pends()$, ie $A'' \le M$ +by the LHS. And $A \le A''$. +} + +\section{Commit annotation} -\section{Test more symbols} +We annotate each Topbloke commit $C$ with: +\gathbegin + \patchof{C} +\gathnext + \baseof{C}, \text{ if } C \in \py +\gathnext + \bigforall_{\pa{Q}} + \text{ either } C \haspatch \pa{Q} \text{ or } C \nothaspatch \pa{Q} +\gathnext + \bigforall_{\pay{Q} \not\ni C} \pendsof{C}{\pay{Q}} +\end{gather} -$ C \haspatch \p $ +We do not annotate $\pendsof{C}{\py}$ for $C \in \py$. Doing so would +make it wrong to make plain commits with git because the recorded $\pends$ +would have to be updated. The annotation is not needed because +$\forall_{\py \ni C} \; \pendsof{C}{\py} = \{C\}$. -$ C \nothaspatch \p $ +\section{Simple commit} -$ \p \patchisin C $ +A simple single-parent forward commit $C$ as made by git-commit. +\begin{gather} +\tag*{} C \hasparents \{ A \} \\ +\tag*{} \patchof{C} = \patchof{A} \\ +\tag*{} D \isin C \equiv D \isin A \lor D = C +\end{gather} -$ \p \notpatchisin C $ +\subsection{No Replay} +Trivial. -$ \{ B \} \areparents C $ +\subsection{Unique Base} +If $A, C \in \py$ then $\baseof{C} = \baseof{A}$. $\qed$ + +\subsection{Tip Contents} +We need to consider only $A, C \in \py$. From Tip Contents for $A$: +\[ D \isin A \equiv D \isin \baseof{A} \lor ( D \in \py \land D \le A ) \] +Substitute into the contents of $C$: +\[ D \isin C \equiv D \isin \baseof{A} \lor ( D \in \py \land D \le A ) + \lor D = C \] +Since $D = C \implies D \in \py$, +and substituting in $\baseof{C}$, this gives: +\[ D \isin C \equiv D \isin \baseof{C} \lor + (D \in \py \land D \le A) \lor + (D = C \land D \in \py) \] +\[ \equiv D \isin \baseof{C} \lor + [ D \in \py \land ( D \le A \lor D = C ) ] \] +So by Exact Ancestors: +\[ D \isin C \equiv D \isin \baseof{C} \lor ( D \in \py \land D \le C +) \] +$\qed$ + +\subsection{Base Acyclic} + +Need to consider only $A, C \in \pn$. + +For $D = C$: $D \in \pn$ so $D \not\in \py$. OK. + +For $D \neq C$: $D \isin C \equiv D \isin A$, so by Base Acyclic for +$A$, $D \isin C \implies D \not\in \py$. $\qed$ + +\subsection{Coherence and patch inclusion} + +Need to consider $D \in \py$ + +\subsubsection{For $A \haspatch P, D = C$:} + +Ancestors of $C$: +$ D \le C $. + +Contents of $C$: +$ D \isin C \equiv \ldots \lor \true \text{ so } D \haspatch C $. + +\subsubsection{For $A \haspatch P, D \neq C$:} +Ancestors: $ D \le C \equiv D \le A $. + +Contents: $ D \isin C \equiv D \isin A \lor f $ +so $ D \isin C \equiv D \isin A $. + +So: +\[ A \haspatch P \implies C \haspatch P \] + +\subsubsection{For $A \nothaspatch P$:} + +Firstly, $C \not\in \py$ since if it were, $A \in \py$. +Thus $D \neq C$. + +Now by contents of $A$, $D \notin A$, so $D \notin C$. + +So: +\[ A \nothaspatch P \implies C \nothaspatch P \] +$\qed$ + +\subsection{Foreign inclusion:} + +If $D = C$, trivial. For $D \neq C$: +$D \isin C \equiv D \isin A \equiv D \le A \equiv D \le C$. $\qed$ + +\section{Merge} + +Merge commits $L$ and $R$ using merge base $M$ ($M < L, M < R$): +\gathbegin + C \hasparents \{ L, R \} +\gathnext + \patchof{C} = \patchof{L} +\gathnext + D \isin C \equiv + \begin{cases} + (D \isin L \land D \isin R) \lor D = C : & \true \\ + (D \not\isin L \land D \not\isin R) \land D \neq C : & \false \\ + \text{otherwise} : & D \not\isin M + \end{cases} +\end{gather} + +\subsection{Conditions} + +\[ \eqn{ Merges Exhaustive }{ + L \in \py => \Bigl[ R \in \py \lor R \in \pn \Bigr] +}\] +\[ \eqn{ Tip Merge }{ + L \in \py \land R \in \py \implies \Bigl[ \text{TBD} \Bigr] +}\] +\[ \eqn{ Base Merge }{ + L \in \py \land R \in \pn \implies \Bigl[ R \ge \baseof{L} \land M = + \baseof{L} \Bigr] +}\] \end{document}