# HG changeset patch # User Martin Geisler # Date 1307543684 -7200 # Node ID 78a5a27609a550e583037ee6133fa246dd5173f9 # Parent 027862d2fec927c4974d214b6791e0a6b1f54cbb Introduction, conclussion, more consistent diff --git a/revset-talk.tex b/revset-talk.tex --- a/revset-talk.tex +++ b/revset-talk.tex @@ -22,16 +22,68 @@ \section{Introduction} +\begin{frame}[fragile]{Confusing Histories} + Big projects can give rise to a branchy history: + \begin{itemize} + \item several concurrent branches + \item many developers pushing changes + \end{itemize} + + \pause + + Mercurial help you to cut away the unnecessary fluff: + \begin{itemize} + \item<2-> Revision sets selects revisions (Mercurial 1.6): +\begin{lstlisting} +$ hg log -r "branch('stable') and user('Martin')" +\end{lstlisting} + Can be used in all places where Mercurial expects revisions + + \item<3-> File sets selects files in revisions (Mercurial 1.9 or + 2.0): +\begin{lstlisting} +$ hg revert "set:added() and size('>20MB')" +\end{lstlisting} + Can be used in all places where Mercurial expects file names + + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{Flexibility} + The query languages lets you solve hard problems: + \begin{itemize}[<+->] + \item Imagine you have a dirty working copy: +\begin{lstlisting} +$ hg status +M index.html +A logo.png +\end{lstlisting} + But how can you see the diff of \path{index.html} only? + +\item Easy! You use your nifty Unix shell: +\begin{lstlisting} +$ hg diff $(hg status --no-status --modified) +\end{lstlisting} + +\item With file sets you can do +\begin{lstlisting} +$ hg diff "set:modified()" +\end{lstlisting} +and it will work on all platforms + + \end{itemize} +\end{frame} + \begin{frame}[fragile]{Quoting} How to handle special characters: \begin{itemize}[<+->] -\item You will need to quote your revsets on the command line: +\item You will need to quote your queries on the command line: \begin{lstlisting} $ hg log -r parents() zsh: parse error near `()' \end{lstlisting} -\item Strings in revsets can be in single- or double-quotes: +\item Strings in queries can be in single- or double-quotes: \begin{lstlisting} $ hg log -r "user('Martin')" \end{lstlisting} @@ -56,57 +108,143 @@ \begin{frame}[fragile]{Predicates} Predicates select changesets for inclusion in the resulting set: \begin{itemize} - \item Changeset metadata: \cmd{closed()}, \cmd{head()}, - \cmd{merge()}, \cmd{author(string)}, \cmd{date(interval)}, + \item \cmd{closed()}, \cmd{head()}, \cmd{merge()}: simple changeset properties -Closed heads: -\begin{lstlisting} -$ hg log -r "head() and closed()" -\end{lstlisting} - -Reopened branches: -\begin{lstlisting} -$ hg log -r "closed() and not head()" -\end{lstlisting} + \item \cmd{author(string)}, \cmd{date(interval)}: search by user + name or by commit date \begin{lstlisting} $ hg log -r "author('Martin') and merge()" \end{lstlisting} + \item \cmd{grep(regex)}, \cmd{keyword(string)}: search in commit + message, user name, changed file names for a regular expression or + a substring + + %\item \cmd{bisected(string)}: changesets marked good/bad/skip while + % bisecting + + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{Matching by Files in Changesets} + Matching by how a file changed: + \begin{itemize} + \item \cmd{adds(pattern)}: a file matching pattern was added + \item \cmd{modifies(pattern)}: a file matching pattern was modified + \item \cmd{removes(pattern)}: a file matching pattern was removed + \item<2-> \cmd{file(pattern)}: combination of all the above + \item<3-> \cmd{contains(pattern)}: a file matching pattern was present + \end{itemize} +\end{frame} + +\subsection{Functions} + +\begin{frame}[fragile]{Following the Changeset Graph} + A common task is to follow the graph from a particular changeset: + \begin{itemize} + \item \cmd{::set} or \cmd{ancestors(set)}: ancestors of changesets in set + \item \cmd{set::} or \cmd{descendants(set)}: descendants of changesets in set + \item \cmd{X::Y}: a combination of the above, finding changesets between X and Y \end{itemize} - bisected(string) - grep(regex) - keyword(string) + \pause - \cmd{adds(pattern)} - \cmd{contains(pattern)} - \cmd{file(pattern)} - \cmd{filelog(pattern)} - \cmd{modifies(pattern)} - \cmd{removes(pattern)} - + Changes that need to be merged into the default branch: +\begin{lstlisting} +$ hg log -r "ancestors(stable) - ancestors(default)" +$ hg log -r "::stable - ::default" +\end{lstlisting} \end{frame} +\begin{frame}[fragile]{Family Relations} + + \begin{itemize} + \item \cmd{ancestor(single, single)}: greatest common ancestor of + the two changesets. Used to find out what needs to be merged in a + merge between X and Y: +\begin{lstlisting} +$ hg log -r "ancestor(X, Y)::Y" +\end{lstlisting} + \item \cmd{children(set)}, \cmd{parents([set])}: set of all children/parents of set + + \item \cmd{heads(set)}, \cmd{roots(set)}: changesets from set with + no children/parents in set + +\end{itemize} +\end{frame} -\begin{frame}{Functions} - ancestor(single, single) - ancestors(set) - children(set) - descendants(set) - follow([file]) +\begin{frame}[fragile]{Parents and Grand Parents} + Going from a changeset to the parent changeset is easy: + \begin{itemize} + \item \cmd{p1([set])}, \cmd{p2([set])}: the first/second parent of + changesets in set or of the working copy if no set is given + + \item \cmd{x\textasciicircum}, \cmd{x\textasciicircum 2}: the + first/second parent of \cmd x + + \item \cmd{x\textasciitilde n}: the $n$'th first ancestor of \cmd x, + \cmd{x\textasciitilde 0} is \cmd x, \cmd{x\textasciitilde 3} is + \cmd{x\textasciicircum\textasciicircum\textasciicircum} + \end{itemize} + + To see both sides of a merge changeset M use +\begin{lstlisting} +$ hg diff -r "p1(M):M" && hg diff -r "p2(M):M" +\end{lstlisting} + or the shorter +\begin{lstlisting} +$ hg diff -c M && hg diff -r "M^2:M" +\end{lstlisting} - heads(set) + %\item \cmd{follow([file])}: follow working copy parents or follow a + % file history across renames (like \cmd{hg log -f}) +\end{frame} - outgoing([path]) +\begin{frame}[fragile]{The Next Push} + The \cmd{hg outgoing} command tells what will be pushed, and so does + this function: + \begin{itemize} + \item \cmd{outgoing([path])}: changesets not in the destination + repository + \end{itemize} + +\pause + + It is now easy to see what you will push as a single diff: +\begin{lstlisting} +$ hg diff -r "outgoing()" +\end{lstlisting} +% \cmd{hg diff} extracts the first/last revision using \cmd{min()} and +% \cmd{max()} + +\pause - p1([set]) - p2([set]) - parents([set]) - present(set) - roots(set) + It is also easy to reset a repository: +\begin{lstlisting} +$ hg strip "outgoing()" +\end{lstlisting} + People familiar with Git will know this as +\begin{lstlisting} +$ git reset --hard origin/master +\end{lstlisting} + +\end{frame} + +\begin{frame}[fragile]{Handling Missing Revisions} + If you don't know if a given revision is present, then use: + \begin{itemize} + \item \cmd{present(set)}: prevents lookup errors if a revision in + set is not found. Used like +\begin{lstlisting} +$ hg log -r "head() and (present('bad'):: - present('fix')::)" +\end{lstlisting} + where bad is a known buggy changeset and fix is a bugfix. Without + the use of \cmd{present()}, an error would be raised if the bugfix + is not yet in the repository. + \end{itemize} \end{frame} \begin{frame}{Final Touches on Your Query} @@ -114,8 +252,7 @@ \begin{itemize}[<+->] \item \cmd{max(set)}, \cmd{min(set)}: the changeset with minimum/maximum revision number in the set - \item \cmd{reverse(set)}: the set is an ordered set; this reverses - it + \item \cmd{reverse(set)}: the set is ordered; this reverses it \item \cmd{limit(set, n)}, \cmd{last(set, n)}: the first/last $n$ changesets \item \cmd{sort(set[, [-]key...])}: sorting the set by revision @@ -135,8 +272,10 @@ \end{enumerate} First match wins. + \pause + You can override this using predicates: - \begin{itemize}[<+->] + \begin{itemize} %\item \cmd{rev(number)} %\item \cmd{id(hash)} \item \cmd{bookmark([name])}, \cmd{tag([name])}: the changeset with @@ -153,22 +292,39 @@ \subsection{Operators} \begin{frame}{Operators} + You can combine two revision sets using: + \begin{itemize} + \item \cmd{x and y} or \cmd{x \& y}: changesets in both \cmd x and + \cmd y + + \item \cmd{x or y} or \cmd{x | y} or \cmd{x + y}: changesets in + either \cmd x or \cmd y + + \item \cmd{x - y}: changesets in \cmd x but not in \cmd y + + \end{itemize} \end{frame} \begin{frame}[fragile]{Examples} \begin{itemize} - \item Changes that need to be merged into the default branch: -\begin{lstlisting} -$ hg log -r "ancestors(stable) - ancestors(default)" -$ hg log -r "::stable - ::default" -\end{lstlisting} + \item Heads on the current branch: \begin{lstlisting} $ hg log -r "head() and branch(.)" \end{lstlisting} + Closed heads: +\begin{lstlisting} +$ hg log -r "head() and closed()" +\end{lstlisting} + + Reopened branches: +\begin{lstlisting} +$ hg log -r "closed() and not head()" +\end{lstlisting} + \item Open heads on the current branch: \begin{lstlisting} $ hg log -r "head() and branch(.) and not closed()" @@ -187,61 +343,35 @@ \section{File Sets} \begin{frame}{Selecting Files} + File sets let you: \begin{itemize} \item select files from working copy \item select files from old revisions - \item might be part of Mercurial 1.9 (July) or 2.0 (November) \end{itemize} -\end{frame} - -\begin{frame} - File sets give an amazing flexibility. -\end{frame} - -\begin{frame}[fragile]{Working Copy Status} - The \cmd{hg status} command lets you select files: -\begin{lstlisting} -$ hg status -M index.html -A logo.png -$ hg status --added -A logo.png -\end{lstlisting} - -How do you see the change to \path{index.html} without seeing the diff -of the added \path{logo.png} file? You do: -\begin{lstlisting} -$ hg diff $(hg status --no-status --modified) -\end{lstlisting} - -With file sets you can do -\begin{lstlisting} -$ hg diff "set:modified()" -\end{lstlisting} -and it will work on all platforms. - + Hopefully part of Mercurial 1.9 (July) or 2.0 (November) \end{frame} \begin{frame}[fragile]{Working Copy Status} The proposed predicates are: \begin{itemize}[<+->] -\item status flags: \cmd{modified()}, \cmd{added()}, \cmd{removed()}, - \cmd{deleted()}, \cmd{unknown()}, \cmd{ignored()}, \cmd{clean()} +\item \cmd{modified()}, \cmd{added()}, \cmd{removed()}, + \cmd{deleted()}, \cmd{unknown()}, \cmd{ignored()}, \cmd{clean()}: + status flags -\item copied files: \cmd{copied()} +\item \cmd{copied()}: copied files, quite hard to extract today -\item tracked files that \emph{would} be ignored: \cmd{ignorable()} +\item \cmd{ignorable()}: tracked files that \emph{would} be ignored -\item all tracked files: - \cmd{tracked()} +\item \cmd{tracked()}: all tracked files -\item after a merge: \cmd{conflicted()} and \cmd{not conflicted()} +\item \cmd{conflicted()}: like \cmd{hg resolve --list} after a merge \end{itemize} + \end{frame} -\begin{frame}[fragile] +\begin{frame}[fragile]{Searching by Path} We can replace the \cmd{find} Unix command: \begin{itemize} \item \cmd{glob(P)} instead of \cmd{find -path P} @@ -256,49 +386,40 @@ This shows that \path{foo.h} is a new header file in version 2.0. \end{frame} -\begin{frame} +\begin{frame}{File Type Predicates} Other \cmd{find}-like predicates will be: \begin{itemize} - \item file type: \cmd{executable()}, \cmd{symlink()}, - \item permissions: \cmd{perm()}, \cmd{owner()} - \item other: \cmd{date()}, \cmd{size()} - \end{itemize} -\end{frame} - -\begin{frame} - Matching files by content: - \begin{itemize} - \item \cmd{grep()}: like the Unix \cmd{grep} we all love - \item \cmd{contains()}: simple sub-string matching - \item \cmd{binary()}, \cmd{text()} - \item \cmd{decodes()}: check if file can be decoded with the given - character set, such as UTF-8, UTF-16, \dots - \item \cmd{eol()}: line-ending type, Unix (\cmd{LF}) or DOS (\cmd{CRLF}) + \item \cmd{executable()}, \cmd{symlink()}: file type + \item \cmd{perm()}, \cmd{owner()}: file permissions + \item \cmd{date()}, \cmd{size()}: other file meta data \end{itemize} \end{frame} - - -\begin{frame}[fragile]{Working Copy Predicates} +\begin{frame}[fragile]{Looking Into Files} + Matching files by content: + \begin{itemize}[<+->] + \item \cmd{grep()}: like the Unix \cmd{grep} we all love + \item \cmd{contains()}: simple sub-string matching + \item \cmd{binary()}, \cmd{text()}: does file contain a NUL byte? \begin{lstlisting} -$ hg status -? logo.png -? index.html -$ hg status "set:unknown() and not binary()" -? index.html +$ hg add "set:unknown() and not binary()" \end{lstlisting} + + \item \cmd{decodes()}: check if file can be decoded with the given + character set, such as UTF-8, UTF-16, \dots + + Lets you find mistakes: +\begin{lstlisting} +$ hg status --all "set:glob('**.py') and not decodes('UTF-8')" +C src/foo.py +\end{lstlisting} + + \item \cmd{eol()}: line-ending type, Unix (LF) or DOS (CRLF) + \end{itemize} \end{frame} - -\begin{frame}[fragile]{Working Copy Predicates} -\begin{lstlisting} -$ hg status -c 100 "set:decodes('UTF-8')" -M plan.txt -\end{lstlisting} -\end{frame} - -\begin{frame} - Future extensions: +\begin{frame}{Adding New Predicates} + The feature will be extensible, some possible future extensions: \begin{itemize} \item \cmd{magic()}: recognize files based on file content, like the \cmd{file} program in Unix @@ -324,12 +445,39 @@ \end{description} \end{frame} +\section{Conclusion} -\appendix -\newcounter{finalframe} -\setcounter{finalframe}{\value{framenumber}} +\begin{frame}{Conclusion} + In short: + \begin{itemize} + \item revision sets lets you zoom in on the right part of the history + \item file sets will let you pick out the relevant files + \item both mechanisms are completely general + \end{itemize} + + \pause + + Please get in touch if you have more questions: + \begin{itemize} + \item Email: \curl{mg@aragost.com} + \item IRC: \curl{mg} in \curl{\#mercurial} on \curl{irc.freenode.net} + \end{itemize} -\setcounter{framenumber}{\value{finalframe}} + \pause + + \begin{center} + \begin{tikzpicture} + \tikzstyle{every node}=[font=\Huge\bfseries] + \node[black, shift={(0.8pt, -0.8pt)}] {Thank you!}; + \node[orange!50!red] {Thank you!}; + \end{tikzpicture} + \end{center} +\end{frame} + +%\appendix +%\newcounter{finalframe} +%\setcounter{finalframe}{\value{framenumber}} +%\setcounter{framenumber}{\value{finalframe}} \end{document}