Merge branch 'jc/ref-excludes'
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2013 20:59:09 +0000 (12:59 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Dec 2013 20:59:09 +0000 (12:59 -0800)
People often wished a way to tell "git log --branches" (and "git
log --remotes --not --branches") to exclude some local branches
from the expansion of "--branches" (similarly for "--tags", "--all"
and "--glob=<pattern>"). Now they have one.

* jc/ref-excludes:
rev-parse: introduce --exclude=<glob> to tame wildcards
rev-list --exclude: export add/clear-ref-exclusion and ref-excluded API
rev-list --exclude: tests
document --exclude option
revision: introduce --exclude=<glob> to tame wildcards

1  2 
Documentation/git-rev-parse.txt
Documentation/rev-list-options.txt
builtin/rev-parse.c
revision.c
revision.h
index a436b24cc4068419280e80e0513dec40c383c0c0,4cba660bc85f1be9e8758ea29d0add20aa671e05..0d2cdcde556662d9d858dc282a9fced469814337
@@@ -24,23 -24,9 +24,23 @@@ distinguish between them
  
  OPTIONS
  -------
 +
 +Operation Modes
 +~~~~~~~~~~~~~~~
 +
 +Each of these options must appear first on the command line.
 +
  --parseopt::
        Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
  
 +--sq-quote::
 +      Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
 +      section below). In contrast to the `--sq` option below, this
 +      mode does only quoting. Nothing else is done to command input.
 +
 +Options for --parseopt
 +~~~~~~~~~~~~~~~~~~~~~~
 +
  --keep-dashdash::
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
        the first non-option argument.  This can be used to parse sub-commands
        that take options themselves.
  
 ---sq-quote::
 -      Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
 -      section below). In contrast to the `--sq` option below, this
 -      mode does only quoting. Nothing else is done to command input.
 +--stuck-long::
 +      Only meaningful in `--parseopt` mode. Output the options in their
 +      long form if available, and with their arguments stuck.
 +
 +Options for Filtering
 +~~~~~~~~~~~~~~~~~~~~~
  
  --revs-only::
        Do not output flags and parameters not meant for
@@@ -71,9 -55,6 +71,9 @@@
  --no-flags::
        Do not output flag parameters.
  
 +Options for Output
 +~~~~~~~~~~~~~~~~~~
 +
  --default <arg>::
        If there is no parameter given by the user, use `<arg>`
        instead.
@@@ -129,17 -110,6 +129,17 @@@ can be used
        strip '{caret}' prefix from the object names that already have
        one.
  
 +--abbrev-ref[=(strict|loose)]::
 +      A non-ambiguous short name of the objects name.
 +      The option core.warnAmbiguousRefs is used to select the strict
 +      abbreviation mode.
 +
 +--short::
 +--short=number::
 +      Instead of outputting the full SHA-1 values of object names try to
 +      abbreviate them to a shorter unique name. When no length is specified
 +      7 is used. The minimum length is 4.
 +
  --symbolic::
        Usually the object names are output in SHA-1 form (with
        possible '{caret}' prefix); this option makes them output in a
        unfortunately named tag "master"), and show them as full
        refnames (e.g. "refs/heads/master").
  
 ---abbrev-ref[=(strict|loose)]::
 -      A non-ambiguous short name of the objects name.
 -      The option core.warnAmbiguousRefs is used to select the strict
 -      abbreviation mode.
 -
 ---disambiguate=<prefix>::
 -      Show every object whose name begins with the given prefix.
 -      The <prefix> must be at least 4 hexadecimal digits long to
 -      avoid listing each and every object in the repository by
 -      mistake.
 +Options for Objects
 +~~~~~~~~~~~~~~~~~~~
  
  --all::
        Show all refs found in `refs/`.
@@@ -177,20 -155,32 +177,34 @@@ shown.  If the pattern does not contai
        character (`?`, `*`, or `[`), it is turned into a prefix
        match by appending `/*`.
  
 ---show-toplevel::
 -      Show the absolute path of the top-level directory.
+ --exclude=<glob-pattern>::
+       Do not include refs matching '<glob-pattern>' that the next `--all`,
+       `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise
+       consider. Repetitions of this option accumulate exclusion patterns
+       up to the next `--all`, `--branches`, `--tags`, `--remotes`, or
+       `--glob` option (other options or arguments do not clear
+       accumlated patterns).
+ +
+ The patterns given should not begin with `refs/heads`, `refs/tags`, or
+ `refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,
+ respectively, and they must begin with `refs/` when applied to `--glob`
+ or `--all`. If a trailing '/{asterisk}' is intended, it must be given
+ explicitly.
 +--disambiguate=<prefix>::
 +      Show every object whose name begins with the given prefix.
 +      The <prefix> must be at least 4 hexadecimal digits long to
 +      avoid listing each and every object in the repository by
 +      mistake.
  
 ---show-prefix::
 -      When the command is invoked from a subdirectory, show the
 -      path of the current directory relative to the top-level
 -      directory.
 +Options for Files
 +~~~~~~~~~~~~~~~~~
  
 ---show-cdup::
 -      When the command is invoked from a subdirectory, show the
 -      path of the top-level directory relative to the current
 -      directory (typically a sequence of "../", or an empty string).
 +--local-env-vars::
 +      List the GIT_* environment variables that are local to the
 +      repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
 +      Only the names of the variables are listed, not their value,
 +      even if they are set.
  
  --git-dir::
        Show `$GIT_DIR` if defined. Otherwise show the path to
@@@ -212,27 -202,17 +226,27 @@@ print a message to stderr and exit wit
  --is-bare-repository::
        When the repository is bare print "true", otherwise "false".
  
 ---local-env-vars::
 -      List the GIT_* environment variables that are local to the
 -      repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
 -      Only the names of the variables are listed, not their value,
 -      even if they are set.
 +--resolve-git-dir <path>::
 +      Check if <path> is a valid repository or a gitfile that
 +      points at a valid repository, and print the location of the
 +      repository.  If <path> is a gitfile then the resolved path
 +      to the real repository is printed.
  
 ---short::
 ---short=number::
 -      Instead of outputting the full SHA-1 values of object names try to
 -      abbreviate them to a shorter unique name. When no length is specified
 -      7 is used. The minimum length is 4.
 +--show-cdup::
 +      When the command is invoked from a subdirectory, show the
 +      path of the top-level directory relative to the current
 +      directory (typically a sequence of "../", or an empty string).
 +
 +--show-prefix::
 +      When the command is invoked from a subdirectory, show the
 +      path of the current directory relative to the top-level
 +      directory.
 +
 +--show-toplevel::
 +      Show the absolute path of the top-level directory.
 +
 +Other Options
 +~~~~~~~~~~~~~
  
  --since=datestring::
  --after=datestring::
  <args>...::
        Flags and parameters to be parsed.
  
 ---resolve-git-dir <path>::
 -      Check if <path> is a valid repository or a gitfile that
 -      points at a valid repository, and print the location of the
 -      repository.  If <path> is a gitfile then the resolved path
 -      to the real repository is printed.
 -
  
  include::revisions.txt[]
  
@@@ -289,9 -275,7 +303,9 @@@ Each line of options has this format
        `<flags>` are of `*`, `=`, `?` or `!`.
        * Use `=` if the option takes an argument.
  
 -      * Use `?` to mean that the option is optional (though its use is discouraged).
 +      * Use `?` to mean that the option takes an optional argument. You
 +        probably want to use the `--stuck-long` mode to be able to
 +        unambiguously parse the optional argument.
  
        * Use `*` to mean that this option should not be listed in the usage
          generated for the `-h` argument. It's shown for `--help-all` as
index 2991d70a4a24d10c7f121b766d5f52545414b591,7de3ad5427a971f36fa0f4c78dbf70d9a072672b..03533af7152e8b137cd3d885de3ec273b91d4791
@@@ -18,27 -18,33 +18,27 @@@ ordering and formatting options, such a
  -<number>::
  -n <number>::
  --max-count=<number>::
 -
        Limit the number of commits to output.
  
  --skip=<number>::
 -
        Skip 'number' commits before starting to show the commit output.
  
  --since=<date>::
  --after=<date>::
 -
        Show commits more recent than a specific date.
  
  --until=<date>::
  --before=<date>::
 -
        Show commits older than a specific date.
  
  ifdef::git-rev-list[]
  --max-age=<timestamp>::
  --min-age=<timestamp>::
 -
        Limit the commits output to specified time range.
  endif::git-rev-list[]
  
  --author=<pattern>::
  --committer=<pattern>::
 -
        Limit the commits output to ones with author/committer
        header lines that match the specified pattern (regular
        expression).  With more than one `--author=<pattern>`,
@@@ -46,6 -52,7 +46,6 @@@
        chosen (similarly for multiple `--committer=<pattern>`).
  
  --grep-reflog=<pattern>::
 -
        Limit the commits output to ones with reflog entries that
        match the specified pattern (regular expression). With
        more than one `--grep-reflog`, commits whose reflog message
@@@ -53,6 -60,7 +53,6 @@@
        error to use this option unless `--walk-reflogs` is in use.
  
  --grep=<pattern>::
 -
        Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).  With
        more than one `--grep=<pattern>`, commits whose message
@@@ -63,39 -71,46 +63,39 @@@ When `--show-notes` is in effect, the m
  if it is part of the log message.
  
  --all-match::
 -      Limit the commits output to ones that match all given --grep,
 +      Limit the commits output to ones that match all given `--grep`,
        instead of ones that match at least one.
  
  -i::
  --regexp-ignore-case::
 -
 -      Match the regexp limiting patterns without regard to letters case.
 +      Match the regular expression limiting patterns without regard to letter
 +      case.
  
  --basic-regexp::
 -
        Consider the limiting patterns to be basic regular expressions;
        this is the default.
  
  -E::
  --extended-regexp::
 -
        Consider the limiting patterns to be extended regular expressions
        instead of the default basic regular expressions.
  
  -F::
  --fixed-strings::
 -
        Consider the limiting patterns to be fixed strings (don't interpret
        pattern as a regular expression).
  
  --perl-regexp::
 -
 -      Consider the limiting patterns to be Perl-compatible regexp.
 +      Consider the limiting patterns to be Perl-compatible regular expressions.
        Requires libpcre to be compiled in.
  
  --remove-empty::
 -
        Stop when a given path disappears from the tree.
  
  --merges::
 -
        Print only merge commits. This is exactly the same as `--min-parents=2`.
  
  --no-merges::
 -
        Do not print commits with more than one parent. This is
        exactly the same as `--max-parents=1`.
  
  --max-parents=<number>::
  --no-min-parents::
  --no-max-parents::
 -
        Show only commits which have at least (or at most) that many parent
        commits. In particular, `--max-parents=1` is the same as `--no-merges`,
        `--min-parents=2` is the same as `--merges`.  `--max-parents=0`
@@@ -122,26 -138,31 +122,26 @@@ parents) and `--max-parents=-1` (negati
        brought in to your history by such a merge.
  
  --not::
 -
        Reverses the meaning of the '{caret}' prefix (or lack thereof)
 -      for all following revision specifiers, up to the next '--not'.
 +      for all following revision specifiers, up to the next `--not`.
  
  --all::
 -
        Pretend as if all the refs in `refs/` are listed on the
        command line as '<commit>'.
  
  --branches[=<pattern>]::
 -
        Pretend as if all the refs in `refs/heads` are listed
        on the command line as '<commit>'. If '<pattern>' is given, limit
        branches to ones matching given shell glob. If pattern lacks '?',
        '{asterisk}', or '[', '/{asterisk}' at the end is implied.
  
  --tags[=<pattern>]::
 -
        Pretend as if all the refs in `refs/tags` are listed
        on the command line as '<commit>'. If '<pattern>' is given, limit
        tags to ones matching given shell glob. If pattern lacks '?', '{asterisk}',
        or '[', '/{asterisk}' at the end is implied.
  
  --remotes[=<pattern>]::
 -
        Pretend as if all the refs in `refs/remotes` are listed
        on the command line as '<commit>'. If '<pattern>' is given, limit
        remote-tracking branches to ones matching given shell glob.
        is automatically prepended if missing. If pattern lacks '?', '{asterisk}',
        or '[', '/{asterisk}' at the end is implied.
  
+ --exclude=<glob-pattern>::
+       Do not include refs matching '<glob-pattern>' that the next `--all`,
+       `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise
+       consider. Repetitions of this option accumulate exclusion patterns
+       up to the next `--all`, `--branches`, `--tags`, `--remotes`, or
+       `--glob` option (other options or arguments do not clear
+       accumlated patterns).
+ +
+ The patterns given should not begin with `refs/heads`, `refs/tags`, or
+ `refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,
+ respectively, and they must begin with `refs/` when applied to `--glob`
+ or `--all`. If a trailing '/{asterisk}' is intended, it must be given
+ explicitly.
  --ignore-missing::
 -
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
  
  ifndef::git-rev-list[]
  --bisect::
 -
        Pretend as if the bad bisection ref `refs/bisect/bad`
        was listed and as if it was followed by `--not` and the good
        bisection refs `refs/bisect/good-*` on the command
  endif::git-rev-list[]
  
  --stdin::
 -
        In addition to the '<commit>' listed on the command
        line, read them from the standard input. If a '--' separator is
        seen, stop reading commits and start reading paths to limit the
  
  ifdef::git-rev-list[]
  --quiet::
 -
        Don't print anything to standard output.  This form
        is primarily meant to allow the caller to
        test the exit status to see if a range of objects is fully
        connected (or not).  It is faster than redirecting stdout
 -      to /dev/null as the output does not have to be formatted.
 +      to `/dev/null` as the output does not have to be formatted.
  endif::git-rev-list[]
  
  --cherry-mark::
 -
        Like `--cherry-pick` (see below) but mark equivalent commits
        with `=` rather than omitting them, and inequivalent ones with `+`.
  
  --cherry-pick::
 -
        Omit any commit that introduces the same change as
 -      another commit on the "other side" when the set of
 +      another commit on the ``other side'' when the set of
        commits are limited with symmetric difference.
  +
  For example, if you have two branches, `A` and `B`, a usual way
  to list all commits on only one side of them is with
  `--left-right` (see the example below in the description of
 -the `--left-right` option).  It however shows the commits that were cherry-picked
 -from the other branch (for example, "3rd on b" may be cherry-picked
 -from branch A).  With this option, such pairs of commits are
 +the `--left-right` option). However, it shows the commits that were
 +cherry-picked from the other branch (for example, ``3rd on b'' may be
 +cherry-picked from branch A). With this option, such pairs of commits are
  excluded from the output.
  
  --left-only::
  --right-only::
 -
        List only commits on the respective side of a symmetric range,
        i.e. only those which would be marked `<` resp. `>` by
        `--left-right`.
@@@ -210,6 -253,7 +225,6 @@@ More precisely, `--cherry-pick --right-
  list.
  
  --cherry::
 -
        A synonym for `--right-only --cherry-mark --no-merges`; useful to
        limit the output to the commits on our side and mark those that
        have been applied to the other side of a forked history with
  
  -g::
  --walk-reflogs::
 -
        Instead of walking the commit ancestry chain, walk
        reflog entries from the most recent one to older ones.
        When this option is used you cannot specify commits to
        exclude (that is, '{caret}commit', 'commit1..commit2',
        nor 'commit1\...commit2' notations cannot be used).
  +
 -With '\--pretty' format other than oneline (for obvious reasons),
 +With `--pretty` format other than `oneline` (for obvious reasons),
  this causes the output to have two extra lines of information
  taken from the reflog.  By default, 'commit@\{Nth}' notation is
  used in the output.  When the starting commit is specified as
  'commit@\{now}', output also uses 'commit@\{timestamp}' notation
 -instead.  Under '\--pretty=oneline', the commit message is
 +instead.  Under `--pretty=oneline`, the commit message is
  prefixed with this information on the same line.
 -This option cannot be combined with '\--reverse'.
 +This option cannot be combined with `--reverse`.
  See also linkgit:git-reflog[1].
  
  --merge::
 -
        After a failed merge, show refs that touch files having a
        conflict and don't exist on all heads to merge.
  
  --boundary::
 -
        Output excluded boundary commits. Boundary commits are
        prefixed with `-`.
  
@@@ -255,9 -302,11 +270,9 @@@ is how to do it, as there are various s
  The following options select the commits to be shown:
  
  <paths>::
 -
        Commits modifying the given <paths> are selected.
  
  --simplify-by-decoration::
 -
        Commits that are referred by some branch or tag are selected.
  
  Note that extra commits can be shown to give a meaningful history.
  The following options affect the way the simplification is performed:
  
  Default mode::
 -
        Simplifies the history to the simplest history explaining the
        final state of the tree. Simplest because it prunes some side
        branches if the end result is the same (i.e. merging branches
        with the same content)
  
  --full-history::
 -
        Same as the default mode, but does not prune some history.
  
  --dense::
 -
        Only the selected commits are shown, plus some to have a
        meaningful history.
  
  --sparse::
 -
        All commits in the simplified history are shown.
  
  --simplify-merges::
 -
 -      Additional option to '--full-history' to remove some needless
 +      Additional option to `--full-history` to remove some needless
        merges from the resulting history, as there are no selected
        commits contributing to this merge.
  
  --ancestry-path::
 -
        When given a range of commits to display (e.g. 'commit1..commit2'
        or 'commit2 {caret}commit1'), only display commits that exist
        directly on the ancestry chain between the 'commit1' and
@@@ -312,35 -367,36 +327,35 @@@ The horizontal line of history A---Q i
  each merge.  The commits are:
  
  * `I` is the initial commit, in which `foo` exists with contents
 -  "asdf", and a file `quux` exists with contents "quux".  Initial
 +  ``asdf'', and a file `quux` exists with contents ``quux''. Initial
    commits are compared to an empty tree, so `I` is !TREESAME.
  
 -* In `A`, `foo` contains just "foo".
 +* In `A`, `foo` contains just ``foo''.
  
  * `B` contains the same change as `A`.  Its merge `M` is trivial and
    hence TREESAME to all parents.
  
 -* `C` does not change `foo`, but its merge `N` changes it to "foobar",
 +* `C` does not change `foo`, but its merge `N` changes it to ``foobar'',
    so it is not TREESAME to any parent.
  
 -* `D` sets `foo` to "baz".  Its merge `O` combines the strings from
 -  `N` and `D` to "foobarbaz"; i.e., it is not TREESAME to any parent.
 +* `D` sets `foo` to ``baz''. Its merge `O` combines the strings from
 +  `N` and `D` to ``foobarbaz''; i.e., it is not TREESAME to any parent.
  
 -* `E` changes `quux` to "xyzzy", and its merge `P` combines the
 -  strings to "quux xyzzy".  `P` is TREESAME to `O`, but not to `E`.
 +* `E` changes `quux` to ``xyzzy'', and its merge `P` combines the
 +  strings to ``quux xyzzy''. `P` is TREESAME to `O`, but not to `E`.
  
  * `X` is an independent root commit that added a new file `side`, and `Y`
    modified it. `Y` is TREESAME to `X`. Its merge `Q` added `side` to `P`, and
    `Q` is TREESAME to `P`, but not to `Y`.
  
 -'rev-list' walks backwards through history, including or excluding
 -commits based on whether '\--full-history' and/or parent rewriting
 -(via '\--parents' or '\--children') are used.  The following settings
 +`rev-list` walks backwards through history, including or excluding
 +commits based on whether `--full-history` and/or parent rewriting
 +(via `--parents` or `--children`) are used. The following settings
  are available.
  
  Default mode::
 -
        Commits are included if they are not TREESAME to any parent
 -      (though this can be changed, see '\--sparse' below).  If the
 +      (though this can be changed, see `--sparse` below).  If the
        commit was a merge, and it was TREESAME to one parent, follow
        only that parent.  (Even if there are several TREESAME
        parents, follow only one of them.)  Otherwise, follow all
@@@ -359,11 -415,12 +374,11 @@@ available, removed `B` from considerati
  considered via `N`, but is TREESAME.  Root commits are compared to an
  empty tree, so `I` is !TREESAME.
  +
 -Parent/child relations are only visible with --parents, but that does
 +Parent/child relations are only visible with `--parents`, but that does
  not affect the commits selected in default mode, so we have shown the
  parent lines.
  
  --full-history without parent rewriting::
 -
        This mode differs from the default in one point: always follow
        all parents of a merge, even if it is TREESAME to one of them.
        Even if more than one side of the merge has commits that are
@@@ -383,8 -440,9 +398,8 @@@ about the parent/child relationships be
  them disconnected.
  
  --full-history with parent rewriting::
 -
        Ordinary commits are only included if they are !TREESAME
 -      (though this can be changed, see '\--sparse' below).
 +      (though this can be changed, see `--sparse` below).
  +
  Merges are always included.  However, their parent list is rewritten:
  Along each parent, prune away commits that are not included
@@@ -398,7 -456,7 +413,7 @@@ themselves.  This results i
          `-------------'
  -----------------------------------------------------------------------
  +
 -Compare to '\--full-history' without rewriting above.  Note that `E`
 +Compare to `--full-history` without rewriting above.  Note that `E`
  was pruned away because it is TREESAME, but the parent list of P was
  rewritten to contain `E`'s parent `I`.  The same happened for `C` and
  `N`, and `X`, `Y` and `Q`.
@@@ -407,19 -465,22 +422,19 @@@ In addition to the above settings, you 
  affects inclusion:
  
  --dense::
 -
        Commits that are walked are included if they are not TREESAME
        to any parent.
  
  --sparse::
 -
        All commits that are walked are included.
  +
 -Note that without '\--full-history', this still simplifies merges: if
 +Note that without `--full-history`, this still simplifies merges: if
  one of the parents is TREESAME, we follow only that one, so the other
  sides of the merge are never walked.
  
  --simplify-merges::
 -
        First, build a history graph in the same way that
 -      '\--full-history' with parent rewriting does (see above).
 +      `--full-history` with parent rewriting does (see above).
  +
  Then simplify each commit `C` to its replacement `C'` in the final
  history according to the following rules:
  --
  +
  The effect of this is best shown by way of comparing to
 -'\--full-history' with parent rewriting.  The example turns into:
 +`--full-history` with parent rewriting.  The example turns into:
  +
  -----------------------------------------------------------------------
          .-A---M---N---O
          `---------'
  -----------------------------------------------------------------------
  +
 -Note the major differences in `N`, `P` and `Q` over '--full-history':
 +Note the major differences in `N`, `P`, and `Q` over `--full-history`:
  +
  --
  * `N`'s parent list had `I` removed, because it is an ancestor of the
  Finally, there is a fifth simplification mode available:
  
  --ancestry-path::
 -
        Limit the displayed commits to those directly on the ancestry
 -      chain between the "from" and "to" commits in the given commit
 -      range. I.e. only display commits that are ancestor of the "to"
 -      commit, and descendants of the "from" commit.
 +      chain between the ``from'' and ``to'' commits in the given commit
 +      range. I.e. only display commits that are ancestor of the ``to''
 +      commit and descendants of the ``from'' commit.
  +
  As an example use case, consider the following commit history:
  +
  A regular 'D..M' computes the set of commits that are ancestors of `M`,
  but excludes the ones that are ancestors of `D`. This is useful to see
  what happened to the history leading to `M` since `D`, in the sense
 -that "what does `M` have that did not exist in `D`". The result in this
 +that ``what does `M` have that did not exist in `D`''. The result in this
  example would be all the commits, except `A` and `B` (and `D` itself,
  of course).
  +
  When we want to find out what commits in `M` are contaminated with the
  bug introduced by `D` and need fixing, however, we might want to view
  only the subset of 'D..M' that are actually descendants of `D`, i.e.
 -excluding `C` and `K`. This is exactly what the '--ancestry-path'
 +excluding `C` and `K`. This is exactly what the `--ancestry-path`
  option does. Applied to the 'D..M' range, it results in:
  +
  -----------------------------------------------------------------------
                                L--M
  -----------------------------------------------------------------------
  
 -The '\--simplify-by-decoration' option allows you to view only the
 +The `--simplify-by-decoration` option allows you to view only the
  big picture of the topology of the history, by omitting commits
  that are not referenced by tags.  Commits are marked as !TREESAME
  (in other words, kept after history simplification rules described
@@@ -514,47 -576,50 +529,47 @@@ Bisection Helper
  ~~~~~~~~~~~~~~~~~
  
  --bisect::
 -
 -Limit output to the one commit object which is roughly halfway between
 -included and excluded commits. Note that the bad bisection ref
 -`refs/bisect/bad` is added to the included commits (if it
 -exists) and the good bisection refs `refs/bisect/good-*` are
 -added to the excluded commits (if they exist). Thus, supposing there
 -are no refs in `refs/bisect/`, if
 -
 +      Limit output to the one commit object which is roughly halfway between
 +      included and excluded commits. Note that the bad bisection ref
 +      `refs/bisect/bad` is added to the included commits (if it
 +      exists) and the good bisection refs `refs/bisect/good-*` are
 +      added to the excluded commits (if they exist). Thus, supposing there
 +      are no refs in `refs/bisect/`, if
 ++
  -----------------------------------------------------------------------
        $ git rev-list --bisect foo ^bar ^baz
  -----------------------------------------------------------------------
 -
 ++
  outputs 'midpoint', the output of the two commands
 -
 ++
  -----------------------------------------------------------------------
        $ git rev-list foo ^midpoint
        $ git rev-list midpoint ^bar ^baz
  -----------------------------------------------------------------------
 -
 ++
  would be of roughly the same length.  Finding the change which
  introduces a regression is thus reduced to a binary search: repeatedly
  generate and test new 'midpoint's until the commit chain is of length
  one.
  
  --bisect-vars::
 -
 -This calculates the same as `--bisect`, except that refs in
 -`refs/bisect/` are not used, and except that this outputs
 -text ready to be eval'ed by the shell. These lines will assign the
 -name of the midpoint revision to the variable `bisect_rev`, and the
 -expected number of commits to be tested after `bisect_rev` is tested
 -to `bisect_nr`, the expected number of commits to be tested if
 -`bisect_rev` turns out to be good to `bisect_good`, the expected
 -number of commits to be tested if `bisect_rev` turns out to be bad to
 -`bisect_bad`, and the number of commits we are bisecting right now to
 -`bisect_all`.
 +      This calculates the same as `--bisect`, except that refs in
 +      `refs/bisect/` are not used, and except that this outputs
 +      text ready to be eval'ed by the shell. These lines will assign the
 +      name of the midpoint revision to the variable `bisect_rev`, and the
 +      expected number of commits to be tested after `bisect_rev` is tested
 +      to `bisect_nr`, the expected number of commits to be tested if
 +      `bisect_rev` turns out to be good to `bisect_good`, the expected
 +      number of commits to be tested if `bisect_rev` turns out to be bad to
 +      `bisect_bad`, and the number of commits we are bisecting right now to
 +      `bisect_all`.
  
  --bisect-all::
 -
 -This outputs all the commit objects between the included and excluded
 -commits, ordered by their distance to the included and excluded
 -commits. Refs in `refs/bisect/` are not used. The farthest
 -from them is displayed first. (This is the only one displayed by
 -`--bisect`.)
 +      This outputs all the commit objects between the included and excluded
 +      commits, ordered by their distance to the included and excluded
 +      commits. Refs in `refs/bisect/` are not used. The farthest
 +      from them is displayed first. (This is the only one displayed by
 +      `--bisect`.)
  +
  This is useful because it makes it easy to choose a good commit to
  test when you want to avoid to test some of them for some reason (they
@@@ -604,8 -669,9 +619,8 @@@ avoid showing the commits from two para
  together.
  
  --reverse::
 -
        Output the commits in reverse order.
 -      Cannot be combined with '\--walk-reflogs'.
 +      Cannot be combined with `--walk-reflogs`.
  
  Object Traversal
  ~~~~~~~~~~~~~~~~
  These options are mostly targeted for packing of Git repositories.
  
  --objects::
 -
        Print the object IDs of any object referenced by the listed
 -      commits.  '--objects foo ^bar' thus means "send me
 +      commits.  `--objects foo ^bar` thus means ``send me
        all object IDs which I need to download if I have the commit
 -      object 'bar', but not 'foo'".
 +      object _bar_ but not _foo_''.
  
  --objects-edge::
 -
 -      Similar to '--objects', but also print the IDs of excluded
 -      commits prefixed with a "-" character.  This is used by
 -      linkgit:git-pack-objects[1] to build "thin" pack, which records
 +      Similar to `--objects`, but also print the IDs of excluded
 +      commits prefixed with a ``-'' character.  This is used by
 +      linkgit:git-pack-objects[1] to build ``thin'' pack, which records
        objects in deltified form based on objects contained in these
        excluded commits to reduce network traffic.
  
  --unpacked::
 -
 -      Only useful with '--objects'; print the object IDs that are not
 +      Only useful with `--objects`; print the object IDs that are not
        in packs.
  
  --no-walk[=(sorted|unsorted)]::
 -
        Only show the given commits, but do not traverse their ancestors.
        This has no effect if a range is specified. If the argument
 -      "unsorted" is given, the commits are show in the order they were
 -      given on the command line. Otherwise (if "sorted" or no argument
 -      was given), the commits are show in reverse chronological order
 +      `unsorted` is given, the commits are shown in the order they were
 +      given on the command line. Otherwise (if `sorted` or no argument
 +      was given), the commits are shown in reverse chronological order
        by commit time.
  
  --do-walk::
 -
 -      Overrides a previous --no-walk.
 +      Overrides a previous `--no-walk`.
  
  Commit Formatting
  ~~~~~~~~~~~~~~~~~
@@@ -652,41 -723,46 +667,41 @@@ endif::git-rev-list[
  include::pretty-options.txt[]
  
  --relative-date::
 -
        Synonym for `--date=relative`.
  
  --date=(relative|local|default|iso|rfc|short|raw)::
 -
        Only takes effect for dates shown in human-readable format, such
 -      as when using "--pretty". `log.date` config variable sets a default
 -      value for log command's --date option.
 +      as when using `--pretty`. `log.date` config variable sets a default
 +      value for the log command's `--date` option.
  +
  `--date=relative` shows dates relative to the current time,
 -e.g. "2 hours ago".
 +e.g. ``2 hours ago''.
  +
 -`--date=local` shows timestamps in user's local timezone.
 +`--date=local` shows timestamps in user's local time zone.
  +
  `--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
  +
  `--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
 -format, often found in E-mail messages.
 +format, often found in email messages.
  +
 -`--date=short` shows only date but not time, in `YYYY-MM-DD` format.
 +`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
  +
  `--date=raw` shows the date in the internal raw Git format `%s %z` format.
  +
 -`--date=default` shows timestamps in the original timezone
 +`--date=default` shows timestamps in the original time zone
  (either committer's or author's).
  
  ifdef::git-rev-list[]
  --header::
 -
        Print the contents of the commit in raw-format; each record is
        separated with a NUL character.
  endif::git-rev-list[]
  
  --parents::
 -
        Print also the parents of the commit (in the form "commit parent...").
        Also enables parent rewriting, see 'History Simplification' below.
  
  --children::
 -
        Print also the children of the commit (in the form "commit child...").
        Also enables parent rewriting, see 'History Simplification' below.
  
@@@ -696,6 -772,7 +711,6 @@@ ifdef::git-rev-list[
  endif::git-rev-list[]
  
  --left-right::
 -
        Mark which side of a symmetric diff a commit is reachable from.
        Commits from the left side are prefixed with `<` and those from
        the right with `>`.  If combined with `--boundary`, those
@@@ -725,6 -802,7 +740,6 @@@ you would get an output like this
  -----------------------------------------------------------------------
  
  --graph::
 -
        Draw a text-based graphical representation of the commit history
        on the left hand side of the output.  This may cause extra lines
        to be printed in between commits, in order for the graph history
  +
  This enables parent rewriting, see 'History Simplification' below.
  +
 -This implies the '--topo-order' option by default, but the
 -'--date-order' option may also be specified.
 +This implies the `--topo-order` option by default, but the
 +`--date-order` option may also be specified.
  
  ifdef::git-rev-list[]
  --count::
        Print a number stating how many commits would have been
        listed, and suppress all other output.  When used together
 -      with '--left-right', instead print the counts for left and
 +      with `--left-right`, instead print the counts for left and
        right commits, separated by a tab. When used together with
 -      '--cherry-mark', omit patch equivalent commits from these
 +      `--cherry-mark`, omit patch equivalent commits from these
        counts and print the count for equivalent commits separated
        by a tab.
  endif::git-rev-list[]
  
 -
  ifndef::git-rev-list[]
  Diff Formatting
  ~~~~~~~~~~~~~~~
  
 -Below are listed options that control the formatting of diff output.
 +Listed below are options that control the formatting of diff output.
  Some of them are specific to linkgit:git-rev-list[1], however other diff
  options may be given. See linkgit:git-diff-files[1] for more options.
  
  -c::
 -
        With this option, diff output for a merge commit
        shows the differences from each of the parents to the merge result
        simultaneously instead of showing pairwise diff between a parent
        which were modified from all parents.
  
  --cc::
 -
 -      This flag implies the '-c' option and further compresses the
 +      This flag implies the `-c` option and further compresses the
        patch output by omitting uninteresting hunks whose contents in
        the parents have only two variants and the merge result picks
        one of them without modification.
  
  -m::
 -
        This flag makes the merge commits show the full diff like
        regular commits; for each merge parent, a separate log entry
        and diff is generated. An exception is that only diff against
 -      the first parent is shown when '--first-parent' option is given;
 +      the first parent is shown when `--first-parent` option is given;
        in that case, the output represents the changes the merge
        brought _into_ the then-current branch.
  
  -r::
 -
        Show recursive diffs.
  
  -t::
 -
 -      Show the tree objects in the diff output. This implies '-r'.
 +      Show the tree objects in the diff output. This implies `-r`.
  endif::git-rev-list[]
diff --combined builtin/rev-parse.c
index 3e8c4cce060ed4d4bc4ea881f45546d6cffc725c,f52f8048bb7522792312e036a0ed6e92a48c0880..1d9ecafd417cf763d0c33438b74cec28c463ed74
@@@ -9,6 -9,8 +9,8 @@@
  #include "quote.h"
  #include "builtin.h"
  #include "parse-options.h"
+ #include "diff.h"
+ #include "revision.h"
  
  #define DO_REVS               1
  #define DO_NOREV      2
@@@ -30,7 -32,7 +32,8 @@@ static int abbrev_ref
  static int abbrev_ref_strict;
  static int output_sq;
  
 +static int stuck_long;
+ static struct string_list *ref_excludes;
  
  /*
   * Some arguments are relevant "revision" arguments,
@@@ -187,6 -189,8 +190,8 @@@ static int show_default(void
  
  static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
  {
+       if (ref_excluded(ref_excludes, refname))
+               return 0;
        show_rev(NORMAL, sha1, refname);
        return 0;
  }
@@@ -322,15 -326,12 +327,15 @@@ static int parseopt_dump(const struct o
        struct strbuf *parsed = o->value;
        if (unset)
                strbuf_addf(parsed, " --no-%s", o->long_name);
 -      else if (o->short_name)
 +      else if (o->short_name && (o->long_name == NULL || !stuck_long))
                strbuf_addf(parsed, " -%c", o->short_name);
        else
                strbuf_addf(parsed, " --%s", o->long_name);
        if (arg) {
 -              strbuf_addch(parsed, ' ');
 +              if (!stuck_long)
 +                      strbuf_addch(parsed, ' ');
 +              else if (o->long_name)
 +                      strbuf_addch(parsed, '=');
                sq_quote_buf(parsed, arg);
        }
        return 0;
@@@ -351,13 -352,11 +356,13 @@@ static int cmd_parseopt(int argc, cons
                NULL
        };
        static struct option parseopt_opts[] = {
 -              OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
 +              OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
                                        N_("keep the `--` passed as an arg")),
 -              OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
 +              OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
                                        N_("stop parsing after the "
                                           "first non-option argument")),
 +              OPT_BOOL(0, "stuck-long", &stuck_long,
 +                                      N_("output in stuck long form")),
                OPT_END(),
        };
  
@@@ -493,6 -492,21 +498,6 @@@ int cmd_rev_parse(int argc, const char 
        if (argc > 1 && !strcmp("--sq-quote", argv[1]))
                return cmd_sq_quote(argc - 2, argv + 2);
  
 -      if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
 -              int i;
 -              for (i = 0; local_repo_env[i]; i++)
 -                      printf("%s\n", local_repo_env[i]);
 -              return 0;
 -      }
 -
 -      if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
 -              const char *gitdir = resolve_gitdir(argv[2]);
 -              if (!gitdir)
 -                      die("not a gitdir '%s'", argv[2]);
 -              puts(gitdir);
 -              return 0;
 -      }
 -
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
  
                        if (!prefixcmp(arg, "--branches=")) {
                                for_each_glob_ref_in(show_reference, arg + 11,
                                        "refs/heads/", NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
                                for_each_branch_ref(show_reference, NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!prefixcmp(arg, "--tags=")) {
                                for_each_glob_ref_in(show_reference, arg + 7,
                                        "refs/tags/", NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
                                for_each_tag_ref(show_reference, NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!prefixcmp(arg, "--glob=")) {
                                for_each_glob_ref(show_reference, arg + 7, NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!prefixcmp(arg, "--remotes=")) {
                                for_each_glob_ref_in(show_reference, arg + 10,
                                        "refs/remotes/", NULL);
+                               clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
                                for_each_remote_ref(show_reference, NULL);
+                               clear_ref_exclusion(&ref_excludes);
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--exclude=")) {
+                               add_ref_exclusion(&ref_excludes, arg + 10);
                                continue;
                        }
 +                      if (!strcmp(arg, "--local-env-vars")) {
 +                              int i;
 +                              for (i = 0; local_repo_env[i]; i++)
 +                                      printf("%s\n", local_repo_env[i]);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--show-toplevel")) {
                                const char *work_tree = get_git_work_tree();
                                if (work_tree)
                                printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
                                continue;
                        }
 +                      if (!strcmp(arg, "--resolve-git-dir")) {
 +                              const char *gitdir = resolve_gitdir(argv[i+1]);
 +                              if (!gitdir)
 +                                      die("not a gitdir '%s'", argv[i+1]);
 +                              puts(gitdir);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
                                printf("%s\n", is_inside_git_dir() ? "true"
                                                : "false");
diff --combined revision.c
index a8adb3fc8891a73fb4017d18b00020bca790c766,ddc71b9e0fb44302f312c03efa56fa69fb99cffe..05d2d7763a3c9a2bd555cb19ef9af01c5767adb8
@@@ -15,7 -15,6 +15,7 @@@
  #include "string-list.h"
  #include "line-log.h"
  #include "mailmap.h"
 +#include "commit-slab.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -139,7 -138,8 +139,7 @@@ void mark_tree_uninteresting(struct tre
         * We don't care about the tree any more
         * after it has been marked uninteresting.
         */
 -      free(tree->buffer);
 -      tree->buffer = NULL;
 +      free_tree_buffer(tree);
  }
  
  void mark_parents_uninteresting(struct commit *commit)
@@@ -200,7 -200,7 +200,7 @@@ static void add_pending_object_with_mod
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
 -              int len = interpret_branch_name(name, &buf);
 +              int len = interpret_branch_name(name, 0, &buf);
                int st;
  
                if (0 < len && name[len] && buf.len)
@@@ -1180,11 -1180,28 +1180,28 @@@ struct all_refs_cb 
        const char *name_for_errormsg;
  };
  
+ int ref_excluded(struct string_list *ref_excludes, const char *path)
+ {
+       struct string_list_item *item;
+       if (!ref_excludes)
+               return 0;
+       for_each_string_list_item(item, ref_excludes) {
+               if (!fnmatch(item->string, path, 0))
+                       return 1;
+       }
+       return 0;
+ }
  static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
-       struct object *object = get_reference(cb->all_revs, path, sha1,
-                                             cb->all_flags);
+       struct object *object;
+       if (ref_excluded(cb->all_revs->ref_excludes, path))
+           return 0;
+       object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
        add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
        add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
        return 0;
@@@ -1197,6 -1214,24 +1214,24 @@@ static void init_all_refs_cb(struct all
        cb->all_flags = flags;
  }
  
+ void clear_ref_exclusion(struct string_list **ref_excludes_p)
+ {
+       if (*ref_excludes_p) {
+               string_list_clear(*ref_excludes_p, 0);
+               free(*ref_excludes_p);
+       }
+       *ref_excludes_p = NULL;
+ }
+ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
+ {
+       if (!*ref_excludes_p) {
+               *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));
+               (*ref_excludes_p)->strdup_strings = 1;
+       }
+       string_list_append(*ref_excludes_p, exclude);
+ }
  static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
                int (*for_each)(const char *, each_ref_fn, void *))
  {
@@@ -1372,8 -1407,7 +1407,8 @@@ static void prepare_show_merge(struct r
                        i++;
        }
        free_pathspec(&revs->prune_data);
 -      init_pathspec(&revs->prune_data, prune);
 +      parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
 +                     PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune);
        revs->limited = 1;
  }
  
@@@ -1420,40 -1454,26 +1455,40 @@@ int handle_revision_arg(const char *arg
                }
                if (!get_sha1_committish(this, from_sha1) &&
                    !get_sha1_committish(next, sha1)) {
 -                      struct commit *a, *b;
 -                      struct commit_list *exclude;
 -
 -                      a = lookup_commit_reference(from_sha1);
 -                      b = lookup_commit_reference(sha1);
 -                      if (!a || !b) {
 -                              if (revs->ignore_missing)
 -                                      return 0;
 -                              die(symmetric ?
 -                                  "Invalid symmetric difference expression %s...%s" :
 -                                  "Invalid revision range %s..%s",
 -                                  arg, next);
 -                      }
 +                      struct object *a_obj, *b_obj;
  
                        if (!cant_be_filename) {
                                *dotdot = '.';
                                verify_non_filename(revs->prefix, arg);
                        }
  
 -                      if (symmetric) {
 +                      a_obj = parse_object(from_sha1);
 +                      b_obj = parse_object(sha1);
 +                      if (!a_obj || !b_obj) {
 +                      missing:
 +                              if (revs->ignore_missing)
 +                                      return 0;
 +                              die(symmetric
 +                                  ? "Invalid symmetric difference expression %s"
 +                                  : "Invalid revision range %s", arg);
 +                      }
 +
 +                      if (!symmetric) {
 +                              /* just A..B */
 +                              a_flags = flags_exclude;
 +                      } else {
 +                              /* A...B -- find merge bases between the two */
 +                              struct commit *a, *b;
 +                              struct commit_list *exclude;
 +
 +                              a = (a_obj->type == OBJ_COMMIT
 +                                   ? (struct commit *)a_obj
 +                                   : lookup_commit_reference(a_obj->sha1));
 +                              b = (b_obj->type == OBJ_COMMIT
 +                                   ? (struct commit *)b_obj
 +                                   : lookup_commit_reference(b_obj->sha1));
 +                              if (!a || !b)
 +                                      goto missing;
                                exclude = get_merge_bases(a, b, 1);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
 +
                                a_flags = flags | SYMMETRIC_LEFT;
 -                      } else
 -                              a_flags = flags_exclude;
 -                      a->object.flags |= a_flags;
 -                      b->object.flags |= flags;
 -                      add_rev_cmdline(revs, &a->object, this,
 +                      }
 +
 +                      a_obj->flags |= a_flags;
 +                      b_obj->flags |= flags;
 +                      add_rev_cmdline(revs, a_obj, this,
                                        REV_CMD_LEFT, a_flags);
 -                      add_rev_cmdline(revs, &b->object, next,
 +                      add_rev_cmdline(revs, b_obj, next,
                                        REV_CMD_RIGHT, flags);
 -                      add_pending_object(revs, &a->object, this);
 -                      add_pending_object(revs, &b->object, next);
 +                      add_pending_object(revs, a_obj, this);
 +                      add_pending_object(revs, b_obj, next);
                        return 0;
                }
                *dotdot = '.';
@@@ -1519,7 -1538,7 +1554,7 @@@ struct cmdline_pathspec 
  static void append_prune_data(struct cmdline_pathspec *prune, const char **av)
  {
        while (*av) {
 -              ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
 +              ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = *(av++);
        }
  }
@@@ -1531,7 -1550,7 +1566,7 @@@ static void read_pathspec_from_stdin(st
                int len = sb->len;
                if (len && sb->buf[len - 1] == '\n')
                        sb->buf[--len] = '\0';
 -              ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
 +              ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = xstrdup(sb->buf);
        }
  }
@@@ -1969,33 -1988,44 +2004,44 @@@ static int handle_revision_pseudo_opt(c
        if (!strcmp(arg, "--all")) {
                handle_refs(submodule, revs, *flags, for_each_ref_submodule);
                handle_refs(submodule, revs, *flags, head_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
                handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
                handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref(handle_one_ref, optarg, &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
+               return argcount;
+       } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
+               add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
        } else if (!prefixcmp(arg, "--branches=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!prefixcmp(arg, "--tags=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!prefixcmp(arg, "--remotes=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                handle_reflog(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
@@@ -2134,10 -2164,10 +2180,10 @@@ int setup_revisions(int argc, const cha
                 *      call init_pathspec() to set revs->prune_data here.
                 * }
                 */
 -              ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
 +              ALLOC_GROW(prune_data.path, prune_data.nr + 1, prune_data.alloc);
                prune_data.path[prune_data.nr++] = NULL;
 -              init_pathspec(&revs->prune_data,
 -                            get_pathspec(revs->prefix, prune_data.path));
 +              parse_pathspec(&revs->prune_data, 0, 0,
 +                             revs->prefix, prune_data.path);
        }
  
        if (revs->def == NULL)
                revs->limited = 1;
  
        if (revs->prune_data.nr) {
 -              diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
 +              copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
                if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                        revs->prune = 1;
                if (!revs->full_diff)
 -                      diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
 +                      copy_pathspec(&revs->diffopt.pathspec,
 +                                    &revs->prune_data);
        }
        if (revs->combine_merges)
                revs->ignore_merges = 0;
@@@ -2780,7 -2809,7 +2826,7 @@@ static int commit_match(struct commit *
        return retval;
  }
  
 -static inline int want_ancestry(struct rev_info *revs)
 +static inline int want_ancestry(const struct rev_info *revs)
  {
        return (revs->rewrite_parents || revs->children.name);
  }
@@@ -2837,14 -2866,6 +2883,14 @@@ enum commit_action simplify_commit(stru
        if (action == commit_show &&
            !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
 +              /*
 +               * --full-diff on simplified parents is no good: it
 +               * will show spurious changes from the commits that
 +               * were elided.  So we save the parents on the side
 +               * when --full-diff is in effect.
 +               */
 +              if (revs->full_diff)
 +                      save_parents(revs, commit);
                if (rewrite_parents(revs, commit, rewrite_one) < 0)
                        return commit_error;
        }
@@@ -2864,7 -2885,6 +2910,7 @@@ static struct commit *get_revision_1(st
                free(entry);
  
                if (revs->reflog_info) {
 +                      save_parents(revs, commit);
                        fake_reflog_parent(revs->reflog_info, commit);
                        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
                }
@@@ -2987,7 -3007,7 +3033,7 @@@ static struct commit *get_revision_inte
        if (revs->max_count) {
                c = get_revision_1(revs);
                if (c) {
 -                      while (0 < revs->skip_count) {
 +                      while (revs->skip_count > 0) {
                                revs->skip_count--;
                                c = get_revision_1(revs);
                                if (!c)
        if (c)
                c->object.flags |= SHOWN;
  
 -      if (!revs->boundary) {
 +      if (!revs->boundary)
                return c;
 -      }
  
        if (!c) {
                /*
@@@ -3049,8 -3070,9 +3095,8 @@@ struct commit *get_revision(struct rev_
  
        if (revs->reverse) {
                reversed = NULL;
 -              while ((c = get_revision_internal(revs))) {
 +              while ((c = get_revision_internal(revs)))
                        commit_list_insert(c, &reversed);
 -              }
                revs->commits = reversed;
                revs->reverse = 0;
                revs->reverse_output_stage = 1;
        c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
 +      if (!c)
 +              free_saved_parents(revs);
        return c;
  }
  
@@@ -3095,54 -3115,3 +3141,54 @@@ void put_revision_mark(const struct rev
        fputs(mark, stdout);
        putchar(' ');
  }
 +
 +define_commit_slab(saved_parents, struct commit_list *);
 +
 +#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
 +
 +void save_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list **pp;
 +
 +      if (!revs->saved_parents_slab) {
 +              revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
 +              init_saved_parents(revs->saved_parents_slab);
 +      }
 +
 +      pp = saved_parents_at(revs->saved_parents_slab, commit);
 +
 +      /*
 +       * When walking with reflogs, we may visit the same commit
 +       * several times: once for each appearance in the reflog.
 +       *
 +       * In this case, save_parents() will be called multiple times.
 +       * We want to keep only the first set of parents.  We need to
 +       * store a sentinel value for an empty (i.e., NULL) parent
 +       * list to distinguish it from a not-yet-saved list, however.
 +       */
 +      if (*pp)
 +              return;
 +      if (commit->parents)
 +              *pp = copy_commit_list(commit->parents);
 +      else
 +              *pp = EMPTY_PARENT_LIST;
 +}
 +
 +struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
 +{
 +      struct commit_list *parents;
 +
 +      if (!revs->saved_parents_slab)
 +              return commit->parents;
 +
 +      parents = *saved_parents_at(revs->saved_parents_slab, commit);
 +      if (parents == EMPTY_PARENT_LIST)
 +              return NULL;
 +      return parents;
 +}
 +
 +void free_saved_parents(struct rev_info *revs)
 +{
 +      if (revs->saved_parents_slab)
 +              clear_saved_parents(revs->saved_parents_slab);
 +}
diff --combined revision.h
index 89132df2fa481ce6cdd142fa31f5b2cfd2f43219,c67c46d716dc99a6236b43f15cf8ea2e64772b7b..88967d6a24df852453f93cb7bc466386347b0c45
@@@ -5,7 -5,6 +5,7 @@@
  #include "grep.h"
  #include "notes.h"
  #include "commit.h"
 +#include "diff.h"
  
  #define SEEN          (1u<<0)
  #define UNINTERESTING   (1u<<1)
@@@ -26,7 -25,6 +26,7 @@@
  struct rev_info;
  struct log_info;
  struct string_list;
 +struct saved_parents;
  
  struct rev_cmdline_info {
        unsigned int nr;
@@@ -61,6 -59,9 +61,9 @@@ struct rev_info 
        /* The end-points specified by the end user */
        struct rev_cmdline_info cmdline;
  
+       /* excluding from --branches, --refs, etc. expansion */
+       struct string_list *ref_excludes;
        /* Basic information */
        const char *prefix;
        const char *def;
  
        /* line level range that we are chasing */
        struct decoration line_log_data;
 +
 +      /* copies of the parent lists, for --full-diff display */
 +      struct saved_parents *saved_parents_slab;
  };
  
+ extern int ref_excluded(struct string_list *, const char *path);
+ void clear_ref_exclusion(struct string_list **);
+ void add_ref_exclusion(struct string_list **, const char *exclude);
  #define REV_TREE_SAME         0
  #define REV_TREE_NEW          1       /* Only new files */
  #define REV_TREE_OLD          2       /* Only files removed */
@@@ -278,20 -281,4 +286,20 @@@ typedef enum rewrite_result (*rewrite_p
  
  extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
        rewrite_parent_fn_t rewrite_parent);
 +
 +/*
 + * Save a copy of the parent list, and return the saved copy.  This is
 + * used by the log machinery to retrieve the original parents when
 + * commit->parents has been modified by history simpification.
 + *
 + * You may only call save_parents() once per commit (this is checked
 + * for non-root commits).
 + *
 + * get_saved_parents() will transparently return commit->parents if
 + * history simplification is off.
 + */
 +extern void save_parents(struct rev_info *revs, struct commit *commit);
 +extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
 +extern void free_saved_parents(struct rev_info *revs);
 +
  #endif