#lang meta/web

(define-context "stubs/git"
  #:robots (add-newlines (for/list ([d '(plt libs testing play)])
                           @list{Disallow: /@|d|/})))

(provide git)
(define git
  @page[#:title "Development Repository" #:file ""]{
    This is a stub page to get the header for the gitweb server.})

;; ----------------------------------------------------------------------------
;; gitweb stuff

(define header+footer
  (lazy (cdr (or (regexp-match
                  ;; extract just the meat between the <body>...</body>
                  #rx"<body[^<>]*>(.*?){{{BODY}}}(.*?)</body>"
                  (xml->string @page[#:id 'git #:html-only #t "{{{BODY}}}"]))
                 (error 'gitweb-skeleton "internal error")))))
(define header @plain[#:file "header.html" (car  (force header+footer))])
(define footer @plain[#:file "footer.html" (cadr (force header+footer))])

(define gitweb-logo (copyfile (in-here "gitweb-logo.png") "tiny-logo.png"))

(define home-text
  (lazy @text{
    @p{This is the Racket git server.}
    @p{See the "brief", PLT-oriented @intro{introduction to git}.}}))
(define home-file @plain[#:file "home-text.html" home-text])

(define gitweb-config
  @plain[#:file "gitweb_config.perl"]{
    our $projectroot = "repos";
    @||
    # used in the page's title tag
    our $site_name = "Racket Repository";
    # header/footer and home text html files
    our $site_header = "header.html";
    our $site_footer = "footer.html";
    our $home_text = "@(regexp-replace #rx"^.*/" (home-file) "")";
    push @"@"stylesheets, "@(the-resources 'style-path)";
    @||
    our $favicon = "@(the-resources 'icon-path)";
    our $logo = "@gitweb-logo";
    our $logo_url = "http://racket-lang.org/";
    our $logo_label = "Racket Homepage";
    @||
    # specification of visible repositories (managed by gitolite)
    our $projects_list = "plt-repos";
    our $projects_list_description_width = 80;
    our $default_projects_order = "age";
    # hide repositories that are not listed in the above
    our $strict_export = 1;
    @||
    # show repository only if this file exists
    our $export_ok = "git-daemon-export-ok";
    @||
    # respond with "server busy" above this
    our $maxload = 4;
    @||
    # label for the "home" link
    our $home_link_str = "root";
    @||
    $feature{'pathinfo'}{'default'} = [1];
    $my_uri = "http://git.racket-lang.org/";
    $home_link = "http://git.racket-lang.org/";
    # ? $feature{'javascript-actions'}{'default'} = [0];
    @||
    # some smaller features, usually overrideable by individual repos
    $feature{'grep'}{'default'} = [1];
    $feature{'grep'}{'override'} = 1;
    $feature{'blame'}{'default'} = [1];
    $feature{'blame'}{'override'} = 1;
    $feature{'pickaxe'}{'default'} = [1];
    $feature{'pickaxe'}{'override'} = 1;
    $feature{'snapshot'}{'default'} = ['zip', 'tgz'];
    # $feature{'snapshot'}{'override'} = 1;
    $feature{'avatar'}{'default'} = ['gravatar'];
    # $feature{'avatar'}{'override'} = 1;
    })

;; ----------------------------------------------------------------------------
;; git "guide"

(define intro (let ()

(define (cmd  . text) (span class: "code" text))
(define (path . text) (span class: "path" text))
(define (man name . text)
  (a href: (list "http://www.kernel.org/pub/software/scm/git/docs/"
                 (and name (list name ".html")))
     (if (null? text) (span class: "man" name) text)))
(define (selflink . url) (a href: url url))
(define git-host        "git.racket-lang.org")
(define at-racket       "@racket-lang.org")
(define at-git-racket   "@git.racket-lang.org")
(define (npre . text) (apply pre style: "margin-left: 0;" text))
(define style
  @style/inline[type: 'text/css]{
    .p {
      display: block;
      margin: 1em 0;
      @; text-indent: 1em;
    }
    .code, .path, .man, pre {
      font-family: monospace;
      font-size: large;
      font-weight: bold;
      background-color: #eeeeee;
    }
    .code, .path, .man {
      white-space: nowrap;
    }
    .the_text a:link, .the_text a:visited {
      text-decoration: underline;
    }
    .the_text pre {
      margin-left: 2em;
      padding: 0.6em 0 0.6em 0.6em;
    }
    .the_text ul, .the_text ol, .the_text dl,
    .the_text li, .the_text dt, .the_text dd {
      margin-top: 1em;
      margin-bottom: 1em;
    }})

;; xhtml strict doesn't allow lists inside <p>, so fake our own paragraphs
;; using divs:
(define p* (make-separated-tag values (λ text (apply div class: 'p text))))

@page[#:title "git intro" #:extra-headers style]{

@sections[#:newpages? #t]

@div[class: 'the_text]{

@section{Getting git}
@p*{
  I @strong{highly} recommend getting a new git installation.  Git itself is
  pretty stable (that is, you probably will not run into bugs with whatever
  version you have installed), but there are many usability related
  improvements.  Specifically, I am using 1.7.x and it is likely that some
  things in this document are specific to that version.
@~
  You can @a[href: "http://git-scm.com/download"]{download a recent version},
  available in binary form for several popular platforms.  In addition to
  these, you can get a build for
  @ul*{
  @~ Ubuntu:
     @pre{sudo add-apt-repository ppa:git-core/ppa
          sudo apt-get update
          sudo apt-get install git-core}
  @~ OSX using macports:
     @pre{sudo port selfupdate
          sudo port install git-core +svn}}
  (For OSX, you can also get @a[href: "http://gitx.frim.nl/"]{@cmd{GitX}} —
  it's a good gui front-end for git, similar to @cmd{gitk} and @cmd{git gui}.)
@~
  You can also build git from source is — here are the steps that I'm using to
  install a new version:
  @pre{GVER=1.7.10
       BASE=http://git-core.googlecode.com/files
       TARGET=/usr/local
       cd /tmp; curl $BASE/git-$GVER.tar.gz | gunzip | tar xf -; cd git-$GVER
       make prefix=$TARGET all && sudo make prefix=$TARGET install}
  If you do this and you want the @man[#f]{man pages} too, then getting the
  pre-built man pages is the easiest route (building them requires some
  “exotic” tools):
  @pre{cd $TARGET/share/man
       curl $BASE/git-manpages-$GVER.tar.gz | gunzip | sudo tar xf -}}

@section{General git setup}
@p*{
  Commits to a git repository are done locally, so git needs to know who you
  are.  (Unlike subversion, where you need to identify yourself to be able to
  talk to the server, so the commit object is created there based on who you
  authenticated as.)  To get git to know you, run the following two commands:
  @pre{git config --global user.name "My Name"
       git config --global user.email "foo@at-racket"}
  This sets your @em{default} name and email for @em{all} repositories — it
  stores this information in @path{~/.gitconfig} which is the global
  configuration file, used for such defaults.  You can edit this file directly
  too — it is in a fairly obvious textual format.  There is a lot that can be
  configured, see below for some of these (and see the @man{git-config} man
  page for many more details).
@~
  In addition to this file, each repository has its own configuration file
  (located at @path{.git/config}).  Whenever git needs to check some option, it
  will use both the repository-specific config file (if you're in a repository)
  and the global one.  The @cmd{--global} flag above tells git to set the
  option in the global file.  Note that a configuration file cannot be part of
  the repository itself — so when you get a repository, you still need to do
  any local configuration you want.  (This is intentional, since the
  configuration file can specify various commands to run, so it avoids a major
  security hazard.)
@~
  Important: this sets your default identity name and email for @em{all}
  repositories.  This may be a problem if you want to commit to different git
  repositories under different identities.  See the section on customizing git
  below for more details on this.}

@section{SSH setup}
@p*{
  Since git is a distributed system, you can do everything locally on your own
  repository, but obviously, the goal is to communicate with other people so
  you'll need to push these changes somewhere else.  The most popular way to
  communicate with remote repositories — including repositories on the PLT
  server, is via ssh.  (Access is controlled via a tool called “gitolite” —
  more on this below.)  The username and hostname of the server is
  @cmd{git@at-git-racket} — and you should be able to connect to this account
  using the ssh identity key that corresponds to the public key that you use
  with the git server.  To try it, run
  @pre{ssh git@at-git-racket}
  and the server (gitolite, actually) should reply with information about your
  current permissions.  The exact details of this is not important for now,
  just the fact that you were able to connect and get some reply.
@~
  Using an ssh configuration file (usually @path{~/.ssh/config}), you
  can set up a short name for the server.  For example, you can have this:
  @pre{Host pltgit
         HostName @git-host
         User git}
  and now you can simply use @cmd{ssh pltgit info} instead of the last example:
  @cmd{ssh} will know that @cmd{pltgit} is actually defined as
  @cmd{git@at-git-racket}.
@~
  This is the @strong{preferred} way to set things up: besides being more
  convenient in that you need to type less — it is also a useful extra level of
  indirection, so if the server settings ever change (for example, we might
  switch to a non-standard port number), you can simply edit your ssh config
  file, and continue working as usual.  In addition, such a configuration is
  needed if you created a specific ssh identity file to be used with git —
  specifying an alternative identity file on the @cmd{ssh} command line is
  possible (an @cmd{-i} flag, in the case of openssh), but remember that most
  of your interactions with the remote server are done implicitly through git.
  (It is possible to configure how git invokes ssh, but it is much easier to
  just configure ssh).  In this case, you will have:
  @pre{Host pltgit
         HostName @git-host
         User git
         IdentityFile ~/.ssh/my-plt-git-identity-file}
@~
  In addition to an ssh configuration file, git also has a way to create prefix
  shorthands.  For example, if you use this configuration:
  @pre|{git config --global url.git@foo.org:.insteadOf foo:}|
  then whenever git expects a repository URL, it will replace @cmd{foo:}
  with @cmd|{git@foo.org:}|, for example:
  @pre{git clone foo:bar}
  While it is possible to use this instead of an ssh config file to access the
  @cmd{plt} repository, the former is preferable.  The reason for that is that
  you will also interact with the server directly via ssh commands (described
  in the following section).  Keeping the alias in your ssh configuration means
  that you will use the same alias for both @cmd{git} commands and other
  @cmd{ssh}-based commands.  You may still want to use it for other servers,
  specifically, here is a popular setup for github (this is configuration text
  that you can paste into your global @cmd{.gitconfig} file):
  @pre|{[url "git://github.com/"]
          insteadOf = github:
        [url "git@github.com:"]
          pushInsteadOf = github:
          pushInsteadOf = git://github.com/}|
  It translates @cmd{github:} to a github read-only @cmd{git://} URL, and it
  translates pushes to the same prefix to use github's ssh URLs.  Note that it
  also translates the read-only @cmd{git://} url to an ssh url for pushing.
  (The same setup can be used for @cmd{gist.github.com}, to deal with github
  gists via @cmd{git}.)}

@section{Gitolite: the server's gateway}
@p*{
  All access to the PLT server is done via @cmd{ssh}, and this is where
  gitolite comes in as the “who can do what” manager.  What actually happens on
  the server is that no matter what command you're trying to run (as you
  usually would, for example: @cmd{ssh somewhere ls}), the server has settings
  that make it always run its own command — and that is a gitolite script.  The
  script knows the command that you were actually trying to run, and it will
  reply based on that.  In the above ssh example, you're not specifying any
  command (so if it wasn't for the pre-set gitolite script, you'd be asking for
  a remote shell to start), and gitolite responds by telling you about your
  permissions.
@~
  This is actually the @cmd{info} command, so you get the same reply with
  @cmd{ssh pltgit info}.  Again, this connects to ssh and tries to run
  @cmd{info}; gitolite sees that you're trying to run @cmd{info}, and instead
  of running it, it responds with that information.  There are a few additional
  commands that you can use this way — these are all “meta commands” in the
  sense that you're not interacting with a git process on the other end, but
  rather get gitolite to perform various tasks on your behalf.  You can run the
  @cmd{help} command (@cmd{ssh pltgit help}) to see a list of available
  commands.  They are mostly useful in dealing with your private repositories
  on the server, which will be discussed further below.}

@section{A (very) quick introduction to git}
@p*{
  This is a quick description; see the last section for more resources
  (specifically,
  @a[href: "http://eagain.net/articles/git-for-computer-scientists/"]{
    Git for Computer Scientists} covers these basics well).  Understanding how
  git models and stores data will make it significantly easier to work with it.
@~
  A git repository is actually a database of a few kinds of objects, which form
  a DAG.  There are only a few of these kinds of objects, and they are all
  addressed by the SHA1 checksum of their contents.  You will generally see a
  lot of these SHA1 strings (40 hexadecimal characters), since they form a kind
  of a universal address for such objects.  (For convenience, any unique prefix
  of a SHA1 can be used with git commands when you need to refer to it.)
  Whenever the following descriptions mention a pointer — this is actually such
  a SHA1 hash.}
@ul*{
@~ A @em{blob} object is a generic container for any information, which
   (usually) represents a file.  This object has no pointers to any other
   objects.  It does not have anything except for the actual contents: no name,
   permission bits, etc.
@~ A @em{tree} object represents a directory hierarchy: it contains a list of
   names, and for each name a pointer to the object that is its contents.  Some
   of these will point at blobs (when the tree contains a file), and some of
   these will point at other trees (when it contains a sub-tree).  (These
   objects are similar to directories in a file system in that they contain all
   “meta” information on files: their names and permission bits are kept here.)
@~ A @em{commit} object represents a versioned snapshot of a tree, forming a
   line of work.  It has the following bits of information:
   @ul*{@~ tree: a pointer to the tree object that was committed
        @~ parent: a pointer to the previous commit, which this one revised
        @~ author: the identity of the commit author (name, email, date)
        @~ committer: the identity of the committer
        @~ the text of the commit message (which can be arbitrarily long)}
   The parent field is actually any number of parents: there will be no parents
   if this is the first commit in the line of work, or more than one parent if
   this is a “merge” commit that merges two lines of work.  Furthermore, there
   is nothing that prevents a git repository from having completely separate
   lines of work — in fact, you can have several independent projects contained
   in your repository.
   @br
   @small{(Note that git distinguishes the author of a commit from the person
     who actually performed the commit, for example — a patch could be created
     by X, and sent to Y to be committed.)}
@~ Finally, there is a @em{tag} object, which is very roughly a pointer to
   another object (almost always a commit), and is not important for now.}
@p*{
  The fact that all of these objects are addressed by the SHA1 hash of their
  contents has some immediate important implications.}
@ul*{
@~ Since SHA1 are cryptographic checksums, they can be considered @em{unique}
   for all practical purposes.
@~ The git repository is inherently hash-consed: you can never have “two
   identical files” in git — because a file is stored at its SHA1 hash, two
   identical files will always be stored once. (Note that the name of a file is
   stored in the tree that contains it, so the SHA1 of the contents does not
   depend on it.)  The same holds for two trees: if you have two identical
   directories (same contents of files, same names, etc), then there will
   actually be only one tree stored in the repository.
@~ Furthermore, these addresses are actually global: any two repositories that
   hold a file with the same contents will have it at the exact same SHA1 hash.
   (For example, if I have a repository that contains several projects, and
   each project contains several copies of the same LGPL text, then I'll have
   only a single blob object with that contents.)  This is not only making the
   store efficient, it also makes it possible to refer to an object by its hash
   — for example, you can refer to the SHA1 of a specific file at a specific
   version in an email, and this will have the exact same meaning for anyone
   that reads the file (eg, anyone can run @cmd{git show @i{SHA1}} to see that
   file).  (This does require that the readers have the actual object in their
   repository, of course — but no mistakes can happen, statistically speaking.)
@~ This holds for commits too: since a commit has the SHA1 of the tree it
   points to, then the commit SHA1 depends on the tree it points to.  More
   importantly, since a commit object has the SHA1 of its parent(s), then the
   commit depends on them.  This means that “replaying” a number of commits on
   a different parent commit (eg, when doing a “rebase”) will always result in
   a separate line of commit objects.  These SHA1s are also global, meaning
   that talking about a specific revision by its SHA1 will always refer to it
   unambiguously (as long as others have that object in their repositories).
@~ By itself, this kind of storage @em{cannot} have any reference cycle.  (At
   least there is no practical way to get one.)  The storage is therefore
   inherently a DAG.  In addition to this object store, git does have a number
   of external references (eg, a branch is actually a pointer to a SHA1) — and
   those could be arbitrary, but the object storage itself cannot have cycles.
@~ The fact that a commit has a pointer to a tree is what makes git keep
   revisions of the whole tree — a commit cannot mark a change to a subtree.
   (At least not with the usual higher-level commands that git implements.)}
@p*{
  On top of this object store, there is a layer of meta-information about it.
  The most important component here are branches (and tags).  A branch is
  basically a file that has the SHA1 of a specific commit (for example, your
  @cmd{master} branch is a SHA1 that is stored in
  @path{.git/refs/heads/master}).  This is what makes branch creation extremely
  cheap: all you need to do is create a new file with the SHA1.
@~
  In addition, the @cmd{HEAD} (where your working directory is currently), will
  usually have a symbolic reference rather than a SHA1 (you can see this
  symbolic reference in the @path{.git/HEAD} file, which should usually look
  like @cmd{ref: refs/heads/@i{branch-name}}).  When you commit a new version,
  a new commit object is created, and the branch that the @cmd{HEAD} points to
  is updated.  It is also possible to checkout a specific SHA1 of a commit
  directly — the result of this is called “detached HEAD”, since the HEAD is
  not a symbolic reference.  The possible danger in doing this is that @cmd{git
  commit} will create new commits that are derived from the one you're on, but
  no branch is updated; if you later checkout something else, no reference is
  left to your new commit which means that it could be lost now.  For this
  reason, if you checkout a SHA1 directly, git will spit out a detailed
  warning, including instructions on how you could name your current position
  (create a branch that points there).
@~
  Tags come in two flavors: lightweight tags are SHA1 pointers like branches.
  The problem with this is that such a tag could easily move to a different
  commit, which is considered bad practice.  For this reason, there are also
  “annotated tags”, which are tag objects that are created in the object store.
  These tags contain information that is similar to a commit (there's the
  tagger's identity, the commit that it points to, and a log message) — and
  they are reliable since you can refer to their SHA1.  In this case, the
  symbolic reference for such a tag (its name) will point to the tag object in
  the store (it is also possible to move it, but that would also be bad
  practice).  Furthermore, tags (of both kinds) can point to any object in the
  store — they can point to a tree or even to a specific blob.  This is
  sometimes used to store meta-information (eg, web pages) inside the
  repository.  (The repository for git itself has a tag that points to a blob
  holding the maintainer's GPG key.)
@~
  Note that all of this is under a more high level of managing information
  between branches and repositories, with push/pull being the main operations
  at that level.  A high-level overview (more below):
  @ul*{
  @~ a branch is a line of development, represented as a pointer to the commit
     at its tip;
  @~ branches can be organized into hierarchies using @path{/} as a separator;
  @~ some branches are local, and some are remote — remote ones are named
     @path{remotes/origin/@i{branch}};
  @~ local branches are represented as files in
     @path{.git/refs/heads/@i{branch}} and remote ones are in
     @path{.git/refs/remotes/origin/@i{branch}};
  @~ @cmd{origin} is just the conventional name for the original repository you
     cloned — later on you can add new remote repositories so you can push and
     pull to/from them conveniently;
  @~ some local branches are set to track remote ones, usually (but not
     necessarily) the two will have the same name;
  @~ you can also have local branches to track other local branches (with
     pushing and pulling happening inside your repository);
  @~ @cmd{git fetch} is used to update your remote branches — ie, connect to
     the remote repository, get new commits (and the required parents and
     trees), and update your remote branch with the new tips;
  @~ @cmd{git merge} and @cmd{git rebase} are used to update one branch with
     commits on another;
  @~ @cmd{git pull} is, roughly speaking, a convenient way to do a fetch
     followed by a merge (or a rebase, when used with @cmd{--rebase}).}
@~
  There are several git tools that are relevant here.  These are @em{not}
  commands that you need to know for everyday use — so you can ignore this
  part.  It's only relevant if you want to see more of the low level structure
  (or maybe if you want to write code that interfaces with a repository at this
  level).}
@dl*{
@~ @cmd{git show @i{SHA1}}
@~ Show the object, in some appropriate way based on the type of the object.
   (For blobs it shows the contents, for trees you get a listing of its
   contents, and for commits it shows the log and the patch.)
@~ @cmd{git cat-file {-t | -s | @i{type} | -p} @i{SHA1}}
@~ A more low-level command that tells you the type/size of an object (@cmd{-t}
   and @cmd{-s}), or shows the contents of an object as-is when given a type.
   @cmd{-p} will “pretty-print” the object, eg, showing the contents of a tree
   object instead of dumping its binary encoding.
@~ @cmd{git gc}
@~ Starts from a rootset holding all known references (branches, tags, etc),
   and collects dangling objects.  Such objects are generated due to various
   reasons — for example, rebasing means that new commits are generated, and
   the old ones are kept around.  Actually, this will not remove recently
   referenced objects — there is a protection mechanism that keeps them around
   for a while, so if you somehow mess things up there is still a way to
   recover.
@~ @cmd{git fsck}
@~ Does a “file system check” on the repository.
@~ @cmd{git rev-parse @i{symbolic-name}}
@~ Prints out the full SHA1 of a symbolic name (eg, a branch name or a tag
   name).  Will also print out the SHA1 given a possibly short prefix of one.
   (Actually, this command can also show other information about a repository,
   which makes it an important entry point for programs that deal with a
   repository.)}

@section{Clone the PLT repository}
@p*{
  As you probably know by now, in git you don't checkout a repository — you
  clone it, getting a copy of the complete repository you cloned.  This
  includes the object store and the various references (branches and tags).
  There are several ways to get the PLT repository, but the one that is
  relevant to work on it is to do so through ssh — since this allows pushing
  changes back to the server.  (It is also possible to clone from one place and
  push to another, but if you start with cloning through ssh your clone will be
  already set up to push changes back.)  The information that gitolite gives
  you (with @cmd{ssh pltgit info}, assuming the above ssh setup) includes two
  repositories that you have write access to: @cmd{plt} is the main repository,
  and @cmd{play} is setup similarly (intended to try things out, see the
  “Fooling around” section below).  To get the main repository, run
  @pre{git clone pltgit:plt}
  which will create a @path{plt} directory with your new clone.  You can now
  start working in this directory.
@~
  The repository is also available from other sources, some can be used for
  read-only cloning:
  @ul*{
  @~ @cmd{git clone git://@|git-host|/plt.git}@br
     cloning the repository using git's own network protocol
  @~ @cmd{git clone http://@|git-host|/plt.git}@br
     clone the repository over http
  @~ @cmd{git clone http://github.com/plt/racket.git}@br
     this uses the repository mirror on github, which is automatically kept in
     sync (you can also use @cmd{https://...})}
  and some present a web interface for additional information:
  @ul*{
  @~ @cmd{@selflink{http://@|git-host|/plt}}@br
     a web interface to inspect the repository
  @~ @cmd{@selflink{https://github.com/plt/racket}}@br
     github's fancier web interface}}

@section{Start working: git commits vs subversion commits}
@p*{
  As seen in the previous section, you start with
  @pre{git clone pltgit:plt@";" cd plt}
  And now you get to actually do some work.
@~
  For the normal cycle of operations, working with git is not all that
  different from working with subversion — you would change some files, and
  then:
  @pre{git commit some/paths}
  or
  @pre{git commit some/paths -m "add some feature" -m "requires another"}
  only now the commit lives only in @strong{your clone only}, not in the server
  (which is why committing is blindingly fast, not requiring a network
  connection).  To push your commits to the server, run @cmd{git push}, and to
  pull updates from the server run @cmd{git pull}.  This is obviously very much
  oversimplifying the process: mainly neglecting to talk about updates on the
  server when you already have local changes.  (See below for a more detailed
  explanation.)  Note that in these examples I'm explicitly specifying the
  paths to commit, either the files that you want to commit or a directory
  where you want to commit all changes.  See the section below on the “staging
  area” for more details.
@~
  One major difference to keep in mind is that git commits are @strong{not}
  like subversion commits.  (This is confusing since many places that discuss
  the difference between the two and/or try to teach git to subversion users
  almost always work under the assumption that commits in the two systems are
  the same.)  The thing is that git commits are done at a finer level than
  subversion commits — since a commit is done locally and not on the server.
  To really imitate how subversion works, you would push all commits right
  after you create them — essentially equating commits with pushes, which is
  how you work with a subversion repository.  But by just @em{not} doing this,
  you will immediately get some of the benefits that git gives you.  So a
  better way to think about it is: in git you commit at points that make sense
  for the respective changes, usually at a finer level than subversion commits.
  Then, you push back a bunch of commits to the server — whether one or a
  hundred.  The point where you push your changes to the server is effectively
  the point where you decide that you're in a good enough state to make your
  work public.
@~
  Incidentally, following this intuition, drdr is running a build for every
  push to the server — not for every commit.  When you push to the server, it
  will tell you which push number this is — these numbers are going to be used
  by drdr, and they (very!) roughly correspond to subversion commits.
  (Currently, every push gets a number, but in the future this might be used
  only for pushes to the master branch.)  There's no plan at the moment to use
  these numbers for anything else.}

@section{Fooling around with git}
@p*{
  Experimenting with git is easy to do, and the server is set up to make it
  even easier.  You can use one of the following ways to experiment safely with
  the main repository:
  @ul*{
  @~ There is a @cmd{play} repository on the server.  This repository is very
     similar to the @cmd{plt} repository, and it is set up in the same way that
     @cmd{plt} is.  Feel free to destroy it in any way you want, even if it
     becomes unusable, it's easy to just recreate it.
  @~ You can create (and later delete) your own repositories — including making
     your own copy of the main repository, an operation that is known as
     “fork”.  Your fork will be created efficiently (ie, creating a fork of the
     @cmd{plt} repository is cheap), but any changes made to it will not affect
     the main repository.  A fork is created with a gitolite command, and once
     it's there you can clone it and eventually delete it.  Here are the
     relevant commands — use your actual username in place of @cmd{$user} (or
     have @cmd{$user} set to your username):
     @ol*{@~ @cmd{ssh pltgit fork plt $user/myplt}
          @~ @cmd{git clone pltgit:$user/myplt}
          @~ ...play with this clone, push, pull, etc...
          @~ @cmd{ssh pltgit delete $user/myplt}}
     More on user repositories below.}}

@section{The staging area}
@p*{
  Something that tends to confuse people is git's “staging area”.  This is a
  concept that is unique to git — roughly speaking, you can have three versions
  of a tree:
  @ul*{
  @~ the files that you actually see (and edit) — the working directory,
  @~ another is the staging area which you add stuff to from the working tree
     using @cmd{git add},
  @~ and then there is the tree that is in the HEAD with all prior versions.}
@~
  The thing that can confuse here is that when you @cmd{git add some/file} for
  a file that you edited (or created) and then edit it further, then the
  version will get committed by a plain @cmd{git commit} will be the one that
  was added.  Note that @cmd{git status} will tell you which modifications are
  in the staging area waiting to be committed, and which modifications are in
  your working directory — in the given example, it will tell you that
  @path{some/file} is in both.
@~
  The staging area can be useful at times, but most likely at the beginning
  stages you will want to just avoid it.  The good news is that it is easy to
  do so.}
@dl*{
@~ @strong{Avoid the @cmd{-a} flag}.
@~ @p*{
     Before we see how to ignore it, note that there are many web pages that
     will tell you to use @cmd{-a} with the commit command.  This will make git
     commit all changes to tracked files — @strong{including tracked files that
       are outside of your current directory}, and this can make you commit
     changes that you didn't intend to commit.}
@~ @strong{Always specify a path to @cmd{git commit}}.
@~ @p*{
     The easiest way to avoid the staging area is to specify the path(s) to
     what you want to commit, possibly @path{.} for all changes in the current
     directory (and below).  Specifying a path this way will make
     @cmd{git commit} behave very similarly to subversion: tracked files that
     were modified will get committed, and added files (with @cmd{git add})
     that are listed in the paths-to-be-committed are also committed.  Tracked
     and added files that are not listed (and not in a specified subdirectory)
     are left as-is.  So, if you had a habit of doing this with subversion
     (@cmd{svn commit .}), then git will essentially do the same.  You will
     still need to use @cmd{git add} for newly created files, but this is
     essentially the same as with subversion.
   @~
     It is also possible to “make up new git commands” for yourself.  See the
     following section on the subject: it adds a new @cmd{git ci} command that
     passes @path{.} to @cmd{git commit}, similarly to what @cmd{svn ci} does
     by default.}
@~ It is a good idea to @strong{avoid using the @cmd{-m} flag}, until you're
   more comfortable with git.
@~ @p*{
     Let git pop up an editor to write the commit log: the file that you will
     edit will list the changes that you are about to commit as well as changes
     that you are not going to commit.  Glancing through it, you will see
     changes that you missed, furthermore, the paths are relative which makes
     it easy to quickly distinguish paths in the current directory and outside
     of it (the latter will begin with @path{../}).  If you see any problems,
     just make sure that you have no commit message in the editor and when you
     quit it git will abort the commit (same as subversion).}
@~ @strong{Don't push out all commits to the server immediately}.
@~ @p*{
     Even if you did commit something by mistake, it is possible to undo the
     commit — run @cmd{git reset HEAD^}, which will undo the last commit (it
     moves the branch to the parent commit), and the changes that are no longer
     committed will be left in your working directory, so you get to try the
     commit again.  Note that this is possible @strong{only if you didn't push
       out the commit} that you're undoing — if you did, then the server will
     later not allow you to push changes that are not strict extensions of what
     it has (since this is likely to confuse other people who already got your
     commit).
   @~
     So in general, remember that you can commit often, and commit when it
     makes sense to do so, and push commits out only when you're done with
     whatever you were working on.  Consider your local git history as
     something that you have full control over: you can undo commits and redo
     them (in fact, @cmd{git commit --amend} does just that: undo the last
     commit, and combine it with new changes — it's a solution for “oops
     commits”), you can rebase them, and you can just throw away everything and
     start from scratch.  But when you do push your history out, the party is
     over, and any mistakes will need to be rectified in further commits (eg,
     you can no longer use that @cmd{--amend} flag, you have to do an “oops
     commit”).
   @~
     (BTW, strictly speaking, it is only the policy on our server that forbids
     such rewritten history — since this is likely to be a mistake for now, and
     if it happens most people will be confused about what needs to be done.)
   @~
     Also, as said above, pushing all commits immediately means that you're
     essentially restricting yourself to the same mode of operation as
     subversion.  Same mode, but more complicated — and you won't enjoy any of
     the benefits, which will guarantee that you will suffer.}}

@section{Configuring and extending git}
@p*{
  As mentioned above, git uses several configuration files that customize
  various aspects.  The two important ones are your global file
  (@path{~/.gitconfig}) and a per-repository file in @path{.git/config} at the
  repository root — whenever a value is needed, git will first consult the
  repository configuration, and if the option is not set there it will try your
  global version.  (It will also look at a system-wide configuration, but this
  is irrelevant here.)  Configuration options names are separated by a
  @path{.}, and configuration files have a simple syntax, with @cmd{foo.bar}
  option listed as a @cmd{bar = some value} line in a @cmd{[foo]} section.
  Note that you can set @em{any} configuration you want to, no restrictions.
  This can be useful for customizing various extensions, including scripts that
  you may want to write (for example, the git server has a script that checks
  the @cmd{hooks.counter} option to know if it should keep track of pushes).
  This is facilitated by several options to @cmd{git config} which makes it
  easy to query configurations from scripts etc.
@~
  To edit your configuration options you can either use the @cmd{git config}
  command, or you can edit the file directly.  When @cmd{git config} changes
  the file it rewrites only part of the file and leaves the rest untouched,
  which conveniently leaves your own format and any comments you might want to
  include.  To set a value through the command and then get it:
  @pre{git config foo.bar some-value
       git config foo.bar}
  and you can add a @cmd{--global} flag to either form to use only your global
  configuration file.  There are many other options — for dealing with keys
  that must be booleans or integers, for keys with multiple values, etc.  The
  @man{git-config} man page will tell you much more on this.
@~
  The man page also lists the configuration options that customize various
  functionalities.  Here are some important ones that you should consider
  setting (each listed as a command that sets it globally):
  @ul*{
  @~ @npre{git config --global user.name "My Name"
           git config --global user.email "foo@at-git-racket"}
     @p*{
       As said in the beginning of this text, you will likely want to set a
       default username and email for yourself.  But note that if you do set
       this globally, it will be your default identity for all repositories.
       This makes sense only if you commit to PLT-related repositories, but it
       can be confusing if you're also committing to some other non-PLT-related
       repositories and want to commit under a different email (or name) — for
       example, you may want to commit to a public project with a gmail
       address, and to a departmental repository with your
       @cmd|{foo@cs.bar.edu}| email.
     @~
       You could set the racket-lang.org identity locally in your PLT clone or
       you could set your other identity in the other repository, but in any
       case you should be aware of this and avoid letting git guess your name
       and email.  (Some confusion is likely to happen anyway, and git has a
       way to “map” some name/email to another when mistakes happen.)}
  @~ @npre{git config --global push.default tracking}
     @p*{
       By default, when you run @cmd{git push}, git will push all branches that
       correspond to branches in the remote repository.  This can be surprising
       if you're working on several branches since it will push them all out.
       Setting this option to @cmd{tracking} will make git push the current
       branch to the branch it is tracking.
     @~
       Another option for this is @cmd{current}, which makes @cmd{git push}
       always push the current branch to the remote it was cloned from.  This
       is convenient in that you never need to set up how local branches track
       remote ones — it's as if all local branches @em{always} track all remote
       branches under the same name.  For example, after you clone an empty
       repository (see the user repositories section below), a @cmd{git push}
       will push a master branch remotely — whereas with @cmd{tracking} you
       need to have the first push explicitly specify the branch to push,
       usually @cmd{git push origin master} (this sets things up so later you
       can just run @cmd{git push}).  However, using @cmd{current} you can no
       longer push from one local branch to another local branch it is set to
       track.
     @~
       So a possible conclusion here is that you should use @cmd{tracking},
       unless you plan on branches to always track remote branches by the same
       name.  @cmd{tracking} is often preferred over @cmd{current}.}
  @~ @npre{git config --global core.excludesfile "~/.gitignore"}
     @p*{
       You'll probably want to always ignore a number of common patterns, like
       backup files or OSX @path{.DS_Store} files.  To do this, you first set a
       default file as shown here (note that @path{~} is quoted, and git will
       expand it to whatever your home directory is).  If you have this
       setting, you can then create a file at this path with patterns for files
       that you always want to ignore.  This file has shell-patterns (and
       possibly @cmd{#}-comments) — for example:
       @pre{# backups
            *~
            # autosaves (note the #-quoting)
            \#*
            # OSX junk
            .DS_Store}
       (See the @man{gitignore} man page for a few more details.)
     @~
       In addition to this, git repositories can have their own
       @path{.gitignore} files (unlike @path{.git/config} files), which are
       combined hierarchically together with this global option.  In fact, you
       don't really need to set the above ignores for the PLT repository since
       they're already included in its toplevel @path{.gitignore} file — but
       doing so is still a good idea since you're likely to work on other
       repositories too.}
  @~ @npre{git config --global core.editor emacs
           git config --global core.pager less}
     @p*{
       These two settings are used to tell git which command to use for editing
       log messages, and which command is used to paginate output.  (The former
       might already be set in your environment as the value of the
       @cmd{EDITOR} variable.)  If you set the latter to @cmd{cat}, git will
       just spill all output directly.  In addition to these, you can also
       control which individual commands use a pager, for example, to disable
       the pager for @cmd{git log}, you can do this:
       @pre{git config --global pager.log false}}
  @~ @npre{git config --global color.ui auto
           @i{...}
           git config --global color.branch.current yellow red bold
           git config --global color.branch.local   yellow
           git config --global color.branch.remote  green
           @i{...}}
     @p*{
       These settings control how git uses colors: whether it shows them, and
       which colors it will use for various outputs.  There are many of these
       settings, which you can find in the @man{git-config} man page.}
  @~ @p*{
       @cmd{remote.origin.*}, @cmd{branch.master.*}
     @~
       Git keeps track of what the @cmd{origin} repository and how branches
       track other branches in the configuration file too.  (You will have such
       entries for all known remote repositories and branches.)  Usually you
       set these values (often implicitly) via various git commands — but you
       might want to look in your configuration file if you want to tweak
       things yourself.  Note that for configuration names with more than two
       parts, the section name will something like @cmd{[remote "origin"]}.}
  @~ @p*{
       @cmd{sendemail.identity}, @cmd{sendemail.from},
       @cmd{sendemail.bcc}, @cmd{sendemail.suppresscc}, ...
     @~
       These settings configure @cmd{git send-email}, which is used to send
       patches from your repository elsewhere.  You will probably want to
       customize them if/when you get to use this facility often.  (See
       below.)}}
@~
  In addition to these settings, you can extend git with your own aliases and
  commands.  Aliases are stored in your git configuration — so you can use
  @cmd{git config} to create an alias, for example, @pre{git config --global
  alias.up "pull --stat --all"} creates a global @cmd{git up} command which is
  actually a short alias for running @cmd{git pull --stat --all}.
@~
  @strong{Notes about aliases:}
  @ul*{
  @~ To edit aliases, it is more convenient to edit your configuration file
     directly.
  @~ Since aliases are stored in git configuration files, they can be made
     local to each repository.
  @~ When command-line arguments are given to the alias, they will be appended
     to the alias text.
  @~ Aliases @em{cannot} override git commands; this is intentional, to avoid
     scripts breaking due to modified commands.
  @~ An alias that starts with a @cmd{!} character will be run as a shell
     command.  For example, you can use
     @pre{k = "!gitk -d"}
     to make @cmd{git k} run the gitk program with the @cmd{-d} flag.
  @~ Some aliases that I find useful are:
     @pre{# satisfy the "up instinct"
          up = pull --ff-only --stat --all
          # quick status, similar to what subversion shows
          st = status -s
          # we will be dealing more with branches
          br = branch}}
@~
  In addition to aliases, you can create new git commands using a script that
  is called @cmd{git-@i{something}} somewhere in your path.  (Note that these
  cannot override known git commands either.)  Such commands will be available
  as @cmd{git @i{something}}.  One use for this is using our facility for
  managing file properties — the @path{collects/meta/props} program.  To do
  this put this in a file called @cmd{git-prop} somewhere in your PATH, for
  example, @path{~/bin/git-prop}:
  @pre|{#!/bin/sh
        top="$(git rev-parse --show-toplevel)" || exit 1
        exec "$top/collects/meta/props" "$@"}|
  then run @cmd{chmod +x ~/bin/git-prop}, and you can now use it as a git
  command (try @cmd{git prop -h}).  Note the use of the @cmd{rev-parse}
  command: it will display the repository root, which means that you will get
  to run the props script of the repository you're @em{currently} using.
  (There are many git commands that are useful for such scripts.)
@~
  Another useful script is @cmd{git-ci} which mimics the behavior of
  @cmd{svn commit} (avoiding confusion with the staging area).  As said above,
  a good way to achieve this is to specify the current directory (@path{.}) if
  you don't specify any other path.  If you save this as @path{git-ci}, you
  will get a @cmd{git ci} that does just that:
  @pre|{
    #!/bin/sh
    add_dot=yes; for p; do if [ -e "$p" ]; then add_dot=no; fi; done
    if [ -e "$(git rev-parse --git-dir)/MERGE_HEAD" ]; then add_dot=no; fi
    if [ -d "$(git rev-parse --git-dir)/rebase-apply" ]; then add_dot=no; fi
    if [ $add_dot = yes ]; then git commit . "$@"; else git commit "$@"; fi
    }|
  This small script will basically check all arguments and see if one is an
  existing path (or if you're resolving a merge).  If none are, it adds
  @path{.} as a first argument (this avoids confusing it as a value for some
  flag).  Note that this is not completely foolproof: for example, if you'll
  use the @cmd{-m .} hack, it will assume that you did specify a path.  (But
  you should really avoid such log messages.)}

@section{User repositories}
@p*{
  As mentioned above, the PLT server allows you to create your own
  repositories.  Repositories on the server can be organized in a nested
  directory structure, and you “own” all repositories that are in a directory
  with your username.  The gitolite @cmd{info} command that was mentioned above
  shows you this with a @cmd{C CREATER/.*} line: this means that you can create
  any repository if it is in a subdirectory with your username.  (In this
  discussion, “the server” is actually the gitolite script that runs on the
  server.)
@~
  Any git operation that you do on a repository that you own which does not
  exist will make the server create it for you — for example, if you clone such
  a repository.  To run these examples use your git username instead of
  @cmd{$user}, or simply set the @cmd{user} variable in your shell (as this
  example shows) — this is only to make copy-pasting easy, of course.
  @pre{user=eli # your own username here
       git clone pltgit:$user/foo}
  (You are encouraged to run these commands — at the end of this section you'll
  see how you can clean things up.)
@~
  What will happen now is (a) git will initialize a @path{foo} repository for
  you, (b) it will connect to the server to clone its contents, (c) the server
  will notice that it doesn't exist so it will create it, (d) your git process
  will clone the empty result.  Because the remote repository is empty, git
  will complain that “You appear to have cloned an empty repository” — this is
  expected, so you shouldn't worry about it.  Once you have your (empty) clone,
  you can populate it as usual, then push the new content back to the server's
  copy:
  @pre{cd foo
       @i{...create some files...}
       git add .
       git commit -m "initial content"
       git push origin master}
  Note that the last command explicitly names the branch to push over — once
  this push is done, git will remember this relation and further pushes can be
  done with just @cmd{git push}.  If you happen to forget this and use
  @cmd{git push}, then git will not push anything, and it will tell you about
  it and suggest specifying a branch.  On the other hand, if you set the
  @cmd{push.default} configuration option to @cmd{current} (as described in the
  customization section above), then even in this first push you can just run
  @cmd{git push} since git assumes that you always want local branches to
  correspond to remote branches by the same name.
@~
  Instead of cloning a repository to create a new one, you could also start
  with an existing repository and simply push it to a yet-to-exist repository
  on the server.  Again, the server will see that it doesn't exist and will
  create it for you (provided that it is in your directory).  To continue the
  above example, I could now create a new repository from @cmd{foo}:
  @pre{# (still in the foo directory)
       git push pltgit:$user/bar}
@~
  There is, however, an issue of efficiency here: with this last command I just
  created a second copy of it all.  This could be problematic if you have a
  large repository (eg, a copy of the @cmd{plt} repository).  (Note that with
  subversion this is the only way to do things, but there you would create
  copies inside the tree, which subversion optimizes.)  One nice feature of git
  is that creating a clone of a repository on the same filesystem will use
  hard-links for the clone, which makes the clone use very little additional
  space.  But the problem is that you have no access to the PLT server.  The
  solution here is in the form of a gitolite @cmd{fork} command (this is
  actually our own extension) — this command will create a clone on the server,
  starting from a specified repository.  I could therefore create my @path{bar}
  repository as a copy of @path{foo} with the following:
  @pre{ssh pltgit fork $user/foo $user/bar}
  (Note that if you follow these examples and you already have @path{bar}, the
  server will tell you about it.)  The result is a @path{$user/bar} repository
  that was cloned from @path{$user/foo}, and the two share their store using
  hard links.  If the two repositories are updated with identical content, the
  new content will not be shared, but for a large repository like the @cmd{plt}
  repository you still get the benefit of having the bulk of the data shared
  (the complete store, at the time of forking).  To get a feeling for how fast
  this is, you can now clone the @cmd{plt} repository to your own private copy:
  @pre{ssh pltgit fork plt $user/myplt}
  This would seem suspiciously fast for such a large repository — but this
  repository has most of the data packed (objects in the store are put in large
  “pack files”), so there are not too many files, and the server-side cloning
  basically created hard links to these files.  The result is fast, efficient
  (even in speed: when you interact with your clone, files are likely to be
  paged in memory), and cheap.
@~
  As we've seen above, the gitolite @cmd{info} command lists the permissions
  that you have, but it doesn't show you the actual repositories.  For this,
  there is an @cmd{expand} command.  (Yes, this is not a great name; it's
  related to how gitolite was intended to be used.  Remember that there is also
  a @cmd{help} command that describes the available gitolite commands.)  When
  you run the expand command — @cmd{ssh pltgit expand} — you get a listing of
  all of the repositories that you can access, each with an indication of read
  permissions (@cmd{R}) or write permission (@cmd{W}).  A @cmd|{@}| indicator
  means that you have the respective permissions because it is allowed for all
  users.  Each repository is also listed with its owner, or @cmd{<gitolite>} in
  case it is a globally configured repository.
@~
  Some of the gitolite commands are used to configure your repositories — you
  can only use these with repositories that you own.
  @ul*{
  @~ @cmd{getperms} and @cmd{setperms} — these are used to get or set
     permissions for your repositories.  The first will print the current
     permissions (which will be initially empty), and the second will read the
     permissions on its input and set them.  The format of the permissions is
     simple: each line begins with an @cmd{R} or @cmd{RW}, and then the
     usernames that this permission applies to.  You can use the magic username
     @cmd|{@all}| to grant access to everyone in the system.  For example, to
     grant read permissions to everyone, and write permissions to user1, create
     a file with:
     @pre|{R @all
           RW user1 user2}|
     and then run the setperms commands with this as its input:
     @pre{ssh pltgit setperms $user/foo < the-file}
     You can also just run the @cmd{setperms} command and type in the
     permissions directly.  Note that these permissions are not cumulative:
     every use of @cmd{setperms} specifies all permissions.  (We might have a
     more convenient interface for all of this in the future.)
  @~ @cmd{config} — this command can be used to set known configuration options
     in your repositories.  It works with sub-verbs:
     @dl*{@~ @cmd{ssh pltgit config list}
          @~ Displays the known configuration options
          @~ @cmd{ssh pltgit config get @i{repo} @i{config}}
          @~ Displays the configuration value of a specific repository
          @~ @cmd{ssh pltgit config set @i{repo} @i{config} @i{value}}
          @~ Sets the configuration value of a specific repository}
     These configuration options can customize aspects of the scripts that run
     after every push — currently, you can use this to set an email address to
     send notification emails to.  Other configurations may be added in the
     future.  (Note that this does not let you set any configuration, since
     some of these can execute arbitrary commands.)
  @~ @cmd{delete} — finally, this command can be used to delete repositories.
     For example, to clean up the above, you can now run:
     @pre{ssh pltgit delete $user/foo
          ssh pltgit delete $user/bar}
     The repositories are moved to a temporary holding directory, and will
     eventually be removed.  The bottom line here is that if you lost anything
     by mistake recently, chances are there's a backup of your repository.}}

@section{Working with git}

@subsection[#:newpage? #f]{Basics}
@p*{
  The above description is much simplified in that it doesn't deal with
  development that happens outside of your own work — and such development
  obviously changes the story.  Overall, this is not too different from working
  with subversion: if there were any changes on the server you need to update
  your working copy first, and this implies dealing with conflicts if there are
  any.  But the way to deal with such things in git is significantly different
  than dealing with them in subversion, and this difference is at the technical
  level (different commands) and at the workflow level (you will likely branch
  much more, and you're likely to push less frequently than you commit with
  subversion).
@~
  To start working, you first need to get a repository clone.  Usually you will
  clone the PLT repository (or a private copy that you do your work in), but
  remember that to experiment with git you have the @cmd{play} repository or
  you could make a fork of the PLT repository to play with and remove it when
  you're done.  Either way, be sure to try these things out — it will make your
  life much easier in the future.
@~
  In the following examples I will use an empty repository to demonstrate
  things and I will list the exact commands that I'm using (this means that I
  will use unix commands to create and edit files, and use @cmd{-m} when
  committing).  Lines that I enter are displayed with a @cmd{$} prompt, most
  output lines are omitted, comments start with @cmd{#}, and @cmd{$user} is set
  to my username.  Note that if you try this yourself, the SHA1s of commits
  will be different (the reason for that is that a commit object includes the
  author name and email and the date).  Note also that in some places I will
  “jump to an earlier continuation”: start from an earlier state and do
  something different — so if you want to try these things out it will be
  convenient to put the commands in a shell script so you can re-run it to get
  to the earlier state.
@~
  First, I create a private empty repository, populate it, and update the
  remote repository:
  @pre{$ user=eli                    # your own username here
       $ mkdir /tmp/sandbox; cd /tmp/sandbox
       $ ssh pltgit delete $user/foo # delete previous repository, if any
       $ git clone pltgit:$user/foo
       $ cd foo
       $ echo "foo" > foo; echo "bar" > bar
       $ git add .
       $ git st                      # uses the `st' alias as shown above
       A  bar
       A  foo
       $ git commit -m "initial content" .
       [master (root-commit) 87f1f02] initial content
       @i{...}
       # git tells us the branch we committed to, the new commit SHA1 and
       # that this is the first commit, and the log message; we can verify
       # this now with `git log'
       $ git log
       commit 87f1f02c23b32e7f9b...  # this is the commit object I created
       @i{...}
       $ git push                    # (or `git push origin master' if needed)
       To pltgit:eli/foo             # where we pushed to, and the branch
        * [new branch]      master -> master}
@~
  @small{[A quick note on commit messages: several git command consider the
    first paragraph of your commit message as a short description for it.  This
    is all of the text up to the first blank line if you write a commit message
    in an editor, or the first @cmd{-m} message if you use it with
    @cmd{git commit} (it accepts multiple @cmd{-m} arguments, for multiple
    paragraphs).  Keep this in mind when composing such messages.]}
@~
  To see what happens when multiple people commit to the repository, we create
  a second clone of our repository now in a @path{foo2} directory:
  @pre{$ cd ..
       $ git clone pltgit:$user/foo foo2
       $ cd foo                      # go back to foo now}
@~
  Lets make two new commits now:
  @pre{$ echo "more foo" >> foo; echo "more bar" >> bar
       $ git ci -m "more stuff"      # uses the `git-ci' script from above
       [master b7d3c41] more stuff
       $ echo "even more foo" >> foo
       $ git ci -m "even more stuff"
       [master 18bc0e6] even more stuff}
@~
  At this point, instead of blindly pushing these commits, lets look around
  first.  One useful tool for inspecting the history is @cmd{gitk} — if you run
  it now, you will see the simple 3-commit graph, and two of them are marked as
  branches — clearly showing that your local master branch is 2 commits ahead
  of the remote one.  This could be different at this point: someone else might
  have pushed more commits to the remote — remember that your remote master
  branch (@cmd{remotes/origin/master}) is not really what's on the remote, but
  rather what you know about it last time you pulled from it.
@~
  Another useful command to examine the history is @cmd{git log}, which can
  show commit history in many ways.  As things stand in the current repository,
  if you just run @cmd{git log}, you will see a listing of the same three
  commits that gitk shows.  To get a more condensed format with
  one-line-per-commit, use @cmd{--oneline}.  Another thing that you can do is
  show a specific range of commits — you can do this by specifying two
  revisions separated with @cmd{..}, where the revisions can be written
  explicitly using the (short prefix) SHA1 form, or more conveniently using a
  symbolic name (eg, branch, tag, HEAD):
  @pre{$ git log origin/master..master}
@~
  Since we @em{are} currently on the master branch, we could use @cmd{HEAD} for
  the second one (@cmd{origin/master..HEAD}), but this is also the default, so
  an even shorter form is @cmd{origin/master..}.  In addition to @cmd{git log},
  you can also use @cmd{git diff} in a similar way, but instead of a commit
  listing, you get the diff between the two specified points, so
  @pre{$ git diff origin/master..}
  will show you the changes that you did not yet push.  Note that there are a
  number of places where git will guess the full name of branches, for example,
  @cmd{origin/master} is actually a short name for @cmd{remotes/origin/master}.
  In a similar way, just @cmd{origin} will make git guess that you're talking
  about @cmd{origin/master}.
@~
  In these cases, the revision specification for @cmd{log} and @cmd{diff} are
  the same, but this is a little misleading: @cmd{git diff} usually works by
  comparing two specific end points in your history, but @cmd{git log} actually
  works on a @em{set of commits} rather than on a range.  The @cmd{R1..R2}
  notation is actually shorthand for @cmd{^R1 R2} — specifying a commit means
  “the commit and all of its parents”, and a @cmd{^} prefix negates a set, so
  @cmd{^R1 R2} means “include the set of commits leading to R2 (inclusive), but
  exclude the ones leading to R1 (inclusive)”.
@~
  In addition to this range/set specification, there is a lot to specifying a
  revision set.  As mentioned, you can use a SHA1 (or a shorter unique prefix),
  or you can use a symbolic name.  You can also use @cmd{R^} for the parent of
  @cmd{R} (the first parent in the case of merge commits, which have more than
  one parent), @cmd{R^^} would be the grand parent, @cmd{R~3} is the
  3rd-generation parent commit.  There is also @cmd|{R@{yesterday}}|,
  @cmd|{R@{1 month 2 weeks go}}| etc for a symbolic name R — which refers to
  the branch/HEAD at that point in time (this refers to @em{your own} version
  of it at that time; there are @cmd{--since} and @cmd{--until} flags to filter
  commits by the time they were made at).  You can also use a -N flag (where N
  is an integer) to show only N commits.  Finally, you can use a branch name R
  with @cmd|{R@{upstream}}| (short: @cmd|{R@{u}}|) to refer to the “upstream”
  version of the branch — the branch that R is set to follow.  This is
  particularly convenient for things like @pre|{$ git log --oneline @{u}..}|
  which will always show the commits that you have over the branch your current
  one follows.  (For example, you could set up an alias to use this.)
@~
  We now continue by pushing our two commits to the remote server.  Since we
  already did a push, a plain @cmd{git push} works fine.
  @pre{$ git push                    # no need to specify a branch now
       To pltgit:eli/foo
          87f1f02..18bc0e6  master -> master}
@~
  As you can see in the last line, git tells us that we pushed from our local
  @cmd{master} branch to the remote @cmd{master} branch, and that this made it
  advance from the first commit we pushed (87f1f02) to the last one we created
  now (18bc0e6).
@~
  Since we've made some progress in one place, we can go to the @path{foo2}
  clone to see what happens when we update a repository that did not have these
  changes:
  @pre{$ cd ../foo2
       $ git pull
       @i{...}
       From pltgit:eli/foo
          87f1f02..18bc0e6  master     -> origin/master
       Updating 87f1f02..18bc0e6
       Fast-forward
        bar |    1 +
        foo |    2 ++
        2 files changed, 3 insertions(+), 0 deletions(-)}
@~
  This looks expected — git shows the new commits that we received, and they're
  the same as what we pushed earlier.  Using @cmd{git pull} is actually doing
  two things: it's running @cmd{git fetch} first to update your remote branch
  from the server, and then it uses @cmd{git merge} to merge it into your
  master branch.  The point where @cmd{get merge} starts is the “Updating” line
  — and there's an important thing to note here: the next line says
  “Fast-forward”, which is a special kind of a merge.  When you merge some
  branch into your branch, and this branch is a proper superset of your branch
  (it has commits that your branch doesn't, and all commits in your branch are
  included in it), git will simply “move your branch forward” to the other: it
  will update your branch to the tip of the merged one, and then your working
  directory will be update accordingly.
@~
  It is often better to do the fetch first, so you can see the changes that
  happened remotely before you merge them.  To do this we're going to use
  @cmd{git fetch}, avoiding the merge step that @cmd{git pull} does.  In fact,
  since creating a merge commit is something that you might want to always do,
  @cmd{git pull --ff-only} will only do the merge if it will be a fast-forward
  merge.
@~
  Assuming we start again from the @path{foo2} repository before the above
  pull, we get the same output up to the point where the merge started:
  @pre|{$ git fetch
        @i{...}
        From pltgit:eli/foo
           87f1f02..18bc0e6  master     -> origin/master
        $ git log --oneline @{u}..
        # nothing
        $ git log --oneline ..@{u}
        # the same two commits}|
  The first log doesn't show anything, since we have no commits over the ones
  in the remote (the “upstream” of our current branch).  To see this, consider
  that after expanding empty names to @cmd{HEAD}, and the @cmd|{@{u}}| to the
  remote branch name we get @cmd{remotes/origin/master..master}, and this is
  short for @cmd{^remotes/origin/master master} — the set of commits made of
  our master branch and all parents, minus the set of commits from the remote
  branch and up — since it's ahead of that, we get an empty set.  The second
  log command reverses the two, giving us the set of commits that the remote
  has and the local branch doesn't.  In addition to @cmd{git log}, you can use
  @cmd{gitk} to inspect the repository: use the @cmd{--all} flag to make it
  show all branches.  Either way you'll be able to see that a fast-forward
  merge is possible.}

@subsection{Concurrent development}
@p*{
  Again, we'll assume starting with the @path{foo2} repository before the pull.
  We will now create a new commit before we get changes.  This makes it similar
  to commits pushed to the server while you do your work — so let's see how
  this common story goes and try to push this change:
  @pre{
    $ echo "blah blah" > blah
    $ git add blah
    $ git ci -m "blah"
    $ git push
    To pltgit:eli/foo
     ! [rejected]        master -> master (non-fast-forward)
    error: failed to push some refs to 'pltgit:eli/foo'
    To prevent you from losing history, non-fast-forward updates were rejected
    Merge the remote changes before pushing again.  See the 'Note about
    fast-forwards' section of 'git push --help' for details.}
@~
  As expected, git refuses to push our change.  The terminology in the error
  message is a little confusing — what it basically says is that the commit(s)
  that we are trying to push are not an extension of the tip of the master
  branch on the server.  A “non-fast-forward update” in this case would mean
  that we'd set the master branch on the remote to be the same as our branch —
  but this means that whatever commit that were pushed to the server (the two
  commits we pushed out from the @path{foo} clone in this case) will be lost.
@~
  To merge in the remote changes, we need to pull them in.  We'll now look at
  three different ways to do this.}
@h3{1. Playing it safe}
@p*{
  First, as we've seen above, doing a separate @cmd{git fetch} step would allow
  you to see where things stand before you do anything.  Alternatively, we can
  use the @cmd{--ff-only} variant of @cmd{git pull}, which will do a merge only
  if it's a fast-forward one, covering the trivial cases.  If a fast-forward
  merge cannot be done, it will tell you about it and then stop:
  @pre{$ git pull --ff-only
       @i{...}
       From pltgit:eli/foo
          87f1f02..18bc0e6  master     -> origin/master
       fatal: Not possible to fast-forward, aborting.}
@~
  We can now use the usual tools to see where things stand.  The following are
  all useful here:
  @ul*{
  @~ @cmd{gitk --all}@br
     This visualizes the commit graph.  If you do this, you will see the four
     commits that we have so far: the initial commit at the root, the commit
     that we did in this clone, and the two commit that we retrieved and are
     waiting on @cmd{remotes/origin/master}.
  @~ (a) @cmd|{git log @{u}..}|@br
     (b) @cmd|{git log ..@{u}}|@br
     (c) @cmd|{git diff @{u}..}|@br
     (c) @cmd|{git diff ..@{u}}|@br
     Inspect the commit that the local branch has over the remote (a), and the
     two that the remote has over the local one (b); look at the difference
     between the local branch and the remote either way (c).
  @~ (a) @cmd|{git log --left-right --oneline @{u}...}|@br
     (b) @cmd|{git log --left-right --oneline ...@{u}}|@br
     (c) @cmd|{git log --graph --oneline --all}|@br
     An alternative notation for specifying commit sets for @cmd{git log} is
     @cmd{R1...R2} (with @em{three} dots) — this stands for all commits from
     both R1 and R2 and their parents, but excluding commits from their “merge
     base” — the parent commit that both descend from.  As demonstrated in (a),
     this is especially useful with the @cmd{--left-right} flag: you'll see the
     commits that are new on the remote branch and the ones that are new on the
     local one, with @cmd{<} or @cmd{>} indicating which side each commit is
     coming from.  Yet another way that @cmd{git log} can be used is (c) with a
     @cmd{--graph} flag, which makes it render the commit graph in ASCII-art.
  @~ @cmd{git show-branch -a}@br
     This is another potentially useful commands that shows how commits are
     distributed over branches in your repository.  In this case you will see a
     matrix with the four commits in separate rows, and each will have a
     @cmd{+} or @cmd{*} indicating whether it is included in a branch.
  @~ (a) @cmd|{git diff @{u}...}|@br
     (b) @cmd|{git diff ...@{u}}|@br
     The three-dots notation is also used by @cmd{git diff}, with a slightly
     different semantics than in @cmd{git log} (remember that @cmd{git log}
     talks about commit sets, and @cmd{git diff} compares two specific points).
     In the @cmd{diff} case, these compare a specific branch tip to the merge
     base of this branch and another, which means that you see a diff with the
     work done on one branch that is not included in the other (this is unlike
     the two-dot syntax where you get the diff between the two branch tips).
     In the first (a) example you will see all changes that you did locally,
     and in (b) the changes that were done remotely.}}
@h3{2. Merging (not the fast-forward variant)}
@p*{
  Now that we did a @cmd{git fetch} or a @cmd{git pull --ff-only} to update the
  remote branch, we can proceed with merging it into our branch — which we can
  do in one of two ways:
  @pre{$ git merge origin            # merge origin/master into master
       Merge made by recursive.
       @i{...}
       # -or-
       $ git pull                    # will do a merge as usual}
@~
  Using @cmd{merge} is better for the usual reason: @cmd{git pull} can bring in
  more updates that were pushed by others since you fetched, and include them
  in the merge.  Note that if instead of running a separate fetch or pulling
  with @cmd{--ff-only} you were using @cmd{git pull}, then you'd essentially
  get to the same point we are now at.
@~
  Either way, the merge tells us “Merge made by recursive” — which is important
  here: if we see “Fast-forward” it means that no new commits were made, but
  “Merge made ...” means that a merge commit was created (“by recursive” refers
  to the merge strategy, git has several of them).  If we look at the commit
  graph now with gitk or with @cmd{git log}, we'll see the new commit that was
  created:
  @pre{$ git log --oneline --graph --date-order
       *   12bf7ee Merge remote branch 'origin'
       |\
       * | a40a45f blah
       | * 18bc0e6 even more stuff
       | * b7d3c41 more stuff
       |/
       * 87f1f02 initial content}
  The merge was successful without manual intervention (you didn't need to
  resolve any conflicts), so it proceeded immediately to create the commit
  that connects the two lines of development, and used some standard
  template for the commit log.  This is the safe thing to do as the
  default for git, but in this case it makes the history complicated with
  no good reason — it just happened that while we were working on @cmd{blah}
  someone else pushed an unrelated change.  (It could be related: perhaps
  one of the remote commits was referring to the file that I've added, but
  especially in the PLT case this would be very rare.)  If you use gitk to
  look at the recent history of the repository, you'll see many such
  commits, and they can make it harder to figure out how the development
  went on.}
@h3{3. Rebasing}
@p*{
  To get a simple/readable history, the goal is to have a more linear history:
  just have the two remote commits and move our commit to follow them (which
  would be the history that you get with subversion under similar conditions).
  But our commit object already points to its parent, so it @em{cannot} move:
  in the above graph, @cmd{a40a45f} is a hash that was computed based on
  @cmd{87f1f02} being its parent, and changing a parent means getting a new
  hash and therefore a new commit object.
@~
  This is where @cmd{git rebase} gets into the picture.  Assuming that we
  didn't merge as described above, we would just use @cmd{rebase} instead:
  @pre{$ git log --graph --all --oneline
       * a40a45f blah                # \
       | * 18bc0e6 even more stuff   #  \
       | * b7d3c41 more stuff        #   > same tree as before the merge
       |/                            #  /
       * 87f1f02 initial content     # /
       $ git rebase origin
       First, rewinding head to replay your work on top of it...
       Applying: blah                # git tells us that it re-applies this
       $ git log --graph --all --oneline
       * 6ebd1fb blah                # \
       * 18bc0e6 even more stuff     #  \ the resulting history
       * b7d3c41 more stuff          #  / is linear
       * 87f1f02 initial content     # /}
@~
  Here's what git did in this rebase: it (1) moved the HEAD to the merge base
  between your local branch and the remote one — the @cmd{87f1f02} commit; (2)
  did a fast-forward merge, which just moves your local branch to the tip of
  the remote one; (3) it now @em{replays the same changes} that you had in your
  commits (only @cmd{a40a45f} in this case) on top of the new tip, leading to
  @em{new} commit objects (@cmd{6ebd1fb} in here).  So we end up with a fresh
  commit object, and the old one (@cmd{a40a45f}) is gone.  (It's not really
  gone — it's kept in your repository store for a while, to protect you from
  losing work.)  If you look at the complete details of the new commit using
  gitk or @cmd{git log}, you will see that this commit has different dates for
  the author and the committer date:
  @pre{$ git log --format=fuller -1 6ebd1fb # all details, show only one commit
       @i{...}
       AuthorDate: 2010-05-02 10:26:00 -0500
       @i{...}
       CommitDate: 2010-05-02 10:30:00 -0500}
  This is because rebasing just created a new commit — but the author time is
  still considered the same.  In any case, you now have linear history that is
  a proper descendant of the remote branch, and you can now push your changes
  out.
@~
  Practically every place where you read about rebasing in git will warn you
  about not doing it for public history.  The problem is that if someone had a
  copy of your previous branch (@cmd{a40a45f}), then next time they will
  update, if their copy of the branch is updated, then things will change in a
  nasty way: even more so if they've committed more changes on that branch.
  (This “someone” can also be yourself, of course.)  This also explains why
  @cmd{git pull} does not rebase by default.
@~
  As far as the PLT repository goes, server will never allow pushes that are
  not strict extensions of what's on it (in other words, it only allows
  fast-forward pushes) — so you won't be able to mess things up for others.
  But as long as your change is something that you work on privately, there is
  absolutely no problem in doing this.  Note that this is the same thing as
  with re-doing commits because of mistakes: as long as a commit did not go
  out, you can fix it in multiple ways; when it does get pushed to the server,
  the only practical way to fix it is by pushing another commit.  (In some
  extreme cases we may do such a thing: for example, if you commit a passwords
  file, then there is no other way to remove it completely from the repository
  — but these are very rare, and such fixes affect everyone.)
@~
  It is therefore best to get one of two habits when you do a @cmd{git pull}:
  either use @cmd{--ff-only} or @cmd{--rebase}.  The latter is a little more
  convenient but you might not feel comfortable about doing a rebase
  automatically — it @em{might} just be that someone worked on the same set of
  files, and you really prefer a plain merge.  For this, you might prefer using
  @cmd{--ff-only} which will automatically work in the trivial cases, and
  otherwise leave you in a state where you can look at things and decide how to
  proceed yourself.}

@subsection{Additional forms of history tweaking}
@p*{
  As described in the previous section, rebasing is not some kind of a magical
  operation: it is really just an expected by-product of the way git works — of
  the fact that commits can be created as descendants of any commit (not just
  tip commits).  You could perform a rebase manually by starting from some
  commit, then inspect each of the changesets that the rebased history
  contains, and play them back on the new commit.  (Lumping this tediousness
  lead to a script, which lead to the rebase command.)  This means that you
  don't really have to limit yourself to replaying these commits exactly as
  they were — for example, you could write new commit log messages, combine two
  commits into one, drop some commits, or reorder their order.
@~
  The rebase command has a flag that makes doing all of these things easy:
  @cmd{--interactive}.  Continuing the above example, we now have four commits
  in our history — and say that we want to tweak the last two.  If we now run
  @pre{$ git rebase HEAD~2}
  we ask git to rebase our current head off of its grandparent commit (remember
  that @cmd{HEAD^} is the parent, @cmd{HEAD^^} is the grandparent, and
  @cmd{HEAD~2} is an alternative syntax for @cmd{HEAD^^}).  Since it @em{is}
  already based there, @cmd{git rebase} does nothing, and tells us that the
  branch is up to date.  But if we add @cmd{--interactive}, we get something
  different: git pops up an editor with this text:
  @pre{pick 18bc0e6 even more stuff
       pick 6ebd1fb blah
       # Rebase b7d3c41..6ebd1fb onto b7d3c41
       # @i{...}}
  This is a listing of the last two commits with their one-line log messages.
  As the text that is below these lines say, you can replace the @cmd{pick}
  before a commit with a different command: you can use @cmd{reword} to get to
  write a different log message (you will get another editor window to do the
  editing), @cmd{squash} to combine a commit with the previous one (it will let
  you edit the log message for the combination), @cmd{fixup} which does the
  same but discards the log message, and finally @cmd{edit} will make the
  rebasing process stop at the relevant commit and let you tweak it before it
  continues.  In addition, removing a commit line means that the commit will be
  skipped, and reordering lines will replay the commits in a different order.
  If any of these lead to conflicts, the rebasing will stop for a manual
  resolution, and you'll need to @cmd{git rebase --continue} when resolved, or
  @cmd{git rebase --abort} to get back to the original state.
@~
  Note that since our @path{foo2} clone tracks a public @path{foo} repository,
  this particular rebase is bad: we intend to edit the last two commits, but
  only one is local to @path{foo2} — the other is a commit that we got from
  @path{foo}, and changing it means that we will get a rebased commit based on
  its parent, and the server will forbid pushing it later.  If you see that you
  went too far when you see the rebase editor, all you need to do is keep those
  lines untouched: in the trivial case of leaving an initial set of commits in
  unmodified, they will be “rebased” by leaving them in as is.  (If you inspect
  the history later, you will see that they have the same SHA1s.)
@~
  A useful case of using @cmd{squash} (or @cmd{fixup}) with interactive
  rebasing is doing @cmd{checkpoint} commits frequently, and eventually
  combining them to a single commit.  This demonstrates one a popular git
  principle: keep commits as logical units that correspond with the changes
  done, since there is no central server that dictates a
  public-commit-or-nothing.  Doing these will make it easier in the future to
  deal with the history: inspect the changeset as a whole, undo it, and it also
  works well with finding bugs in the history — you can have checkpoints for
  intermediate states of the code even if it doesn't work, since this state
  will eventually be hidden.
@~
  A particularly common case for editing history is “oops commits”: you just
  made some change, committed it, and then realized that something is wrong —
  you forgot to change some related reference, to remove some debugging
  printout, or to describe some new aspect of the commit.  You could use
  @cmd{git rebase HEAD^} in these cases to rebase just the last commit while
  editing it, but there is a much more convenient way to do this: @cmd{git
  commit --amend}.  Usually, @cmd{git commit} creates a new commit based on the
  current branch tip and a given commit log message, but with @cmd{--amend} it
  does something different: it takes a snapshot of the tree as usual, but it
  makes the commit be a descendant of the tip's parent commit.  For example,
  assuming we didn't really change anything with the rebase above, our history
  and recent change is now:
  @pre|{$ git log --oneline
        6ebd1fb blah
        18bc0e6 even more stuff
        b7d3c41 more stuff
        87f1f02 initial content
        $ git log --oneline -p -1     # -p => show patch, -1 => only one
        6ebd1fb blah
        diff --git a/blah b/blah
        @i{...}
        --- /dev/null
        +++ b/blah
        @@ -0,0 +1 @@
        +blah blah                    # this is the recent change}|
  This is obviously wrong — we need to have three “blah”s there.  With
  subversion we would now need to perform an “oops, forgot a blah” commit, and
  in fact, we would need to do the same with git if we push these change out
  now.  But as long as we didn't, we can fix it without an additional commit
  using @cmd{--amend}:
  @pre{$ echo "blah blah blah" > blah
       $ git add blah
       $ git ci --amend -m "blah^3"
       [master 5cf863d] blah^3       # the re-made commit
       $ git log --oneline
       5cf863d blah^3
       18bc0e6 even more stuff
       b7d3c41 more stuff
       87f1f02 initial content}
  As you can see, the last commit is gone (remember that it is still backed up,
  in case of problems), and there is a completely new commit instead.  Usually,
  @cmd{--amend} is used without @cmd{-m} — the log message editor will be
  initiated with the previous log message, so you can edit it instead of
  rewriting it from scratch.  If there were no modifications to be committed, a
  @cmd{git commit --amend} is a convenient way to edit the last commit message
  only.  If you leave the message untouched, a new commit will still be made —
  one with a new commit time; and if you delete the text completely, the
  re-commit will be aborted, and you will be left with the old one intact.}

@subsection{Resetting the tree}
@p*{
  Both the @cmd{commit --amend} feature and rebasing build on the ability to
  “move” the current branch tip to some earlier commit in its history.  To do
  this directly, git provides a @cmd{git reset} command, which can move the
  current branch tip to a specified commit, and adjust the working directory
  and/or the staging area accordingly.  For example, for the @cmd{--amend}
  functionality, you will use @cmd{HEAD^} to move the branch tip to its parent
  commit.  You can of course specify any other commit to move to, and since git
  branches are effectively short bookmarks, you can create branches to be able
  to move to them later on (or as targets for rebasing, merging, etc).  In
  addition, you can use @cmd{HEAD} (or just omit the target, since @cmd{HEAD}
  is the default) to only change the working directory (or staging area).
@~
  The reset command has three major modes for its work, specified with a flag.
  (See the @man{git-reset} man page for a more thorough explanation, with lots
  of usage examples, some have evolved into their own functionality — like the
  @cmd{--amend} feature.)  Using @cmd{HEAD^} as the target commit, here are
  some summaries of how it can be used:
  @ul*{
  @~ @cmd{git reset --hard HEAD^}@br
     This will move the branch tip to the previous commit, and will change the
     working tree and the staging area to match.  Translation: completely
     forget the last commit and any work in the working directory.
  @~ @cmd{git reset --soft HEAD^}@br
     Moves the branch tip, but does not change the working directory or the
     staging area.  Translation: undo the last commit, and leave your working
     directory in a state where @cmd{git commit} will get the same change in.
     (Note: not the @cmd{git ci} script — this will add changes in the working
     directory, if any.)
  @~ @cmd{git reset --mixed HEAD^}@br
     Moves the branch tip and the staging area to the parent commit, and any
     modifications done by the commit that are going to be lost are put in your
     working directory.  Translation: similar to the @cmd{--soft} version,
     except that the staging area is cleared, so to recommit the changes you
     will need to add files again (or use @cmd{git ci} as usual).
     @br
     (Note: This is the default mode.)}
@~
  When using @cmd{HEAD} (which is the also default when nothing is mentioned),
  the branch tip is not moved, and we get:
  @ul*{
  @~ @cmd{git reset --hard}@br
     Get rid of all changes to the working directory and the staging area.
     Translation: lose all work that was not committed, getting back to the
     content on the branch (a convenient way to do something similar to an
     @cmd{svn revert -R .} in the root of a subversion working directory).
  @~ @cmd{git reset --mixed}@br
     Get rid of all changes to the staging area, leaving your working directory
     intact.  Translation: lose everything that was added to the staging area.
     If you're avoiding it (for example, if you only use the @cmd{git ci}
     script), then this would be a no-op other than new files that were
     @cmd{git add}ed.
  @~ (@cmd{git reset --soft} is a no-op.)}
@~
  When @cmd{git reset} changes the HEAD, it creates another toplevel reference
  name called @cmd{ORIG_HEAD} that points to the previous commit that
  @cmd{HEAD} pointed at, so if you happen to @cmd{git reset --hard HEAD^} by
  mistake, you can immediately get back to it with
  @cmd{git reset --hard ORIG_HEAD} (but changes in the working tree would still
  be lost).  (@cmd{git merge} is another command that changes the @cmd{HEAD},
  and it also saves the previous value in @cmd{ORIG_HEAD}.)  Finally, note that
  @cmd{git reset} can be restricted to make it work only on a specific set of
  paths, not on the whole repository.}

@subsection{Other forms of reverting}
@p*{
  While we're on the topic of reverting files, there are three more things
  worth mentioning:
  @ul*{
  @~ @cmd{git checkout -- @i{path ...}}@br
     @p*{
       When @cmd{git checkout} is given some paths, it will only check out the
       relevant files from their state in the staging area.  This is a more
       popular way to revert changes to a specific file.  If you avoid using
       the staging area, then this is roughly the same as using reset with the
       @cmd{--hard} flag on the paths, since your staging area will usually be
       the same as your @cmd{HEAD}.  (Note that the @cmd{--} is optional, and
       needed only when a path name can be confused with a branch name.)
     @~
       As with @cmd{reset}, you can also specify a branch to check the path(s)
       from — which is useful to try some files from a different branch
       selectively.  However, note that unlike subversion, git does not
       remember the association of the branch and the paths that were checked
       out of it (the branch is not “sticky”) — the files will simply be
       considered as modified (and they will not be updated when the branch is,
       unless you do the same checkout).}
  @~ @cmd{git show HEAD:@i{path}}@br
     @p*{
       This shows the file as it exists in the @cmd{HEAD}, making it useful to
       inspect the file before you made some additional modifications (similar
       to @cmd{svn cat @i{path}}).  You can also omit the @cmd{HEAD} — using
       @cmd{:@i{path}} will show the file in the staging area, which will
       usually be the same as the @cmd{HEAD}.  One caveat to note here is that
       the path should be the full path relative to the repository root.
       (Note: I have a wrapper @cmd{git cat} script that emulates
       @cmd{svn cat}, I'll add it if anyone wants.)}
  @~ @cmd{git revert @i{commit}}@br
     @p*{
       The @cmd{git revert} command is used to revert the changes introduced by
       the given commit.  It will basically apply the change in that commit
       in reverse, then ask you for a log message for a new commit where the
       message is initially populated with text indicating the commit that
       was applied in reverse.
     @~
       Note that this is very different from @cmd{svn revert} — it is more like
       @pre{svn merge -c-123@";" svn commit "Revert revision 123"}
       Since this is a frequent source of confusion, the @man{git-revert} man
       page mentions it at the top, and it refers readers to @cmd{git reset}
       and @cmd{git checkout} as the way to do the equivalent of @cmd{svn
       revert} (which are described above.)}}}

@subsection{Dealing with conflicts}
@p*{
  We'll now see how to deal with merge conflicts.  First, we'll set up the
  repository for a conflict.  Continuing with the @path{foo2} clone, we'll
  first create a file (which I'll do here using shell commands, to make it easy
  to play with), commit, and push the new history (which includes the blah
  work) back to the server.  Note the use of @cmd{git branch -v} which shows
  the local @cmd{master} branch and the fact that there's two commits that we
  haven't pushed out yet.
  @pre{$ echo "#lang racket"    > foo
       $ echo "(define (foo x)" >> foo
       $ echo "  (* x x))"      >> foo
       $ git ci -m "turn foo into a library"
       [master fd856ef] turn foo into a library
       $ git branch -v
       * master fd856ef [ahead 2] turn foo into a library
       $ git push
       To pltgit:eli/foo
          18bc0e6..fd856ef  master -> master}
@~
  Now hop over to the @path{foo} clone, get the changes (the relevant bits of
  the output are shown), edit the file (using sed, to make it a command line),
  inspect the change, commit it, and push.
  @pre|{$ cd ../foo
        $ git pull
        From pltgit:eli/foo
           18bc0e6..fd856ef  master     -> origin/master
        Updating 18bc0e6..fd856ef
        Fast-forward
         blah |    1 +
         foo  |    6 +++---
         2 files changed, 4 insertions(+), 3 deletions(-)
         create mode 100644 blah
        $ sed -i '2s/x/[x 0]/' foo
        $ git diff
        diff --git a/foo b/foo
        index 78d9889..b81de80 100644
        --- a/foo
        +++ b/foo
        @@ -1,3 +1,3 @@
         #lang racket
        -(define (foo x)
        +(define (foo [x 0])
           (* x x))
        $ git ci -m 'add a default value'
        [master 5035c9a] add a default value
        $ git push
        To pltgit:eli/foo
           fd856ef..5035c9a  master -> master}|
@~
  And now get back to @path{foo2}, and before we pull, modify the same line by
  adding a comment and commit, then do a @cmd{--ff-only} pull and watch it
  refuse to merge as expected, then look at the history so far.
  @pre{$ cd ../foo2
       $ sed -i '2s/$/ ; int->int/' foo
       $ git ci -m 'document the type of foo'
       [master 21a78df] document the type of foo
       $ git pull --ff-only
       From pltgit:eli/foo
          fd856ef..5035c9a  master     -> origin/master
       fatal: Not possible to fast-forward, aborting.
       $ git log --graph --all --oneline -4
       * 21a78df document the type of foo # ← our change
       | * 5035c9a add a default value    # ← the conflicting change we pulled
       |/
       * fd856ef turn foo into a library
       * 5cf863d blah^3}
@~
  Rebasing is the common thing to do, but let's see what happens with a plain
  @cmd{merge} first:
  @pre{$ git merge origin
       Auto-merging foo
       CONFLICT (content): Merge conflict in foo
       Automatic merge failed; fix conflicts and then commit the result.}
@~
  We now have a conflict that needs to be resolved before we can finish the
  merge.  Using @cmd{git st} (the alias listed above for the svn-like status
  that @cmd{git status -s} produces) shows a new @cmd{UU} status for @path{foo}
  — this indicates an “unmerged” (conflicted) file.  To investigate further, we
  use a plain @cmd{git status}, which tells us that our history diverged from
  the remote (we already know that since @cmd{pull --ff-only} failed) and count
  the diverging commits, and it also tells us that @path{foo} is unmerged and
  hints at using @cmd{git add} to resolve it:
  @pre{$ git st
       UU foo
       $ git status
       # On branch master
       # Your branch and 'origin/master' have diverged,
       # and have 1 and 1 different commit(s) each, respectively.
       # Unmerged paths:
       #   (use "git add/rm <file>..." as appropriate to mark resolution)
       #       both modified:      foo}
  You can also see that git knows about the conflict and refuses to do a
  commit:
  @pre{
    $ git commit
    fatal: 'commit' is not possible because you have unmerged files.
    Please, fix them up in the work tree, and then use 'git add/rm <file>' as
    appropriate to mark resolution and make a commit, or use 'git commit -a'.}
@~
  In most cases the way to continue is simple: open the conflicted file in your
  editor, look for the conflict markers and fix the code.  Then, as suggested
  above, use @cmd{git add @i{file}} which tells git that the file is resolved,
  and finally use @cmd{git commit} to commit the result.  (Note that using
  @cmd{git commit @i{file}} will not work, which is why the @cmd{git-ci} script
  avoids adding a @path{.} if the tree requires resolving a merge.)  I'll
  simulate the editing part with echos, and then mark it resolved:
  @pre{$ echo "#lang racket"                   > foo
       $ echo "(define (foo [x 0]) ; int->int" >> foo
       $ echo "  (* x x))"                     >> foo
       $ git add foo                 # ← tell git that it's resolved}
  And now the last step is to run @cmd{git commit}, which will start your
  editor to edit the log message — it will be populated by text that indicates
  the merge and the file that had conflicts, which you can commit as is, or add
  some text regarding the way it was resolved.
@~
  At this point (or before we started working on resolving the conflict), we
  can get back to the original state using @cmd{reset}:
  @pre{$ git reset --hard
       HEAD is now at 21a78df document the type of foo}
@~
  This kind of reset is generally useful if you had some problematic conflict
  to resolve and you want to back up completely and re-try.  But now that we've
  at the start, we will see what happens when we try to rebase with the
  conflict instead:
  @pre{
    $ git rebase origin
    First, rewinding head to replay your work on top of it...
    Applying: document the type of foo
    @i{...}
    CONFLICT (content): Merge conflict in foo
    Failed to merge in the changes.
    @i{...}
    When you have resolved this problem run "git rebase --continue".
    If you would prefer to skip this patch, instead run "git rebase --skip".
    To restore the original branch and stop rebasing run "git rebase --abort".}
@~
  Obviously, we get a different message (note that @cmd{git status} will now
  tell you that you're not currently on any branch — a result of being in the
  middle of a rebase).  The process that follows is very similar to the merge
  case: edit the conflict away, then @cmd{git add} the file.  There are two
  differences: (1) after you @cmd{git add} the resolved files, you should use
  @cmd{git rebase --continue} instead of committing[*]; (2) if you want to
  abort the merge, use @cmd{git rebase --abort} instead of using reset.
  @small{([*] If you did commit, then it means that you wrote a new log message
    for the replayed commit, and you can just as well use the @cmd{--skip} flag
    so rebasing continues with the rest, or you can use @cmd{reset} to undo
    your commit and let rebase do it for you.)}
@~
  When you're in a conflicted state, there are a few git tools that help you in
  the resolution work.  The first useful utility is @cmd{git diff}: when
  there's a conflict, all files that were automatically merged are already
  going to be in your staging area, and parts of conflicted files that could be
  merged merged will be there too.  This leaves only the conflict regions in
  your working directory, which means that @cmd{git diff} will show you only
  the conflicts (since by default it shows differences between the working
  directory and the staging area).  Also, the diff output itself is not a
  standard one.  At the current point of conflict during the rebase that we
  started, this is what we'll see:
  @pre|{$ git diff
        diff --cc foo
        index b81de80,86a4c54..0000000
        --- a/foo
        +++ b/foo
        @@@ -1,3 -1,3 +1,7 @@@
          #lang racket
        ++<<<<<<< HEAD
         +(define (foo [x 0])
        ++=======
        + (define (foo x) ; int->int
        ++>>>>>>> document the type of foo
            (* x x))}|
@~
  The diff header uses @cmd{--cc} which indicates git's “combined diff format”,
  used to represent merge commits (any commit with more than one parent).  The
  next line has the two SHA1s of the two files that are merged.  The diff
  itself starts with three @cmd|{@}|s, and instead of a single indicator
  character (@cmd{+}, @cmd{-}, or @cmd{ }), there are two — indicating a
  three-way diff between the two versions and their common ancestor version.
  In the above you can see that the line with the optional argument is coming
  from @cmd{HEAD}, and the type-annotated one is coming from its commit.  You
  might notice that this look backwards, since we're in the repository where we
  committed the type annotation to the HEAD — but we're now rebasing, which
  means that we start from the remote branch and merge our local changes into
  it, essentially making the rebase perform merges in the other way than plain
  merges.  The conflict markers themselves are marked as new in both versions,
  and the labels that follow them depend on available information (in a
  @cmd{merge}, we would see @cmd{HEAD} and @cmd{origin}).
@~
  During a conflict resolution, the staging area actually holds three versions
  of each file: the common ancestor, our version, and the merged version.
  These things are called “file stages”, and they can be accessed using a
  special syntax:
  @pre{$ git show :1:foo     # the common ancestor of both versions
       $ git show :2:foo     # our version (optional argument)
       $ git show :3:foo     # merged version (type-annotated)}
  (Again, remember that this is a rebase, so the last two are swapped.)  You
  can also checkout one of these versions using @cmd{git checkout foo}, giving
  it an @cmd{--ours} or @cmd{--theirs} flag to specify which version you want
  to use; and you can use @cmd{git diff} to compare against them.  For example,
  we resolve the file (as above) and then try the different diffs (before we
  mark it as resolved) — these examples only show the changed lines from each
  of the diffs:
  @pre{$ echo "#lang racket"                   > foo
       $ echo "(define (foo [x 0]) ; int->int" >> foo
       $ echo "  (* x x))"                     >> foo
       $ git diff -1 foo               # can also use --base
       -(define (foo x)                # original version
       +(define (foo [x 0]) ; int->int # new version
       $ git diff -2 foo               # can also use --ours
       -(define (foo [x 0])
       +(define (foo [x 0]) ; int->int
       $ git diff -3 foo               # can also use --theirs
       -(define (foo x) ; int->int
       +(define (foo [x 0]) ; int->int}
@~
  Finally, @cmd{git log} and @cmd{gitk} accept a @cmd{--merge} flag which shows
  commits relevant to a merge.  With @cmd{git log} the @cmd{--left-right} flag
  is useful here, since you'll see which side the relevant commits are on.
  (But this works only in @cmd{git merge}, not in rebasing.)
@~
  Again, when you're happy with the resolution, you @cmd{git add} the file, and
  because we're doing a @cmd{rebase} rather than a @cmd{merge}, use use it to
  continue:
  @pre{$ git add foo
       $ git rebase --continue
       Applying: document the type of foo
       $ git log --graph --all --oneline -4
       @i{...linear history...}}
  Note that @cmd{git rebase --continue} did the commit of the resolved content
  for you, and it used the previous commit message you've written.  This is a
  good rule-of-thumb for deciding whether you should rebase or merge: if the
  commit message are still fine as a description of the modifications, then a
  rebase is fine; otherwise you might want to @cmd{merge} instead.}

@subsection{Copying/renaming files}
@p*{
  Git is, by design, tracking snapshots of the complete repository tree.
  Specifically, it does @em{not} keep explicit track of file/directory copies
  and renames.  Instead, it provides ways to infer such changes in the
  repository based on the content.  As a result of this, there are almost no
  git commands that deal with file movements:
  @ul*{
  @~ There is no @cmd{git copy} command: you just copy the file and add the new
     one as usual.
  @~ There @em{is} a @cmd{git rm} command, but its purpose is mostly to remove
     a file from the staging area.  You could also just remove the file outside
     of git, and then use either @cmd{git commit @i{removed-file}} or
     @cmd{git commit @i{containing-directory}} to remove it (or using the above
     script — @cmd{git ci} in the same directory).  @cmd{git rm} will delete
     the file from the staging area so you can do a plain @cmd{git commit}
     without naming any paths.
  @~ For the same reason, there is a @cmd{git mv} command — it uses
     @cmd{git rm} as above to update the staging area, and if you're fine with
     ignoring it, then you can just rename the file outside of git, and
     @cmd{git add} the new version — but as we will soon see, it's really best
     to use @cmd{git mv} to avoid the possible confusion if you want the file's
     history to be visible.}
@~
  To try things out, let's properly name the @path{foo} library:
  @pre{$ mv foo foo.rkt
       $ git st
        D foo
       ?? foo.rkt}
  As you can see, we forgot to @cmd{git add} the new file, so if we commit now
  we'll only be committing the deletion.  An important thing to note here is
  that when git infers file copying and renaming, it does so only when the
  operations appear in a @em{single} commit.  So if we commit this change and
  later commit a new version with the new file will make it lose connection to
  its history.  As long as you didn't push the new commits out, you can still
  fix it: simply use @cmd{git rebase --interactive}, and squash the file
  addition together with the deletion.  But let's start over and do the rename
  the easy way:
  @pre{$ rm foo.rkt
       $ git reset --hard
       $ git mv foo foo.rkt
       $ git ci -m "properly name the foo library"}
  to see this commit, we can use @cmd{git show} (which can show arbitrary
  objects, but with no arguments it shows the @cmd{HEAD}).  @cmd{git diff} can
  also be used to show only the diff part — using the @cmd{HEAD^!} syntax that
  roughly means the range from the previous HEAD to the current one:
  @pre{$ git show
       @i{...log message...}
       @i{...addition+deletion...}
       $ git diff HEAD^!
       @i{...addition+deletion...}
       $ git diff --stat HEAD^!      # shows an overview of the changes
        foo     |    3 ---
        foo.rkt |    3 +++
        2 files changed, 3 insertions(+), 3 deletions(-)
       $ git log --oneline foo.rkt
       599b3b6 properly name the foo library}
  All of these show the two operations as disconnected, and the log doesn't
  show any of the prior history.  The thing is that you need to ask git to look
  for file operations, and the @cmd{-M} and @cmd{-C} flags do that.  In
  addition, @cmd{git log} needs a @cmd{--follow} flag to make it follow history
  beyond renames (but note that it can do that only when given a single file
  path).  For example:
  @pre{$ git diff -M --stat HEAD^!
        foo => foo.rkt |    0
        1 files changed, 0 insertions(+), 0 deletions(-)
       $ git log --oneline --follow foo.rkt
       599b3b6 properly name the foo library
       0fb8291 document the type of foo
       5035c9a add a default value
       @i{...}}
  In this case the rename was a trivial one as were no other changes.  This
  makes it especially easy to find renames since the SHA1 of the file would be
  the same.  But git considers such operations as renames as long as they're
  “similar enough” — for example, if you just rename some files and change some
  @cmd{require}s as a result, it will be detected as renames.  (The usual claim
  is that when the content is not similar enough, you can just as well claim
  that the file is new.)  If you think that you might be doing too many changes
  to some files, and you want to preserve the connection, you can do only the
  rename in one commit, and then the modifications in the next.
@~
  An added benefit of this mode of work is that @cmd{git blame} can find lines
  in files that were copied from other files, and deal naturally with a file
  that is split into two files etc.  Like @cmd{log} and @cmd{diff}, it needs
  some flags to do the extra work (see @cmd{-M} and @cmd{-C}).}

@subsection{Managing branches}
@p*{
  As seen in various places above, a branch in git is basically just a SHA1
  pointer to a commit (and therefore to the whole line of commits in its
  development line), with a naming hierarchy that follows some conventions
  (@path{/}-separated, @cmd{master} as the main one, @cmd{remotes} prefix for
  remote branches, @cmd{origin} for the default remote server name, etc).  You
  can see all of this in the toplevel @path{.git} meta directory — there is a
  @path{HEAD} file which represents the head, its content will be a line that
  looks like @cmd{ref: refs/heads/master}, and there will be a
  @cmd{refs/heads/master} file with a content that is the actual SHA1.  There
  are, of course, various other bits of meta-data, so it's not a good idea to
  change such files directly (for example, when there are many names git will
  create a “packed” reference file with many references for efficiency) — but
  overall this is the basic idea.
@~
  Branches come in two main kinds: local branches and remote ones, with remote
  branches having a name that begins with @cmd{remotes/origin/}.  (Later we'll
  see how to add new remote repositories — remote branches from these will have
  names that start with @cmd{remotes/@i{remote-name}/} instead.)  The
  difference between the two is that a remote branch is a way to mirror a
  branch on a remote repository — it is not intended for local work.  For
  example, if you try to check out a remote branch, git will check out a
  “detached head” (details on this below).  If you do that, you'll see that the
  @path{HEAD} file will have an explicit SHA1 rather than the usual
  @cmd{ref: @i{branch-name}}.
@~
  The @cmd{git branch} command is the main way to manage branches.  With no
  flags, it will just print out the list of local branches, marking the current
  branch with a @cmd{*}.  You can add flags to show remote branches instead
  (@cmd{-r}), both kinds (@cmd{-a}), and also to list more information on the
  branches (@cmd{-v}):
  @pre{$ git branch
       * master
       $ git branch -r
         origin/HEAD -> origin/master # (this one is symbolic too)
         origin/master
       $ git branch -av
       * master                599b3b6 [ahead 2] properly name the foo library
         remotes/origin/HEAD   -> origin/master
         remotes/origin/master 5035c9a add a default value}
@~
  When given a single name argument, a branch by that name will be created, and
  it will point to where the @cmd{HEAD} currently points to; a second argument
  can be a name of an existing branch (or any commit) that the new branch will
  start at.  In addition to creating branches starting from the current head,
  this can be useful in creating branches that start from elsewhere, even from
  a “detached head”.  For example, say that in our current repository we want
  to try out some work based on the state of things before the last commit
  (which renamed the @path{foo} file).  We can check out @cmd{HEAD^} (which
  will lead to a detached HEAD), and then create a branch for it:
  @pre{$ git checkout HEAD^
       @i{...}
       You are in 'detached HEAD' state.
       @i{...}
       HEAD is now at 0fb8291... document the type of foo
       $ cat .git/HEAD
       0fb8291...                     # doesn't point to a branch
       $ git branch
       * (no branch)                  # you can see it here too
         master
       $ git status
       # Not currently on any branch. # and here
       $ git branch pre-rename        # create a branch here
       $ git branch
       * (no branch)                  # we're still detached
         master
         pre-rename
       $ git checkout pre-rename
       Switched to branch 'pre-rename'}
  As you can see, creating a branch doesn't check it out — even when the new
  branch is exactly where we already are.  The difference is related to the
  nature of @cmd{HEAD}: it is usually an indirect reference to a branch name,
  and when a commit is made, the branch that @cmd{HEAD} points to is updated.
  But when we are using a detached HEAD, it points directly at a SHA1 —
  committing in this state will work, and the HEAD will point at the newly made
  commit — but there will be no branch that will be updated, so if you checkout
  a different branch (or a different commit) now, the commits you made are
  “lost”.
@~
  The main reason that such commits will be lost is that git branches don't
  live inside the repository store — and dealing with branches is not something
  that gets recorded as part of the history.  To make things safer, git
  maintains something that is known as the “reflog”, which keeps track of where
  your branches have been — those are kept for a while (usually around a
  month), which means that you can easily go back to a previous commit if it
  seems that you lost one (eg, as a result of committing on a detached HEAD).
  (You can see these files in the @path{.git/logs} directory.)
@~
  Since creating a new branch and checking it out is a common combination, the
  @cmd{checkout} command can create a branch before checking it out.  Use the
  @cmd{-b} flag for this:
  @pre{$ git checkout -b also-pre-rename
       Switched to a new branch 'also-pre-rename'
       $ git checkout -b post-rename master
       Switched to a new branch 'post-rename'
       $ ls
       bar  blah  foo.rkt
       $ echo "one more line" >> foo.rkt
       $ git ci -m "one more line"}
@~
  Finally, you use the @cmd{-d} flag to delete branches.
  @pre{
    $ git branch -d post-rename   # won't allow it
    error: Cannot delete the branch 'post-rename' which you are currently on.
    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 2 commits.
    $ git branch -d post-rename
    error: The branch 'post-rename' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D post-rename'.}
  As you can see, git refuses to delete a branch that has unmerged work, since
  this can lead to losing that unmerged work — so you need to use @cmd{-D} for
  that.  In addition, you usually don't delete remote branches, when you do,
  you need to use the @cmd{-r} flag too.}

@subsection{Using branches}
@p*{
  Since git branches are so light weight, they fit any kind of parallel work
  you need to do on several different topics.  A result of that is that it is
  possible to start a new branches for any work you'd want to do — and this is
  common enough that there's a name for such branches, they're called “topic
  branches”.  Such branches are created from the master branch (usually) and
  worked on in parallel.  At any point where you want to work on something new,
  you would create a new branch for it and switch to it (committing any work
  you might have on your current branch before you do so):
  @pre{$ git checkout -b improve-bar master # switch to a fresh topic branch
       Switched to a new branch 'improve-bar'
       $ echo "even more bar" >> bar        # work there
       $ git ci -m "improved bar"           # save that work
       $ git checkout post-rename           # go back to where we were}
@~
  If you need to commit changes before you create the new branch, you shouldn't
  have any problems doing so — because you can change where a branch points to,
  you can just commit whatever you have and then get back to it:
  @pre{
    $ echo "another line" >> foo.rkt
    # at this point you remember that you need to do something else in the
    # `improve-bar' line of work.
    $ git ci -m "checkpoint"
    $ git checkout improve-bar
    # ...work...
    $ git checkout post-rename
    $ git log --oneline -2
    e9a4fcd checkpoint            # this is our temporary checkpoint commit
    d92fb0a one more line
    $ git reset HEAD^             # undo it
    Unstaged changes after reset:
    M       foo.rkt               # git tells us that this is now uncommitted
    $ git st
     M foo.rkt                    # ... as does `status'
    $ git log --oneline -2
    d92fb0a one more line         # the temporary commit is gone
    599b3b6 properly name the foo library}
@~
  You can even decide on some convention to use in some cases, then create new
  git commands as scripts that will do the work for you.  In this case, you
  could write a command that will do a “checkpoint” commit if needed, switch to
  another branch, and if the first commit there has only @cmd{checkpoint} as
  its log message, undo it as above.  There are several git convenience
  commands that started out this way — in this case, checkout the @cmd{git
  stash} command which allows you to save the current work by pushing it on a
  “work in progress” stack, and later pop it back out (possibly on a different
  branch).
@~
  Earlier we've seen how to merge or rebase your master branch from the remote
  master branch, but the full story is that you can merge and rebase @em{any}
  two branches.  This makes branches very flexible: you can create a branch A
  from an existing branch B, eventually merging/rebasing it back into A, or
  directly into master and dump A.  At any point you can run @cmd{gitk --all}
  to see where things stand — in our current repository, this shows us that
  there are redundant @cmd{pre-rename} and @cmd{also-pre-rename} branches, that
  out @cmd{master} branch is two commits ahead of the remote one, and that we
  have @cmd{improve-bar} and @cmd{post-rename} branches with 1 and 2 commits
  over our @cmd{master} branch.  If we're done with these two branches, we can
  now merge/rebase them to our @cmd{master}, or merge/rebase one to the other
  and the result to @cmd{master}, and then push everything out.
@~
  To make working with branches even easier, git has a notion of an “upstream
  branch” — this is a per-branch setting that tells git which branch the
  current one is based on.  By default, any branch that is created with a
  remote branch as its starting point will have that remote branch set as its
  upstream.  We've seen how git treats that information in various places so
  far: @cmd{git status} and @cmd{git branch -v} both use it, and using a second
  @cmd{-v} with the latter shows also the upstream branch:
  @pre{
    $ git reset --hard            # dump the above uncommitted change
    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 2 commits.
    $ git status
    # On branch master
    # Your branch is ahead of 'origin/master' by 2 commits.
    @i{...}
    $ git branch -v
    @i{...}
    * master    599b3b6 [ahead 2] properly name the foo library
    @i{...}
    $ git branch -vv
    * master    599b3b6 [origin/master: ahead 2] properly name the foo library}
@~
  In addition to that, we've seen the @cmd|{@{upstream}}| and @cmd|{@{u}}|
  notation that refers to the upstream branch, making it convenient to further
  examine pending changes that weren't incorporated upstream:
  @pre|{$ git log --oneline @{upstream}..
        599b3b6 properly name the foo library
        0fb8291 document the type of foo}|
@~
  And finally, @cmd{git pull} and @cmd{git push} know where to pull from and
  push to based on this setting.  Overall, this is a very useful feature to
  have when you have many branches, therefore it is possible to use it between
  local branches too.  There are two ways to do this: when a branch is created
  with either @cmd{git branch B} or @cmd{git checkout -b B}, you can use the
  @cmd{--track} flag to set up tracking to the initial branch it's based on.
  @pre{$ git branch -t b1 master
       Branch b1 set up to track local branch master.
       $ git checkout -tb b2 master
       Branch b2 set up to track local branch master.
       Switched to a new branch 'b2'}
  (Note: if you're using @cmd{checkout}, then the @cmd{--track} flag should
  precede the @cmd{-b} flag, as done above.)  If a branch already exists, you
  can use @cmd{git branch --set-upstream} to set the upstream information.
  @pre{$ git branch --set-upstream post-rename
       Branch post-rename set up to track local branch b2.
       $ git branch --set-upstream improve-bar master
       Branch improve-bar set up to track local branch master.}
  As seen here, if it is given just a branch name, the current branch is set as
  its upstream.  @cmd{git branch} can also change the upstream branch, for
  example, if the above tracking of @cmd{b2} was a mistake:
  @pre{$ git branch --set-upstream post-rename master
       Branch post-rename set up to track local branch master.}
  Either way, we can now see this information in the git commands that do so,
  as well as use @cmd|{@{upstream}}|:
  @pre|{$ git branch -vv
          b1          599b3b6 [master] properly name the foo library
        * b2          599b3b6 [master] properly name the foo library
          improve-bar e60c168 [master: ahead 1] improved bar
          master      599b3b6 [origin/master: ahead 2] properly name the foo @;
                                                       library
          post-rename d92fb0a [master: ahead 1] one more line
        $ git checkout improve-bar
        Switched to branch 'improve-bar'
        Your branch is ahead of 'master' by 1 commit.
        $ git log --oneline @{upstream}..
        e60c168 improved bar}|
@~
  In addition, we can use @cmd{git pull} to get changes on the upstream branch
  merged or rebased on the current one:
  @pre{$ git pull
       From .
        * branch            master     -> FETCH_HEAD
       Already up-to-date.}
  Nothing actually happened here, because the current branch
  (@cmd{improve-bar}) already contains all of the commits on the master branch.
  You can see that this is a local pull since git says @cmd{From .}, which
  stands for “our own repository”.  You can also do a @cmd{push} now, which
  will make the current additional commit (listed with @cmd|{@{upstream}..}|)
  appear on the @cmd{master} branch:
  @pre{$ git push
       To .
          599b3b6..e60c168  improve-bar -> master}
@~
  Since the @cmd{improve-bar} line of development is unrelated to the one in
  @cmd{post-rename}, it is now one commit behind the @cmd{master} branch, and
  cannot be pushed as is:
  @pre{
    $ git checkout post-rename
    Switched to branch 'post-rename'
    Your branch and 'master' have diverged,
    and have 1 and 1 different commit(s) each, respectively.
    $ git branch -vv
      improve-bar     e60c168 [master] improved bar
      master          e60c168 [origin/master: ahead 3] improved bar
    * post-rename     d92fb0a [master: ahead 1, behind 1] one more line
    $ git push
    To .
     ! [rejected]        post-rename -> master (non-fast-forward)
    error: failed to push some refs to '.'
    To prevent you from losing history, non-fast-forward updates were rejected
    @i{...}}
  Dealing with this is similar to dealing with updates on the remote server —
  for example, we can rebase the branch before pushing it:
  @pre{$ git pull --rebase
       From .
        * branch            master     -> FETCH_HEAD
       First, rewinding head to replay your work on top of it...
       Applying: one more line
       $ git push
       To .
          e60c168..7bdec0c  post-rename -> master}
@~
  When you use @cmd{git push} to push changes when you have no upstream branch
  set, or when you push to a different branch than the one set, you can use
  @cmd{--set-upstream} to make git remember the push target as the upstream.
  Therefore, an easy way to create a new branch that tracks a possibly new
  remote branch by the same name is:
  @pre{$ git checkout -b my-branch
       Switched to a new branch 'my-branch'
       $ git push origin my-branch --set-upstream
       To pltgit:eli/foo
        * [new branch]      my-branch -> my-branch
       Branch my-branch set up to track remote branch my-branch from origin.}
  And when you deal with remote branches this way, you might want to have a
  local branch that tracks a remote one with a different name.  To do this, you
  use a syntax for the branch to push that specifies the local branch to push
  and the remote one to push to:
  @pre{$ git push origin my-branch:different-branch --set-upstream
       To pltgit:eli/foo
        * [new branch]      my-branch -> different-branch
       Branch my-branch set up to track remote branch different-branch from @;
       origin.}
@~
  Finally, note that git stores the upstream information in the
  repository-local configuration file.  If we look at it now, we will see the
  various upstreams that we have set:
  @pre{$ cat .git/config
       @i{...}
       [remote "origin"]
               fetch = +refs/heads/*:refs/remotes/origin/*
               url = pltgit:eli/foo
       [branch "master"]
               remote = origin
               merge = refs/heads/master
       @i{...}}
  this is the upstream that was made by default when we first checked out our
  clone, together with the information of where the @cmd{origin} repository is.
  Following that are the ones we've setup later:
  @pre{@i{...}
       [branch "b1"]
               remote = .
               merge = refs/heads/master
       [branch "b2"]
               remote = .
               merge = refs/heads/master
       [branch "post-rename"]
               remote = .
               merge = refs/heads/master
       [branch "improve-bar"]
               remote = .
               merge = refs/heads/master
       [branch "my-branch"]
               remote = origin
               merge = refs/heads/different-branch}
@~
  Note that there are branches that track local branches (ones with a
  @cmd{remote = .} setting), and ones that track remote ones; and also note
  that the @cmd{my-branch} branch tracks a remote branch with a different name.
  Since the settings are stored as configurations, it is possible to inspect
  and change them using @cmd{git config}, or even edit the config file
  directly.
  @pre{$ git config branch.my-branch.remote
       origin
       $ git config branch.my-branch.merge
       refs/heads/different-branch}}

@subsection{Managing remotes}
@p*{
  The distributed nature of git means that you can interact with multiple
  remote repositories.  You could have work done with other people done
  locally, where people push/pull from each other's clones (possibly by sending
  around patches, as described below), and eventually when the changes are
  ready push them back to the main repository.  You can even have your
  repository track multiple unrelated remote repositories, essentially giving
  you branches that have @em{unrelated} histories.
@~
  By default, when you clone a remote repository git names it @cmd{origin} —
  and that name appears in many places, most notably in remote branch names.
  As seen in the above config, git remember where the origin repository is via
  a configuration:
  @pre{$ git config remote.origin.url
       pltgit:eli/foo
       $ git config remote.origin.fetch
       +refs/heads/*:refs/remotes/origin/*}
  The first one is the url of the remote repository, and the second one is
  which branches we want to get from it.  As with branches, you can use
  @cmd{git config} to change this information, or you can edit the file
  directly, but there is a command that does this more conveniently, keeping
  things consistent:
  @pre{$ git remote                  # lists all known remotes
       origin
       $ git remote -v               # remotes and push/pull specs
       origin  pltgit:eli/foo (fetch)
       origin  pltgit:eli/foo (push)
       $ git remote show origin      # see a detailed description
       * remote origin
         Fetch URL: pltgit:eli/foo
         Push  URL: pltgit:eli/foo
         HEAD branch: master
         Remote branches:
           different-branch tracked
           master           tracked
           my-branch        tracked
         Local branches configured for 'git pull':
           master    merges with remote master
           my-branch merges with remote different-branch
         Local refs configured for 'git push':
           master    pushes to master    (fast-forwardable)
           my-branch pushes to my-branch (up to date)}
@~
  The @cmd{git remote show} variant will actually query the remote repository
  for its state (using @cmd{git ls-remote}) by default, and tell you when a
  local branch that tracks a remote one is out-of-date.
@~
  There are a few more sub-verbs for the @cmd{git remote} command which you can
  see on the @man{git-remote} man page, the most important one is for adding a
  remote: @cmd{git remote add @i{short-name} @i{url}}.  This is especially
  convenient if you want to have a fork of the @cmd{plt} repository, with most
  interaction happening against it, but occasionally pull/push updates from/to
  the main repository.
@~
  Of course, remember that you don't need to add remotes to push and pull from
  them.  You could do the same by explicitly specifying a url for the
  repository you want to interact with.  For example, you could have
  repositories in different accounts on different machines, and synchronize
  your work between them by pushing and pulling directly from one repository to
  another.  (Reminder: if you do this, then you're likely to have “checkpoint
  commits” — when you're done with the work, you can do an interactive rebase,
  and squash these checkpoint changes back into logical commit.)  But if you do
  this often enough, you will likely find it more convenient to add a named
  remote.}

@subsection{Using private repositories}
@p*{
  A particularly useful use-case for adding a new remote is when you want to
  have private work done in your own fork of the @cmd{plt} repository.  Such a
  mode of work is not strictly necessary — you could just do your work in your
  repository in a long-lived branch, but there are certain cases where working
  with a private repository on the server might be more convenient.  For
  example, you might want to collaborate with someone else (that has access)
  via the server, or you might use a private fork of the @cmd{plt} repository
  as a central point for synchronizing work from clones on different
  filesystems as described at the end of the last section (the difference from
  that is that you basically use the PLT server as your synchronization point).
  Other than having the main repository reside on the PLT server, working with
  a private repository is not different than working with any other repository.
@~
  There are two facts that are worth reminding when you deal with a private
  repository.  First, remember that creating a private fork is cheap: creating
  a new git clone of a repository will use hard links to the repository store
  object, most of which will be contained in large packed files.  The cost in
  terms of space and time for creating a new clone is therefore minimal when
  done on the same filesystem — and using the gitolite @cmd{fork} command is
  doing just that.  Please use the @cmd{fork} command to create a private clone
  — gitolite has a feature where it creates any repository that you refer to
  (as long as it has a name that you're allowed to create — starts with your
  username); this means that you could clone the main @cmd{plt} repository and
  push from it into a private repository that doesn't exist: it will be
  created, but such a copy will not share storage with the main repository — it
  will require a new copy, and it will be slow to create.
@~
  The second thing to remember is that due to the nature of the git store, any
  object, including commits, is stored exactly once.  Since commits contain
  their parents, having a specific commit means that you have its complete
  history — therefore, pulling in any branch from any repository will always
  require getting only commits that you don't already have.  As a result,
  pulling and pushing to/from any repository will be efficient and move around
  only those commits that are missing on the other side.
@~
  You can choose one of two basic approaches to working with a private fork:
  you can have the public repository cloned but have branches pushed to your
  private one, or you can have your private fork cloned and occasionally push
  updates to the public one.  A way to use these two approaches are described
  and explained now.  These examples use the @cmd{play} repository as an
  example (which you are encouraged to experiment with).  Note that you can use
  a hybrid approach: you can think about a repository as a container for commit
  histories, pushing and pulling from any other repository, including the copy
  you're working with, the main @cmd{plt} repository, or a private fork (your
  own or another).  Note also that since forks are cheap, you can keep several
  of them around, for example, you can have a fork for each long-lived branch —
  it's up to you to settle on a layout that is convenient for your work.}
@h3{Using a clone of the public repository, pushing branches to your private
    one:}
@ol*{
@~ Setting up:
   @ul*{@~ Create a fork:
           @pre{ssh pltgit fork play $user/play}
        @~ Get a local copy of the main repository:
           @pre{git clone pltgit:play}
           (or continue working in an existing one)
        @~ Set up a convenient name for your private repository:
           @pre{cd play
                git remote add my-fork pltgit:$user/play}}
@~ To start working on a private branch, create one, and push it to your
   private repository:
   @pre{git checkout -b my-branch}
   Then use @cmd{push} to create this branch in your private repository, with
   @cmd{--set-upstream} so git will remember this setting:
   @pre{git push --set-upstream my-fork my-branch}
   You can also have your branch named differently in your fork, for example:
   @pre{git push --set-upstream my-fork my-branch:master}
   will save your branch as the @cmd{master} branch in your fork.  This might
   be convenient if you want to clone your private repository elsewhere and
   work only on this branch.
@~ You can now work as usual in your repository, pushing/pulling changes
   to/from the master branch will go to the public repository, and doing so
   from @cmd{my-branch} will go to your private fork.  You can merge changes on
   the @cmd{master} branch to your private one, or rebase your branch onto it.
   However, note that the server will not allow pushing a rebased history to
   your clone.  (More details at the end of this section.)  You can bypass that
   by pushing to a new branch while keeping your local branch name:
   @pre{git push --set-upstream my-fork my-branch:my-branch-2}
@~ When you're done merge your branch (possibly rebasing it first) to the
   master branch, and push as usual.
@~ If you want to delete branches on your fork (either because you pushed a
   rebased version under a new name, or because you're done with that line of
   work), use
   @pre{git push my-fork :my-branch}
   Using an empty branch name for the local branch that you push is the way to
   delete remote branches.  (As with local branches, this might lead to losing
   commits, so be careful.  As always, git has a few safety mechanisms in
   place, so even if did this by mistake, it is very likely recoverable.)}
@h3{Using a clone of your private repository, pushing changes to the public
    one:}
@ol*{
@~ Setting up:
   @ul*{@~ Create a fork:
           @pre{ssh pltgit fork play $user/play}
        @~ Get a local copy:
           @pre{git clone pltgit:$user/play}
        @~ Set up a convenient name for the main repository:
           @pre{cd play
                git remote add -t master main pltgit:play}
           (@cmd{-t master} tells git to have only the @cmd{master} branch
           retrieved.)}
@~ Now you can work in this repository as usual — edit, commit, push, etc.
@~ To push changes to the main repository, first make sure that you're on the
   branch with the changes that you want to push, and then:
   @ul*{@~ Get the recent tree from the main repository
           @pre{git fetch main}
        @~ Rebase or merge your changes with this:
           @pre{git rebase main/master}
           or
           @pre{git merge main/master}
        @~ Push the changes back:
           @pre{git push main}}
   Note that rebasing your branch on top of main/master means that it will be
   rewritten, which means that you will not be able to push your branch back to
   your clone.  This is because rewritten histories are currently forbidden by
   the configuration, but this will probably change in the future.  Still, even
   if the server would allow pushing a rebased history it (you will need to use
   @cmd{-f} to force such a push), you would need to deal with the rebased
   branch in other clones you might have.  Because of this, a rebase is fine if
   you're done with the work that you're pushing, otherwise, a merge is more
   convenient.}

@section{Collaborating with others}
@p*{
  Git makes it very easy to collaborate with anyone, anywhere.  You should
  think about repositories as being parts a network which can be synced in any
  topology that is convenient for you.  In the case of the PLT repository, the
  main repository on the git server is the central point where the official
  repository lives, and people who can push are directly syncing content into
  it.  People who cannot push directly do so through someone who can, by
  sending out patches or “pull requests”.  The same applies for any repository,
  of course, including private repositories, even ones that you maintain
  yourself independently of the PLT server.
@~
  In the case of a patch-based workflow, the two sides that are involved are
  the patch author, and the receiver that integrates it into his/her own clone
  (and from there it goes to the main repository as usual).  The work that each
  side does is described in the next two subsections.
@~
  Following that there is a description for making your repository public,
  which you will need if you're working on a private repository, but it is also
  useful for your collaborator to do so you can use a pull-request workflow.
  In this mode there is no need to email patches; instead, both people make
  their repositories readable to each other, and when some work is ready on one
  person's repository, the other pulls the commits.  This is described in the
  last subsection.}

@subsection{Patch-based workflow@br
            — instructions for the patch sender side}
@h4{Executive summary:}
@ol*{@~ Work in a @cmd{plt} repository clone (possibly in your own branch)
     @~ @npre{$ git send-email origin/master}
     @~ You're done — thanks!
     @~ When the patch is applied, you will get the changes through
        @cmd{origin/master}, so if you worked on your master branch, make sure
        to use @cmd{git pull --rebase} which will notice that your changes were
        applied; if you worked on a branch, then you can now delete it (the
        commit objects will be different from the ones you've made).}
@h4{Longer version:}
@ol*{
@~ Work & commit as usual.  In general, it is a good idea to use
   @cmd{Signed-off-by: @i{Your Name} <@i|{your@email}|>} in commit messages,
   which is a conventional way to declare that you agree for your work to be
   released as part of the PLT project, under the terms of the LGPL.
   @cmd{git commit} will add that for you if you use the @cmd{-s} flag.  You
   can also make git do this later, when you send the patches out.
@~ Make sure that you're working with a relatively recent clone, and that
   you're on the branch where you did your work.  In most cases, this would be
   the master branch, but you can do your work in your own branch too, of
   course.
@~ @p*{
     Verify that your commits are all in your history.  You can see the commits
     that you have over the @cmd{plt} history with
     @pre{git log --oneline origin/master..}
     these are the commits that you're going to send over now.  (You can use
     the usual git toolset to tweak them further, or specify only some commits,
     etc.)
   @~
     A relevant point to consider here is that git takes the first paragraph of
     each commit message as a subject line.  When sending out a patch, this is
     made concrete by actually using it as the emails's subject — so it is a
     good idea to make sure that this log looks fine, since the @cmd{--oneline}
     option will make it show those subjects.
   @~
     Obviously, you should also make sure that the commits have clear
     descriptions of your work.  People who in the core group often have some
     general context that they are aware of, so some commit messages can be
     cryptic or even worse (eg, you might find @cmd{.} as a commit message) —
     don't mimic this...  As a more occasional contributor, you should explain
     your work in more details.  (There's no policy on commit messages, but you
     do need to go through some person on the team.)}
@~ @p*{
     At this point you should decide how to send your patches.  Emailing them
     is be the most convenient way to do this — to do this, you would use the
     @cmd{send-email} command:
     @pre{git send-email origin/master..}
     or if you send only some commits, use a different specification.  To make
     things even easier, a single commit specification is considered as the
     starting point and all of the following commits (up to your branch's tip)
     will be included in the emails (in contrast to other git commands like
     @cmd{log}, where a single commit name is considered as the set of commits
     leading up to it) — so you can do this:
     @pre{git send-email origin/master}
     This will ask you a bunch of questions — it's easy to answer but you can
     also specify them as command-line options.  if you intend to do this
     frequently it might be a good idea to make it easier with some settings in
     your global .gitconfig file.  For example, I have these settings:
     @pre|{[sendemail]
             from = Eli Barzilay <eli@barzilay.org>
             bcc = eli@barzilay.org
             suppresscc = self}|
     and you can see more in the @man{git-config} and @man{git-send-email} man
     pages.  The address to send the patches to is also configurable — you can
     use something like
     @pre{to = dev@at-racket}
     or
     @pre{to = someone@at-racket}
     depending on who you send your patches to — but this is better done as a
     repository-local configuration option (or just use the @cmd{--to} flag).
   @~
     You can add a @cmd{-s} flag to the command, to make git add
     @cmd{Signed-off-by} lines to commit messages.  (See above for what this
     means.)
   @~
     If you want to send the files in some other way (eg, send them all
     packaged in an archive as attachment[*]), then just use @cmd{format-patch}
     instead of @cmd{send-email} — git will create a number of patch files in
     your current directory, which will be named @path{NNNN-text.patch} where
     the text is made out of the subject lines of the commit messages (the
     first line).  You can even run
     @pre{git format-patch origin/master --stdout > my-patch}
     to concatenate them all and send the resulting file over.
   @~
     @small{[*] Note that doing this means that it is not as easy to read your
       patch, so avoid doing this if you want to make it easier to read and
       accept it.  On the other hand, if you're working with someone specific,
       they might prefer attachments (for example, it's easier to save the
       attached file from gmail).}}
@~ Once the commits have been pushed to the main repository, you would get
   them when you pull to update.  The commits will now be different objects
   than the ones you have — since the information changed (at least the
   committer information will be different, the log message might have been
   edited, etc).  If you made your commits on your master branch which is set
   to track the @cmd{plt} master branch (the usual setup), then make sure that
   you run @cmd{git pull --rebase} to update — this will identify the commits
   as already included and will not include them in the rebased master.  But if
   you made your commits in a private branch, and assuming that you didn't do
   any additional work there, then you can now just delete that branch.  (If
   you did do more work there, then you should rebase it, to avoid resending
   the same patches again.)}

@subsection{Patch-based workflow@br
            — instructions for the patch receiver side}
@p*{
  Accepting patches that were sent via email (on any other way), is also
  simple.  The command to do this is @cmd{git am}, which expects an argument
  that is a mailbox file holding the patch emails, or you could run it and pipe
  a patch email into it.}
@h4{Executive summary:}
@ol*{@~ @npre{$ git checkout master}
     @~ Save (unmodified) patch emails into a mail folder file.
     @~ @npre{$ git am -3 @i{the-mail-folder}}
     @~ Push the changes up to the server}
@h4{Longer version:}
@ol*{
@~ While you will not be author of the commits, you will be their committed, so
   you should of course be aware of the changes, and be willing to maintain the
   new code and other work that is implied.  So the first step that you should
   do is review the patch and make sure that you are willing to accept
   responsibility for it.
@~ Save the patch emails to a mail folder (usually a file).  You must take care
   to save the emails @em{as is}, including the date, author, and subject
   headers, and avoiding text that could have been butchered by your email
   client.  For example, if you're using gmail, then use the “show original”
   option to view the raw email text, and save that to a file (even in this
   format gmail will have a first line with a bunch of spaces — it's best to
   remove that).  Otherwise, gmail does things like wrap lines, replaces spaces
   by non-breaking spaces, or remove spaces.  Alternatively, extract patch
   files from an archive if that's what you received, or save a single
   attachment file etc.
@~ In your repository clone, make sure that you're on the branch that you want
   to integrate the changes into.  You could do this in your master branch, or
   in a new topic branch (especially if there is more than one patch).
@~ Run @cmd{git am -3 mail-folder} (@cmd{am} stands for “apply-mail”) with the
   mail folder that you've created above.  It will apply the patches and commit
   them one by one.  Like @cmd{git rebase}, if there are conflicts the process
   will stop so you can resolve it — and then run the @cmd{am} command with
   @cmd{--continue}, or @cmd{--skip} this commit and continue with the rest, or
   @cmd{--abort} to go back to the start.  The @cmd{-3} flags tells git that if
   a conflicting patch comes from the above @cmd{format-patch}, and it
   specifies files that we have, then try a 3-way merge — this will make things
   generally better (and it can identify more patches that were applied,
   instead of showing them as conflicts).
   @br
   You can also use an @cmd{-i} flag to the command to get an interactive
   version — for each commit it will ask you what to do with it, and let you
   edit the log message.
@~ Finally push the commits as usual.}

@subsection{Making a private repository publicly available}
@p*{
  If you're working with “outside people” (people with no accounts on the PLT
  server, and no direct file-system access etc) on a private repository, you
  will need to find some way to make your repository available for cloning.  An
  easy way to do so is to put it on a filesystem that those people can access —
  eg, if you're all in the same department.  Another easy way to make a
  repository available is to find a hosting service like github and others —
  there are many options here, some are free but limited, and some cost money;
  if you prefer this easy solution, keep in mind that you can pay for the
  duration of the collaboration and at the end you can simply keep your
  repository clone to yourself (eg, if you're working on a paper then there's
  no need to pay once all work is done).
@~
  But if you want to do it yourself, the quickest and most convenient way to
  make a repository public is to put it in a directory that is available on the
  web.  Such repositories can be cloned directly from the URL the repository is
  available at — there's no need to setup a server in a special way, and no
  need to run cgi scripts.}
@h4{Executive summary:}
@ol*{@~ @npre{$ git clone --bare @i{your-repo} ~/public_html/@i{repo}.git}
     @~ @npre{$ cd ~/public_html/@i{repo}.git/hooks;
              mv post-update.sample post-update;
              chmod +x post-update}
     @~ @npre{$ git remote add public ~/public_html/@i{repo}.git}
     @~ Tell people to clone from @cmd{http://some.where/~you/@i{repo}.git}
     @~ Work, apply email patches, and: @cmd{git push public}}
@h4{Longer version:}
@ol*{
@~ Make a “bare” repository — this is a repository that has no working
   directory:
   @pre{git clone --bare @i{your-repo} @i{repo}.git}
   This will create a @path{@i{repo}.git} directory holding the bare
   repository.  You should use some path in a directory where you have web
   pages published.
@~ The URL where the directory is found at is what other people should use when
   cloning.
@~ You can now push to this repository, and other people will see it too.  To
   make things easier, you can set a remote name for this repository, so it's
   easy for you to push changes to it.
   @pre{git remote add public ~/public_html/@i{repo}.git}
   And now you can use @cmd{git push public}.  (You can also pull from it, but
   since you're going to be the only one who pushes into it, that will not be
   necessary.)
@~ One thing to be aware of is that while a repository can be published through
   HTTP this way, git considers that a “dumb protocol” (because there is no
   proper interaction between the two sides).  To still make cloning possible,
   you will need to maintain some meta-files that hold entry points to the
   objects in your repository — to get this, run:
   @pre{git update-server-info}
   You need to run this after each update to the repository — and to automate
   this you can have a hook do it for you.  In the bare repository you will
   find a @path{hooks} directory with a file called @path{post-update.sample} —
   simply rename this file to @path{post-update}, and make it executable with
   @cmd{chmod +x post-update}.  From now on every push to this repository will
   run the hook and keep the meta files updated.}

@subsection{Pull-request workflow}
@p*{
  A possibly easier way for people to contribute work is to make their
  repositories available somehow.  In the case of a private repository, the two
  sides can be in a shared file system, with read permissions for each other;
  or achieved as described in the previous subsection.  In the case of
  contributing to the @cmd{plt} repository, the contributor can maintain a
  public fork of the @cmd{plt} repository (eg, by forking the @cmd{plt} github
  mirror at @selflink{https://github.com/plt/racket} directly on github).
@~
  In this workflow there is no need to mail patches — instead, the receiver
  simply pulls them directly from the sender's repository.  For example,
  someone tells you that they have some new commits in a @cmd{foo} branch of
  their repository.  Since this is a repository that you can access, and since
  it shares history with yours, you can just pull that branch in, for example:
  @pre{git checkout -b someones-work
       git pull @i{someones-repository-url}}
  Note that the @cmd{pull} will merge the changes, creating a merge
  commit if your @cmd{master} branch cannot be fast-forwarded.  To avoid
  this, you can use @cmd{fetch} instead:
  @pre{git checkout -b someones-work
       git fetch @i{someones-repository-url}}
  Either way, this fetches the remote repository's HEAD.  You can create the
  branch in a single fetch command by specifying the remote branch name, and
  the local branch to fetch into, for example:
  @pre{git fetch @i{someones-repository-url} master:someone}
@~
  If you expect to do this often (eg, you're going to suggest fixes for the
  work and get new work in), then you can add a @cmd{someone} remote to be used
  more conveniently:
  @pre{git remote add someone @i{someones-repository-url}
       git fetch someone
       git checkout -b some-branch someone/some-branch}
  possibly using -t to make the branch track the remote one:
  @pre{git checkout -tb some-branch someone/some-branch}
  Note that there is no need to create a branch before the @cmd{fetch}, since
  it will be fetched to a @cmd{remotes/someone/master} branch.
@~
  Once you pulled in the branch, you can inspect the changes, merge them,
  rebase them, etc.  The important point here is that you have a copy of the
  contributed line of work, which you can use with the usual git toolset.
@~
  When/if you're happy with the changes, you can simply integrate them to your
  master branch, and if this is in a clone of the @cmd{plt} repository, then at
  this point you can simply push these commits to the main server.  Once that
  happens, the contributor can update their own clone, and continue working as
  usual.
@~
  Git has a tool that makes this mode of work a little more organized and
  robust for the contributor: @cmd{git request-pull}.  This simple command
  (surprisingly, it has no flags) expects a commit that marks the start of the
  new work (actually, the last one before it, eg, @cmd{origin/master}), and the
  url of the repository.  For example:
  @pre{git request-pull origin git://github.com/someone/somefork.git}
@~
  Of course, the contributor doesn't have to work directly in the available
  repository — in the case of github or with an over-the-web setup like the one
  described in the previous subsection the public repository is a bare one, and
  no work can be done directly on it.  So what actually happens is: the
  contributor works on his/her own repository, pushes changes to the public
  one, and then requests a pull.
@~
  The @cmd{request-pull} command will therefore check that the new commits are
  indeed available at that location, and find out the branch that they're on
  (in case it's different than the branch that someone is working on).  It then
  prints out a “pull request” text with a description of the changes, the url
  that was specified, the branch name with the new work, and a summary of the
  files that were changed.  In short, all the relevant information is there,
  and it even verified that the commits are indeed available — merging them in
  is now easy.
@~
  (As a sidenote, you can use @cmd{.} as the url:
  @cmd{git request-pull origin .}, and get a condensed summary of your
  changes.)}

@subsection{Pull-request workflow@br
            — recipe for the sender side}
@ol*{@~ Clone the @cmd{plt} repository and work with it as usual, commit your
        work
     @~ Make your repository publicly available
     @~ @npre{$ git request-pull origin @i{your-repository-url}}
     @~ Send the resulting text to @cmd{dev@at-racket}
     @~ You're done — thanks!}
@p{Alternatively, you can fork the @cmd{plt} repository on github:
   @cmd{https://github.com/plt/racket}, commit, then do a pull request.  Note:
   it is better to send a note about your pull request to @cmd{dev@at-racket},
   or you can do the pull request directly with git as listed above (using
   github to have a public repository).}

@subsection{Pull-request workflow@br
            — recipe for the receiver side}
@p{This recipe is for getting some remote work in as a one-time job.  If you
   need to cooperate more closely with someone, you will want to add the remote
   repository with @cmd{git remote} as shown above.}
@ol*{
@~ Get a @cmd{plt} clone, or use your own (it's safe to do the latter, no need
   for a new clone unless you're paranoid):
   @pre{git clone pltgit:plt
        cd plt}
@~ Get the foreign repository's master branch (or any other branch) into a
   local branch:
   @pre{git fetch @i{remote-repository-url} master:foo}
   This pulls the @cmd{master} branch of the remote repository into a local
   @cmd{foo} branch (you can use other names, of course).
@~ Inspect the changes as usual
   @pre{git log master..foo    # new commits
        git diff master...foo  # changes
        git log -p master..foo # both}
   (See above for more details on these.)
@~ If you're happy with the change and want to get it as-is, you can simply
   @cmd{merge} the branch:
   @pre{git merge foo}
   But unless the remote work was done from the point your @cmd{master} points
   at (i.e., there were no new commits), this will generate a merge commit that
   might not be desired.  To avoid it, you can rebase the branch against your
   @cmd{master} and then do the @cmd{merge} (which will now be a fast-forward)
   merge:
   @pre{git checkout foo
        git rebase master
        git checkout master
        git merge foo}
@~ You no longer need the @cmd{foo} branch, so delete it with:
   @pre{git branch -d foo}
@~ Push things back as usual}

@subsection{Merging github pull-requests}
@p*{
  Github is popular enough that some people prefer to work with a github fork
  of the PLT repository, and then send a pull request.  Merging these pull
  requests can be done as with any other repository, as explained in the
  previous section.  However, with github there is an easy way to deal with
  it.
@~
  A pull request has a URL like @cmd{https://github.com/plt/racket/pull/123}
  which you can use in your browser to inspect the changes.  To apply the
  changes locally, a convenient feature is that you can add a @cmd{.patch}
  suffix to every pull request URL which will have a text version of the patch.
  This means that applying the patch is particularly easy on the command line,
  for example:
  @pre{curl https://github.com/plt/racket/pull/123.patch | git am}
  will fetch the patch text and apply it (and you can now push as usual, or
  locally inspect the ptach and possibly edit it in the usual ways).}

@section{Additional Resources}
@dl*{
@~ @strong{Quick and short:}
@~ @dl*{
   @~ @selflink{http://eagain.net/articles/git-for-computer-scientists/}
   @~ Basic description of what makes a git repository
   @~ Cheat sheets:
   @~ @dl*{
      @~ @selflink{http://gitref.org/}
      @~ Quick reference thing, with links to the git man pages and the progit
         book
      @~ @selflink{http://jonas.nitro.dk/git/quick-reference.html}
      @~ Really short
      @~ @selflink{http://cheat.errtheblog.com/s/git}
      @~ Explains some more
      @~ @selflink{http://ktown.kde.org/~zrusin/git/git-cheat-sheet.svg}
      @~ Short, intended for printing
      @~ @selflink{http://help.github.com/git-cheat-sheets/}
      @~ Similar}
   @~ @selflink{http://git.or.cz/course/svn.html}
   @~ subversion->git crash course
   @~ @selflink{http://www.kernel.org/pub/software/scm/git/docs/everyday.html}
   @~ Nice summary of a few things, but too verbose or too advanced in some
      places, and also a little outdated.}
@~ @strong{Books:}
@~ @dl*{
   @~ @selflink{http://book.git-scm.com/}
   @~ The git community book.  Also, there are a bunch of videos
      linked, and some tutorial links in the “Welcome” part.
   @~ @selflink{http://progit.org/book/}
   @~ A frequently recommended book.  (Also some good blog entries.)
   @~ @selflink{http://www-cs-students.stanford.edu/~blynn/gitmagic/}
   @~ Another good book (a bit more verbose than the previous one)}
@~ @strong{Misc:}
@~ @dl*{
   @~ @selflink{http://www.kernel.org/pub/software/scm/git/docs/@;
                gittutorial.html}
   @~ The git tutorial, also available as the @man{gittutorial} man page.
   @~ @selflink{http://help.github.com/}
   @~ Some github guides, well-organized by levels.
   @~ @selflink{http://www.gitready.com/}
   @~ A kind of a collection of small tips; didn't change in a while though.
   @~ @selflink{http://marklodato.github.com/visual-git-guide/}
   @~ This is a short visual document about git.  But it goes a little fast, so
      it would be useful after you're comfortable with the basics.}}

}}))
