* dVCS by way of git Sharing complete repositories is a simple concept which involves a subtle paradigm shift, which in turn opens up interesting new pastures. In this talk I will demonstrate some of these constraints and their solutions, as implemented in git. * A look back at SVN ** Linear history is normal, all graphs are trees In other words, any given commit can have many children, but only one parent. ** Merging is painful and error prone Most solutions to this problem involve writing appropriate commit logs or writing out to files so merges can be traced. Screwing this up can be bad, and as a result it is avoided as much as possible. ** Sharing changes consists of mailing patches Obviously this was all workable, but it didn't exactly engender itself to lazy people like myself. The existance and popularity of CVSup in spite of being written in Modula 3 shows the value of repository sharing. You can think of git as CVSup done right. * Constraints ** Repositories are collections of interwoven histories So: *** Offline operation means history is frequently not linear *** Merging must be easy *** Sharing changes must be easy * How git satisfies dVCS constraints ** History is no longer linear Time is no longer a useful identifier when comparing the history of disparate repositories, and thus can't be used for commit identifiers. Something new must be found. *** git uses SHA hashes to identify repository objects SHA-1 hashes are the basic identifier of every object in the git system, which yields a bunch of nice properties we'll get into later. ** Merging is elevated to a first class operation Git makes merging easy(ier). It will probably never be trivial, but git at least automates the grunt work of tracking down common ancestors to reduce conflicts and ease merging. ** Branching is trivial and encouraged Creating a branch is just creating another ref pointing to an existing commit. It's very fast and efficient. It's very easy to move things between branches, and they are encouraged for any non-trivial work. It doesn't even mess up your history graph a lot of the time, and when it does you can often alter it so it does not. *** What is the object store? **** blobs Blobs are blobs of binary data. **** trees Trees point to blobs or other trees. **** commits Git commits contain a tree, its parent commits, and a tree object, along with meta-data: message, author, commiter, and so forth. **** tags Tag objects contain a commit id and an optional message and cryptographic signature. If neither are present, a tag is merely a symbolic ref. *** All objects are identified by SHA hashes. The unit of history is the commit which can be soley identified by its contents. The hash is easy to compute and provides good entropy properties when building a hash table. **** Some measure of security comes for free All commits are effectively signed by all their previous commits, so verifying a repository becomes trivial given only a valid commit id. *** Investigating the object store **** TODO Show perl code and output of commit/tree/blob from .git/objects **** There is no delta concept in the object store Deltas are generated by `git gc' when it creates pack files. ** merge commits In git, a commit can have many parents, as opposed to SVN where a commit can have only one parent. All commits contain a tree, so when you had to resolve conflicts from a merge, those will be contained in the commit's tree object. ** SHA hashes are a pain to type Git has a concept of `refs' which are typically symbolic references to commits. At the end of the day, every ref ends up as a SHA hash. *** SHA hashes can typically be shortened to a few characters *** tags are fixed refs Tags always refer to a commit, but can also contain a cryptographic signature and message, in which case the ref points to a tag object, which, in turn, points to a commit. For almost any use of tags, you don't need to care about this, since git is fairly smart about it. *** branches and HEAD are symbolic refs Branches are moving refs and always reference their tips. HEAD is a pointer to the tip of the current branch. *** $ref^ and $ref~$n You can follow parents by using caret or tilde notation. Merge commits are followed in their order in the commit blob. # ^ is the parent, ^^ is the paren't parent, and so on e.g: HEAD^ (The next most-recent commit on the current branch) # ~2 is shorthand for ^^ e.g: HEAD~2 (The third most-recent commit on the current branch) ** Sharing commits *** Remotes *** Implicit read-only "vendor" branches. *** Push and Pull *** Example * Merge strategies *** Fast forward When the merge target is an ancestor of the other branch, this just points the target's HEAD at the other branch. *** Recursive Used when more than one common ancestor exists. Builds the merge base revision by recursively merging common ancestors. *** And others See git-merge(1) * A brief note on the index The index stores the tree object of the commit-to-be. # adding to the index cache: git add # removing: git rm --cached ** git reset Can be used to reset the index, or certain files in the index, to a given commit, which is HEAD by default. * Problems git solves ** Mixed two patches together # git reset $filename # git add --patch # git commit ** In combination with git rebase, entire histories can be manipulated # git rebase -i $ref # git reset HEAD^ # git add --patch # git commit -c ORIG_HEAD # git add -u # git commit # git rebase --continue * My seekrit agenda I am a lazy programmer, and the more people who use git the easier my life is. I use git because... * Additional Resources # Git - SVN Crash Course # GitWiki # Git User's Manual # Extensive Man Pages