Merge branch 'jk/alt-odb-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:19 +0000 (13:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
Codepaths involved in interacting alternate object store have
been cleaned up.

* jk/alt-odb-cleanup:
alternates: use fspathcmp to detect duplicates
sha1_file: always allow relative paths to alternates
count-objects: report alternates via verbose mode
fill_sha1_file: write into a strbuf
alternates: store scratch buffer as strbuf
fill_sha1_file: write "boring" characters
alternates: use a separate scratch space
alternates: encapsulate alt->base munging
alternates: provide helper for allocating alternate
alternates: provide helper for adding to alternates list
link_alt_odb_entry: refactor string handling
link_alt_odb_entry: handle normalize_path errors
t5613: clarify "too deep" recursion tests
t5613: do not chdir in main process
t5613: whitespace/style cleanups
t5613: use test_must_fail
t5613: drop test_valid_repo function
t5613: drop reachable_via function

93 files changed:
Documentation/RelNotes/2.10.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.11.0.txt
Documentation/blame-options.txt
Documentation/config.txt
Documentation/fetch-options.txt
Documentation/git-blame.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-fetch-pack.txt
Documentation/gitremote-helpers.txt
Documentation/revisions.txt
Documentation/technical/api-sha1-array.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Makefile
bisect.c
builtin/blame.c
builtin/cat-file.c
builtin/clone.c
builtin/describe.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/index-pack.c
builtin/mktree.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/remote.c
builtin/rev-parse.c
builtin/shortlog.c
builtin/show-branch.c
builtin/submodule--helper.c
cache.h
commit.h
config.c
configure.ac
contrib/coccinelle/.gitignore [new file with mode: 0644]
contrib/coccinelle/qsort.cocci [new file with mode: 0644]
contrib/coccinelle/strbuf.cocci
contrib/completion/git-completion.bash
diff.c
diffcore-delta.c
diffcore-order.c
diffcore-rename.c
dir.c
fast-import.c
fetch-pack.c
fetch-pack.h
git-compat-util.h
graph.c
help.c
http.c
imap-send.c
line-log.c
object.h
pack-bitmap-write.c
pack-check.c
pack-objects.h
pack-write.c
pathspec.c
pretty.c
ref-filter.c
refs.c
refs.h
refs/files-backend.c
remote-curl.c
revision.c
server-info.c
sh-i18n--envsubst.c
sha1-array.c
sha1-array.h
sha1_file.c
sha1_name.c
shallow.c
streaming.c
string-list.c
submodule.c
t/helper/test-dump-untracked-cache.c
t/helper/test-sha1-array.c
t/t1512-rev-parse-disambiguation.sh
t/t5314-pack-cycle-detection.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5539-fetch-http-shallow.sh
t/t6006-rev-list-format.sh
t/t6101-rev-parse-parents.sh
transport-helper.c
transport.c
transport.h
tree.c
upload-pack.c
wt-status.c
diff --git a/Documentation/RelNotes/2.10.2.txt b/Documentation/RelNotes/2.10.2.txt
new file mode 100644 (file)
index 0000000..ed2de0d
--- /dev/null
@@ -0,0 +1,45 @@
+Git v2.10.2 Release Notes
+=========================
+
+Fixes since v2.10.1
+-------------------
+
+ * The code that parses the format parameter of for-each-ref command
+   has seen a micro-optimization.
+
+ * The "graph" API used in "git log --graph" miscounted the number of
+   output columns consumed so far when drawing a padding line, which
+   has been fixed; this did not affect any existing code as nobody
+   tried to write anything after the padding on such a line, though.
+
+ * Almost everybody uses DEFAULT_ABBREV to refer to the default
+   setting for the abbreviation, but "git blame" peeked into
+   underlying variable bypassing the macro for no good reason.
+
+ * Doc update to clarify what "log -3 --reverse" does.
+
+ * An author name, that spelled a backslash-quoted double quote in the
+   human readable part "My \"double quoted\" name", was not unquoted
+   correctly while applying a patch from a piece of e-mail.
+
+ * The original command line syntax for "git merge", which was "git
+   merge <msg> HEAD <parent>...", has been deprecated for quite some
+   time, and "git gui" was the last in-tree user of the syntax.  This
+   is finally fixed, so that we can move forward with the deprecation.
+
+ * Codepaths that read from an on-disk loose object were too loose in
+   validating what they are reading is a proper object file and
+   sometimes read past the data they read from the disk, which has
+   been corrected.  H/t to Gustavo Grieco for reporting.
+
+ * "git worktree", even though it used the default_abbrev setting that
+   ought to be affected by core.abbrev configuration variable, ignored
+   the variable setting.  The command has been taught to read the
+   default set of configuration variables to correct this.
+
+ * A low-level function verify_packfile() was meant to show errors
+   that were detected without dying itself, but under some conditions
+   it didn't and died instead, which has been fixed.
+
+
+Also contains minor documentation updates and code clean-ups.
index 0f82a089cd7e2266a1899673157eaead3e22e403..8608e86e4ebb3d86e4af4eba5e974e5194126202 100644 (file)
@@ -67,6 +67,25 @@ UI, Workflows & Features
 
  * "git gui" l10n to Portuguese.
 
+ * When given an abbreviated object name that is not (or more
+   realistically, "no longer") unique, we gave a fatal error
+   "ambiguous argument".  This error is now accompanied by hints that
+   lists the objects that begins with the given prefix.  During the
+   course of development of this new feature, numerous minor bugs were
+   uncovered and corrected, the most notable one of which is that we
+   gave "short SHA1 xxxx is ambiguous." twice without good reason.
+
+ * "git log rev^..rev" is an often-used revision range specification
+   to show what was done on a side branch merged at rev.  This has
+   gained a short-hand "rev^-1".  In general "rev^-$n" is the same as
+   "^rev^$n rev", i.e. what has happened on other branches while the
+   history leading to nth parent was looking the other way.
+
+ * In recent versions of cURL, GSSAPI credential delegation is
+   disabled by default due to CVE-2011-2192; introduce a configuration
+   to selectively allow enabling this.
+   (merge 26a7b23429 ps/http-gssapi-cred-delegation later to maint).
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -114,6 +133,16 @@ Performance, Internal Implementation, Development Support etc.
  * The codepath in "git fsck" to detect malformed tree objects has
    been updated not to die but keep going after detecting them.
 
+ * We call "qsort(array, nelem, sizeof(array[0]), fn)", and most of
+   the time third parameter is redundant.  A new QSORT() macro lets us
+   omit it.
+
+ * "git pack-objects" in a repository with many packfiles used to
+   spend a lot of time looking for/at objects in them; the accesses to
+   the packfiles are now optimized by checking the most-recently-used
+   packfile first.
+   (merge c9af708b1a jk/pack-objects-optim-mru later to maint).
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -155,35 +184,28 @@ notes for details).
    we are sending an object C, we want a tag B that directly points at
    C but also a tag A that points at the tag B.  We used to miss the
    intermediate tag B in some cases.
-   (merge b773dde jk/pack-tag-of-tag later to maint).
 
  * Update Japanese translation for "git-gui".
-   (merge 02748bc sy/git-gui-i18n-ja later to maint).
 
  * "git fetch http::/site/path" did not die correctly and segfaulted
    instead.
-   (merge d63ed6e jk/fix-remote-curl-url-wo-proto later to maint).
 
  * "git commit-tree" stopped reading commit.gpgsign configuration
    variable that was meant for Porcelain "git commit" in Git 2.9; we
    forgot to update "git gui" to look at the configuration to match
    this change.
-   (merge f14a310 js/git-gui-commit-gpgsign later to maint).
 
  * "git add --chmod=+x" added recently lacked documentation, which has
    been corrected.
-   (merge 7ef7903 et/add-chmod-x later to maint).
 
  * "git log --cherry-pick" used to include merge commits as candidates
    to be matched up with other commits, resulting a lot of wasted time.
    The patch-id generation logic has been updated to ignore merges to
    avoid the wastage.
-   (merge 7c81040 jk/patch-ids-no-merges later to maint).
 
  * The http transport (with curl-multi option, which is the default
    these days) failed to remove curl-easy handle from a curlm session,
    which led to unnecessary API failures.
-   (merge 2abc848 ew/http-do-not-forget-to-call-curl-multi-remove-handle later to maint).
 
  * There were numerous corner cases in which the configuration files
    are read and used or not read at all depending on the directory a
@@ -202,13 +224,11 @@ notes for details).
  * Performance tests done via "t/perf" did not use the same set of
    build configuration if the user relied on autoconf generated
    configuration.
-   (merge cd5c281 ks/perf-build-with-autoconf later to maint).
 
  * "git format-patch --base=..." feature that was recently added
    showed the base commit information after "-- " e-mail signature
    line, which turned out to be inconvenient.  The base information
    has been moved above the signature line.
-   (merge 480871e jt/format-patch-base-info-above-sig later to maint).
 
  * More i18n.
    (merge 43073f8 va/i18n later to maint).
@@ -220,13 +240,11 @@ notes for details).
    than nice.  As the underlying commands used inside "git rebase"
    would fail with a more meaningful error message and advice text
    when the bogus ident matters, this extra check was removed.
-   (merge 1e461c4 jk/rebase-i-drop-ident-check later to maint).
 
  * "git gc --aggressive" used to limit the delta-chain length to 250,
    which is way too deep for gaining additional space savings and is
    detrimental for runtime performance.  The limit has been reduced to
    50.
-   (merge 07e7dbf jk/reduce-gc-aggressive-depth later to maint).
 
  * Documentation for individual configuration variables to control use
    of color (like `color.grep`) said that their default value is
@@ -234,7 +252,6 @@ notes for details).
    When we updated the default value for color.ui from 'false' to
    'auto' quite a while ago, all of them broke.  This has been
    corrected.
-   (merge 14d16e2 mm/config-color-ui-default-to-auto later to maint).
 
  * The pretty-format specifier "%C(auto)" used by the "log" family of
    commands to enable coloring of the output is taught to also issue a
@@ -243,7 +260,6 @@ notes for details).
 
  * A shell script example in check-ref-format documentation has been
    fixed.
-   (merge 92dece7 ep/doc-check-ref-format-example later to maint).
 
  * "git checkout <word>" does not follow the usual disambiguation
    rules when the <word> can be both a rev and a path, to allow
@@ -251,7 +267,6 @@ notes for details).
    file 'foo' in the working tree without having to disambiguate.
    This was poorly documented and the check was incorrect when the
    command was run from a subdirectory.
-   (merge b829b94 nd/checkout-disambiguation later to maint).
 
  * Some codepaths in "git diff" used regexec(3) on a buffer that was
    mmap(2)ed, which may not have a terminating NUL, leading to a read
@@ -264,15 +279,12 @@ notes for details).
    internal directory structure we assumed HomeBrew uses, which was a
    no-no.  The procedure has been updated to ask HomeBrew things we
    need to know to fix this.
-   (merge f86f49b ls/travis-homebrew-path-fix later to maint).
 
  * When "git rebase -i" is given a broken instruction, it told the
    user to fix it with "--edit-todo", but didn't say what the step
    after that was (i.e. "--continue").
-   (merge 37875b4 rt/rebase-i-broken-insn-advise later to maint).
 
  * Documentation around tools to import from CVS was fairly outdated.
-   (merge 106b672 jk/doc-cvs-update later to maint).
 
  * "git clone --recurse-submodules" lost the progress eye-candy in
    recent update, which has been corrected.
@@ -292,7 +304,6 @@ notes for details).
  * In the codepath that comes up with the hostname to be used in an
    e-mail when the user didn't tell us, we looked at ai_canonname
    field in struct addrinfo without making sure it is not NULL first.
-   (merge c375a7efa3 jk/ident-ai-canonname-could-be-null later to maint).
 
  * "git worktree", even though it used the default_abbrev setting that
    ought to be affected by core.abbrev configuration variable, ignored
@@ -327,12 +338,52 @@ notes for details).
  * Doc update to clarify what "log -3 --reverse" does.
    (merge 04be69478f pb/rev-list-reverse-with-count later to maint).
 
+ * Almost everybody uses DEFAULT_ABBREV to refer to the default
+   setting for the abbreviation, but "git blame" peeked into
+   underlying variable bypassing the macro for no good reason.
+   (merge 5293284b4d jc/blame-abbrev later to maint).
+
+ * The "graph" API used in "git log --graph" miscounted the number of
+   output columns consumed so far when drawing a padding line, which
+   has been fixed; this did not affect any existing code as nobody
+   tried to write anything after the padding on such a line, though.
+   (merge 1647793524 jk/graph-padding-fix later to maint).
+
+ * The code that parses the format parameter of for-each-ref command
+   has seen a micro-optimization.
+   (merge e94ce1394e sg/ref-filter-parse-optim later to maint).
+
+ * When we started cURL to talk to imap server when a new enough
+   version of cURL library is available, we forgot to explicitly add
+   imap(s):// before the destination.  To some folks, that didn't work
+   and the library tried to make HTTP(s) requests instead.
+   (merge d2d07ab861 ak/curl-imap-send-explicit-scheme later to maint).
+
+ * The ./configure script generated from configure.ac was taught how
+   to detect support of SSL by libcurl better.
+   (merge 924b7eb1c9 dp/autoconf-curl-ssl later to maint).
+
+ * The command-line completion script (in contrib/) learned to
+   complete "git cmd ^mas<HT>" to complete the negative end of
+   reference to "git cmd ^master".
+   (merge 49416ad22a cp/completion-negative-refs later to maint).
+
+ * The existing "git fetch --depth=<n>" option was hard to use
+   correctly when making the history of an existing shallow clone
+   deeper.  A new option, "--deepen=<n>", has been added to make this
+   easier to use.  "git clone" also learned "--shallow-since=<date>"
+   and "--shallow-exclude=<tag>" options to make it easier to specify
+   "I am interested only in the recent N months worth of history" and
+   "Give me only the history since that version".
+   (merge cccf74e2da nd/shallow-deepen later to maint).
+
+ * It is a common mistake to say "git blame --reverse OLD path",
+   expecting that the command line is dwimmed as if asking how lines
+   in path in an old revision OLD have survived up to the current
+   commit.
+   (merge e1d09701a4 jc/blame-reverse later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
-   (merge e78d57e bw/pathspec-remove-unused-extern-decl later to maint).
-   (merge ce25e4c rs/checkout-some-states-are-const later to maint).
-   (merge a8342a4 rs/strbuf-remove-fix later to maint).
-   (merge b56aa5b rs/unpack-trees-reduce-file-scope-global later to maint).
-   (merge 5efc60c mr/vcs-svn-printf-ulong later to maint).
    (merge a22ae75 rs/cocci later to maint).
    (merge 45ccef87b3 rs/copy-array later to maint).
    (merge 8201688ecd dt/mailinfo later to maint).
index 02cb6845cd96194b655c9898189c4d5f24ca6a92..2669b87c9d59a80e5e4f3d0ade1893ec8e5b3f30 100644 (file)
@@ -28,12 +28,13 @@ include::line-range-format.txt[]
 -S <revs-file>::
        Use revisions from revs-file instead of calling linkgit:git-rev-list[1].
 
---reverse::
+--reverse <rev>..<rev>::
        Walk history forward instead of backward. Instead of showing
        the revision in which a line appeared, this shows the last
        revision in which a line has existed. This requires a range of
        revision like START..END where the path to blame exists in
-       START.
+       START.  `git blame --reverse START` is taken as `git blame
+       --reverse START..HEAD` for convenience.
 
 -p::
 --porcelain::
index e78293b6dbe31cd254cd64c94a045cfe9a2877a9..a17947462a931d70700461c8a1a7a32b20bd3676 100644 (file)
@@ -1736,6 +1736,20 @@ http.emptyAuth::
        a username in the URL, as libcurl normally requires a username for
        authentication.
 
+http.delegation::
+       Control GSSAPI credential delegation. The delegation is disabled
+       by default in libcurl since version 7.21.7. Set parameter to tell
+       the server what it is allowed to delegate when it comes to user
+       credentials. Used with GSS/kerberos. Possible values are:
++
+--
+* `none` - Don't allow any delegation.
+* `policy` - Delegates if and only if the OK-AS-DELEGATE flag is set in the
+  Kerberos service ticket, which is a matter of realm policy.
+* `always` - Unconditionally allow the server to delegate.
+--
+
+
 http.extraHeader::
        Pass an additional HTTP header when communicating with a server.  If
        more than one such entry exists, all of them are added as extra
index 9eab1f5fa42859651c6a0928ae0434799d5ce30e..fb6bebbc618c3f71ab400ff4267264a0167a887a 100644 (file)
        linkgit:git-clone[1]), deepen or shorten the history to the specified
        number of commits. Tags for the deepened commits are not fetched.
 
+--deepen=<depth>::
+       Similar to --depth, except it specifies the number of commits
+       from the current shallow boundary instead of from the tip of
+       each remote branch history.
+
+--shallow-since=<date>::
+       Deepen or shorten the history of a shallow repository to
+       include all reachable commits after <date>.
+
+--shallow-exclude=<revision>::
+       Deepen or shorten the history of a shallow repository to
+       exclude commits reachable from a specified remote branch or tag.
+       This option can be specified multiple times.
+
 --unshallow::
        If the source repository is complete, convert a shallow
        repository to a complete one, removing all the limitations
index 9dccb3319b2f5faa0ca7fd36b365f30cce5d3e89..fdc3aea30a4cdb480d469455a66e51a76366be93 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
            [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
-           [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>]
+           [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
            [--] <file>
 
 DESCRIPTION
index e316c4bd51a64fb9df87ce003f2edffad38b63ad..35cc34b2fb9a0e696eb86e208416fa93aae3c0c5 100644 (file)
@@ -197,6 +197,14 @@ objects from the source repository into a pack in the cloned repository.
        tips of all branches. If you want to clone submodules shallowly,
        also pass `--shallow-submodules`.
 
+--shallow-since=<date>::
+       Create a shallow clone with a history after the specified time.
+
+--shallow-exclude=<revision>::
+       Create a shallow clone with a history, excluding commits
+       reachable from a specified remote branch or tag.  This option
+       can be specified multiple times.
+
 --[no-]single-branch::
        Clone only the history leading to the tip of a single branch,
        either specified by the `--branch` option or the primary
index b0a294d3b5e13b853f6c2c60201c2458788d4f00..f2ab0ee2e7d1ff0f79c09cbd27e745f5f08d139d 100644 (file)
@@ -29,7 +29,8 @@ The content to be added can be specified in several ways:
 2. by using 'git rm' to remove files from the working tree
    and the index, again before using the 'commit' command;
 
-3. by listing files as arguments to the 'commit' command, in which
+3. by listing files as arguments to the 'commit' command
+   (without --interactive or --patch switch), in which
    case the commit will ignore changes staged in the index, and instead
    record the current content of the listed files (which must already
    be known to Git);
@@ -41,7 +42,8 @@ The content to be added can be specified in several ways:
    actual commit;
 
 5. by using the --interactive or --patch switches with the 'commit' command
-   to decide one by one which files or hunks should be part of the commit,
+   to decide one by one which files or hunks should be part of the commit
+   in addition to contents in the index,
    before finalizing the operation. See the ``Interactive Mode'' section of
    linkgit:git-add[1] to learn how to operate these modes.
 
index 24417ee3a66218c9033f450ca07c7b26a3bcc018..d45f6adc69fef8d99d9297c04bba532cd79a1c71 100644 (file)
@@ -87,6 +87,20 @@ be in a separate packet, and the list must end with a flush packet.
        'git-upload-pack' treats the special depth 2147483647 as
        infinite even if there is an ancestor-chain that long.
 
+--shallow-since=<date>::
+       Deepen or shorten the history of a shallow'repository to
+       include all reachable commits after <date>.
+
+--shallow-exclude=<revision>::
+       Deepen or shorten the history of a shallow repository to
+       exclude commits reachable from a specified remote branch or tag.
+       This option can be specified multiple times.
+
+--deepen-relative::
+       Argument --depth specifies the number of commits from the
+       current shallow boundary instead of from the tip of each
+       remote branch history.
+
 --no-progress::
        Do not show the progress.
 
index a4de50ad227bb1653977ee9502a3df20afefaf92..9e8681f9e1b554113e7d1881e214bc617af14109 100644 (file)
@@ -415,6 +415,17 @@ set by Git if the remote helper has the 'option' capability.
 'option depth' <depth>::
        Deepens the history of a shallow repository.
 
+'option deepen-since <timestamp>::
+       Deepens the history of a shallow repository based on time.
+
+'option deepen-not <ref>::
+       Deepens the history of a shallow repository excluding ref.
+       Multiple options add up.
+
+'option deepen-relative {'true'|'false'}::
+       Deepens the history of a shallow repository relative to
+       current boundary. Only valid when used with "option depth".
+
 'option followtags' {'true'|'false'}::
        If enabled the helper should automatically fetch annotated
        tag objects if the object the tag points at was transferred
index 4bed5b1ab741d9c86f5a9443f980080a56b84629..ba11b9c95e3a6efef461e5cadc4834ce232896b1 100644 (file)
@@ -283,7 +283,7 @@ empty range that is both reachable and unreachable from HEAD.
 
 Other <rev>{caret} Parent Shorthand Notations
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Two other shorthands exist, particularly useful for merge commits,
+Three other shorthands exist, particularly useful for merge commits,
 for naming a set that is formed by a commit and its parent commits.
 
 The 'r1{caret}@' notation means all parents of 'r1'.
@@ -291,8 +291,15 @@ The 'r1{caret}@' notation means all parents of 'r1'.
 The 'r1{caret}!' notation includes commit 'r1' but excludes all of its parents.
 By itself, this notation denotes the single commit 'r1'.
 
+The '<rev>{caret}-{<n>}' notation includes '<rev>' but excludes the <n>th
+parent (i.e. a shorthand for '<rev>{caret}<n>..<rev>'), with '<n>' = 1 if
+not given. This is typically useful for merge commits where you
+can just pass '<commit>{caret}-' to get all the commits in the branch
+that was merged in merge commit '<commit>' (including '<commit>'
+itself).
+
 While '<rev>{caret}<n>' was about specifying a single commit parent, these
-two notations consider all its parents. For example you can say
+three notations also consider its parents. For example you can say
 'HEAD{caret}2{caret}@', however you cannot say 'HEAD{caret}@{caret}2'.
 
 Revision Range Summary
@@ -326,6 +333,10 @@ Revision Range Summary
   as giving commit '<rev>' and then all its parents prefixed with
   '{caret}' to exclude them (and their ancestors).
 
+'<rev>{caret}-{<n>}', e.g. 'HEAD{caret}-, HEAD{caret}-2'::
+       Equivalent to '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not
+       given.
+
 Here are a handful of examples using the Loeliger illustration above,
 with each step in the notation's expansion and selection carefully
 spelt out:
@@ -339,6 +350,8 @@ spelt out:
    C                            I J F C
    B..C   = ^B C                C
    B...C  = B ^F C              G H D E B C
+   B^-    = B^..B
+         = ^B^1 B              E I J F B
    C^@    = C^1
          = F                   I J F
    B^@    = B^1 B^2 B^3
index 3e75497a37d696d534c16ff0270a808b57ac9180..dcc52943a5da7dbbfe8d42cdcc7d593ec1ba1416 100644 (file)
@@ -38,16 +38,20 @@ Functions
 `sha1_array_for_each_unique`::
        Efficiently iterate over each unique element of the list,
        executing the callback function for each one. If the array is
-       not sorted, this function has the side effect of sorting it.
+       not sorted, this function has the side effect of sorting it. If
+       the callback returns a non-zero value, the iteration ends
+       immediately and the callback's return is propagated; otherwise,
+       0 is returned.
 
 Examples
 --------
 
 -----------------------------------------
-void print_callback(const unsigned char sha1[20],
+int print_callback(const unsigned char sha1[20],
                    void *data)
 {
        printf("%s\n", sha1_to_hex(sha1));
+       return 0; /* always continue */
 }
 
 void some_func(void)
index 736f3894a85602c10e0bf13fb74eb7f556207672..c59ac9936a89e5e195b9a75bd4fd5d64a2385ffa 100644 (file)
@@ -219,7 +219,9 @@ out of what the server said it could do with the first 'want' line.
 
   shallow-line      =  PKT-LINE("shallow" SP obj-id)
 
-  depth-request     =  PKT-LINE("deepen" SP depth)
+  depth-request     =  PKT-LINE("deepen" SP depth) /
+                      PKT-LINE("deepen-since" SP timestamp) /
+                      PKT-LINE("deepen-not" SP ref)
 
   first-want        =  PKT-LINE("want" SP obj-id SP capability-list)
   additional-want   =  PKT-LINE("want" SP obj-id)
index 4c28d3a8aea3312190d859e4accb70f9d28973bf..26dcc6f502020da5214f1e161f1678813dd24059 100644 (file)
@@ -179,6 +179,31 @@ This capability adds "deepen", "shallow" and "unshallow" commands to
 the  fetch-pack/upload-pack protocol so clients can request shallow
 clones.
 
+deepen-since
+------------
+
+This capability adds "deepen-since" command to fetch-pack/upload-pack
+protocol so the client can request shallow clones that are cut at a
+specific time, instead of depth. Internally it's equivalent of doing
+"rev-list --max-age=<timestamp>" on the server side. "deepen-since"
+cannot be used with "deepen".
+
+deepen-not
+----------
+
+This capability adds "deepen-not" command to fetch-pack/upload-pack
+protocol so the client can request shallow clones that are cut at a
+specific revision, instead of depth. Internally it's equivalent of
+doing "rev-list --not <rev>" on the server side. "deepen-not"
+cannot be used with "deepen", but can be used with "deepen-since".
+
+deepen-relative
+---------------
+
+If this capability is requested by the client, the semantics of
+"deepen" command is changed. The "depth" argument is the depth from
+the current shallow boundary, instead of the depth from remote refs.
+
 no-progress
 -----------
 
index 1aad150b34f7f2e84bea104538033443644af3a2..d15bf8de9d36065865781caf5adb069c25657432 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -467,6 +467,7 @@ SPATCH = spatch
 export TCL_PATH TCLTK_PATH
 
 SPARSE_FLAGS =
+SPATCH_FLAGS = --all-includes
 
 
 
@@ -2314,7 +2315,7 @@ C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ))
 %.cocci.patch: %.cocci $(C_SOURCES)
        @echo '    ' SPATCH $<; \
        for f in $(C_SOURCES); do \
-               $(SPATCH) --sp-file $< $$f; \
+               $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS); \
        done >$@ 2>$@.log; \
        if test -s $@; \
        then \
index 6f512c20638718df91ed4a29aae3518f985c0d30..21bc6daa4393cb33994ba2eb305354e56d166cc0 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -215,7 +215,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
                array[cnt].distance = distance;
                cnt++;
        }
-       qsort(array, cnt, sizeof(*array), compare_commit_dist);
+       QSORT(array, cnt, compare_commit_dist);
        for (p = list, i = 0; i < cnt; i++) {
                char buf[100]; /* enough for dist=%d */
                struct object *obj = &(array[i].commit->object);
index a7bd7a6fd80f7d68f58d094bdc5cd6c288fb0e29..4ddfadb71f7ef93958e2f236e166212e8b6bda2b 100644 (file)
@@ -2111,7 +2111,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
        unsigned largest_score = 0;
        struct blame_entry *e;
        int compute_auto_abbrev = (abbrev < 0);
-       int auto_abbrev = default_abbrev;
+       int auto_abbrev = DEFAULT_ABBREV;
 
        for (e = sb->ent; e; e = e->next) {
                struct origin *suspect = e->suspect;
@@ -2456,6 +2456,41 @@ static char *prepare_final(struct scoreboard *sb)
        return xstrdup_or_null(name);
 }
 
+static const char *dwim_reverse_initial(struct scoreboard *sb)
+{
+       /*
+        * DWIM "git blame --reverse ONE -- PATH" as
+        * "git blame --reverse ONE..HEAD -- PATH" but only do so
+        * when it makes sense.
+        */
+       struct object *obj;
+       struct commit *head_commit;
+       unsigned char head_sha1[20];
+
+       if (sb->revs->pending.nr != 1)
+               return NULL;
+
+       /* Is that sole rev a committish? */
+       obj = sb->revs->pending.objects[0].item;
+       obj = deref_tag(obj, NULL, 0);
+       if (obj->type != OBJ_COMMIT)
+               return NULL;
+
+       /* Do we have HEAD? */
+       if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
+               return NULL;
+       head_commit = lookup_commit_reference_gently(head_sha1, 1);
+       if (!head_commit)
+               return NULL;
+
+       /* Turn "ONE" into "ONE..HEAD" then */
+       obj->flags |= UNINTERESTING;
+       add_pending_object(sb->revs, &head_commit->object, "HEAD");
+
+       sb->final = (struct commit *)obj;
+       return sb->revs->pending.objects[0].name;
+}
+
 static char *prepare_initial(struct scoreboard *sb)
 {
        int i;
@@ -2474,14 +2509,17 @@ static char *prepare_initial(struct scoreboard *sb)
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (sb->final)
-                       die("More than one commit to dig down to %s and %s?",
+                       die("More than one commit to dig up from, %s and %s?",
                            revs->pending.objects[i].name,
                            final_commit_name);
                sb->final = (struct commit *) obj;
                final_commit_name = revs->pending.objects[i].name;
        }
+
+       if (!final_commit_name)
+               final_commit_name = dwim_reverse_initial(sb);
        if (!final_commit_name)
-               die("No commit to dig down to?");
+               die("No commit to dig up from?");
        return xstrdup(final_commit_name);
 }
 
index 94e67ebb7eec087390cfecc5c4371a974401d602..30383e9eb4befb31e0d165d5863615484bcc9da3 100644 (file)
@@ -53,7 +53,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
        char *buf;
        unsigned long size;
        struct object_context obj_context;
-       struct object_info oi = {NULL};
+       struct object_info oi = OBJECT_INFO_INIT;
        struct strbuf sb = STRBUF_INIT;
        unsigned flags = LOOKUP_REPLACE_OBJECT;
        const char *path = force_path;
@@ -401,11 +401,12 @@ struct object_cb_data {
        struct expand_data *expand;
 };
 
-static void batch_object_cb(const unsigned char sha1[20], void *vdata)
+static int batch_object_cb(const unsigned char sha1[20], void *vdata)
 {
        struct object_cb_data *data = vdata;
        hashcpy(data->expand->oid.hash, sha1);
        batch_object_write(NULL, data->opt, data->expand);
+       return 0;
 }
 
 static int batch_loose_object(const unsigned char *sha1,
@@ -448,8 +449,7 @@ static int batch_objects(struct batch_options *opt)
                data.split_on_whitespace = 1;
 
        if (opt->all_objects) {
-               struct object_info empty;
-               memset(&empty, 0, sizeof(empty));
+               struct object_info empty = OBJECT_INFO_INIT;
                if (!memcmp(&data.info, &empty, sizeof(empty)))
                        data.skip_object_info = 1;
        }
index fb75f7ee64a29e4bb79f163e82c73bb8a7bd5280..6c76a6ed66fef567ca06e3e864b37fc3bed151d5 100644 (file)
@@ -41,9 +41,11 @@ static const char * const builtin_clone_usage[] = {
 static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
 static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
 static int option_shallow_submodules;
-static char *option_template, *option_depth;
+static int deepen;
+static char *option_template, *option_depth, *option_since;
 static char *option_origin = NULL;
 static char *option_branch = NULL;
+static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
@@ -94,6 +96,10 @@ static struct option builtin_clone_options[] = {
                   N_("path to git-upload-pack on the remote")),
        OPT_STRING(0, "depth", &option_depth, N_("depth"),
                    N_("create a shallow clone of that depth")),
+       OPT_STRING(0, "shallow-since", &option_since, N_("time"),
+                   N_("create a shallow clone since a specific time")),
+       OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
+                       N_("deepen history of shallow clone by excluding rev")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
        OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
@@ -345,8 +351,11 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
                        continue;
                }
                abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
-               normalize_path_copy(abs_path, abs_path);
-               add_to_alternates_file(abs_path);
+               if (!normalize_path_copy(abs_path, abs_path))
+                       add_to_alternates_file(abs_path);
+               else
+                       warning("skipping invalid relative alternate: %s/%s",
+                               src_repo, line.buf);
                free(abs_path);
        }
        strbuf_release(&line);
@@ -861,8 +870,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                usage_msg_opt(_("You must specify a repository to clone."),
                        builtin_clone_usage, builtin_clone_options);
 
+       if (option_depth || option_since || option_not.nr)
+               deepen = 1;
        if (option_single_branch == -1)
-               option_single_branch = option_depth ? 1 : 0;
+               option_single_branch = deepen ? 1 : 0;
 
        if (option_mirror)
                option_bare = 1;
@@ -1006,6 +1017,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (is_local) {
                if (option_depth)
                        warning(_("--depth is ignored in local clones; use file:// instead."));
+               if (option_since)
+                       warning(_("--shallow-since is ignored in local clones; use file:// instead."));
+               if (option_not.nr)
+                       warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
                if (!access(mkpath("%s/shallow", path), F_OK)) {
                        if (option_local > 0)
                                warning(_("source repository is shallow, ignoring --local"));
@@ -1024,6 +1039,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_depth)
                transport_set_option(transport, TRANS_OPT_DEPTH,
                                     option_depth);
+       if (option_since)
+               transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,
+                                    option_since);
+       if (option_not.nr)
+               transport_set_option(transport, TRANS_OPT_DEEPEN_NOT,
+                                    (const char *)&option_not);
        if (option_single_branch)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 
@@ -1031,7 +1052,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
 
-       if (transport->smart_options && !option_depth)
+       if (transport->smart_options && !deepen)
                transport->smart_options->check_self_contained_and_connected = 1;
 
        refs = transport_get_remote_refs(transport);
index 8a25abe0a03e2b2463ac352daafc326c4322ac32..01490a157efc5d85626833c0b1c25b75fb7ee469 100644 (file)
@@ -352,7 +352,7 @@ static void describe(const char *arg, int last_one)
                            oid_to_hex(oid));
        }
 
-       qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
+       QSORT(all_matches, match_cnt, compare_pt);
 
        if (gave_up_on) {
                commit_list_insert_by_date(gave_up_on, &list);
index c0652a7ed0a928d43db21dac82bc440d268ed194..1e815b5577cc05a6133ded56dda4b84596f80514 100644 (file)
@@ -347,7 +347,7 @@ static void show_filemodify(struct diff_queue_struct *q,
         * Handle files below a directory first, in case they are all deleted
         * and the directory changes to a file or symlink.
         */
-       qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+       QSORT(q->queue, q->nr, depth_first);
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *ospec = q->queue[i]->one;
index bfd0be44a91f66034c98361dc713cc0883693a69..cfe9e447c27469407ab439bacb540a4d0b68d4b4 100644 (file)
@@ -51,6 +51,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct child_process *conn;
        struct fetch_pack_args args;
        struct sha1_array shallow = SHA1_ARRAY_INIT;
+       struct string_list deepen_not = STRING_LIST_INIT_DUP;
 
        packet_trace_identity("fetch-pack");
 
@@ -60,12 +61,12 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc && *argv[i] == '-'; i++) {
                const char *arg = argv[i];
 
-               if (starts_with(arg, "--upload-pack=")) {
-                       args.uploadpack = arg + 14;
+               if (skip_prefix(arg, "--upload-pack=", &arg)) {
+                       args.uploadpack = arg;
                        continue;
                }
-               if (starts_with(arg, "--exec=")) {
-                       args.uploadpack = arg + 7;
+               if (skip_prefix(arg, "--exec=", &arg)) {
+                       args.uploadpack = arg;
                        continue;
                }
                if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
@@ -101,8 +102,20 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        args.verbose = 1;
                        continue;
                }
-               if (starts_with(arg, "--depth=")) {
-                       args.depth = strtol(arg + 8, NULL, 0);
+               if (skip_prefix(arg, "--depth=", &arg)) {
+                       args.depth = strtol(arg, NULL, 0);
+                       continue;
+               }
+               if (skip_prefix(arg, "--shallow-since=", &arg)) {
+                       args.deepen_since = xstrdup(arg);
+                       continue;
+               }
+               if (skip_prefix(arg, "--shallow-exclude=", &arg)) {
+                       string_list_append(&deepen_not, arg);
+                       continue;
+               }
+               if (!strcmp(arg, "--deepen-relative")) {
+                       args.deepen_relative = 1;
                        continue;
                }
                if (!strcmp("--no-progress", arg)) {
@@ -132,6 +145,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                }
                usage(fetch_pack_usage);
        }
+       if (deepen_not.nr)
+               args.deepen_not = &deepen_not;
 
        if (i < argc)
                dest = argv[i++];
index 164623bb6f2eb3ffad3eac59b8ec440b9cf60d9c..d5329f915e57225e5046f33fae89f6348263f191 100644 (file)
@@ -35,13 +35,15 @@ static int fetch_prune_config = -1; /* unspecified */
 static int prune = -1; /* unspecified */
 #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
 
-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
 static int max_children = -1;
 static enum transport_family family;
 static const char *depth;
+static const char *deepen_since;
 static const char *upload_pack;
+static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
 static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *gtransport;
 static struct transport *gsecondary;
@@ -117,6 +119,12 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
        OPT_STRING(0, "depth", &depth, N_("depth"),
                   N_("deepen history of shallow clone")),
+       OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+                  N_("deepen history of shallow repository based on time")),
+       OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+                       N_("deepen history of shallow clone by excluding rev")),
+       OPT_INTEGER(0, "deepen", &deepen_relative,
+                   N_("deepen history of shallow clone")),
        { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
                   N_("convert to a complete repository"),
                   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
@@ -875,7 +883,7 @@ static int quickfetch(struct ref *ref_map)
         * really need to perform.  Claiming failure now will ensure
         * we perform the network exchange to deepen our history.
         */
-       if (depth)
+       if (deepen)
                return -1;
        opt.quiet = 1;
        return check_connected(iterate_ref_map, &rm, &opt);
@@ -983,7 +991,7 @@ static void set_option(struct transport *transport, const char *name, const char
                        name, transport->url);
 }
 
-static struct transport *prepare_transport(struct remote *remote)
+static struct transport *prepare_transport(struct remote *remote, int deepen)
 {
        struct transport *transport;
        transport = transport_get(remote, NULL);
@@ -995,6 +1003,13 @@ static struct transport *prepare_transport(struct remote *remote)
                set_option(transport, TRANS_OPT_KEEP, "yes");
        if (depth)
                set_option(transport, TRANS_OPT_DEPTH, depth);
+       if (deepen && deepen_since)
+               set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
+       if (deepen && deepen_not.nr)
+               set_option(transport, TRANS_OPT_DEEPEN_NOT,
+                          (const char *)&deepen_not);
+       if (deepen_relative)
+               set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        return transport;
@@ -1002,13 +1017,25 @@ static struct transport *prepare_transport(struct remote *remote)
 
 static void backfill_tags(struct transport *transport, struct ref *ref_map)
 {
-       if (transport->cannot_reuse) {
-               gsecondary = prepare_transport(transport->remote);
+       int cannot_reuse;
+
+       /*
+        * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
+        * when remote helper is used (setting it to an empty string
+        * is not unsetting). We could extend the remote helper
+        * protocol for that, but for now, just force a new connection
+        * without deepen-since. Similar story for deepen-not.
+        */
+       cannot_reuse = transport->cannot_reuse ||
+               deepen_since || deepen_not.nr;
+       if (cannot_reuse) {
+               gsecondary = prepare_transport(transport->remote, 0);
                transport = gsecondary;
        }
 
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+       transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
        fetch_refs(transport, ref_map);
 
        if (gsecondary) {
@@ -1219,7 +1246,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
                    "remote name from which new revisions should be fetched."));
 
-       gtransport = prepare_transport(remote);
+       gtransport = prepare_transport(remote, 1);
 
        if (prune < 0) {
                /* no command line request */
@@ -1279,6 +1306,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
 
+       if (deepen_relative) {
+               if (deepen_relative < 0)
+                       die(_("Negative depth in --deepen is not supported"));
+               if (depth)
+                       die(_("--deepen and --depth are mutually exclusive"));
+               depth = xstrfmt("%d", deepen_relative);
+       }
        if (unshallow) {
                if (depth)
                        die(_("--depth and --unshallow cannot be used together"));
@@ -1291,6 +1325,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        /* no need to be strict, transport_set_option() will validate it again */
        if (depth && atoi(depth) < 1)
                die(_("depth %s is not a positive number"), depth);
+       if (depth || deepen_since || deepen_not.nr)
+               deepen = 1;
 
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
index dc2e9e420d6beb47f0e04184dfb7429b6559a789..efab62fd85e314ccb6ec5becedead91823d03dde 100644 (file)
@@ -314,14 +314,10 @@ static void add_people_info(struct strbuf *out,
                            struct string_list *authors,
                            struct string_list *committers)
 {
-       if (authors->nr)
-               qsort(authors->items,
-                     authors->nr, sizeof(authors->items[0]),
-                     cmp_string_list_util_as_integral);
-       if (committers->nr)
-               qsort(committers->items,
-                     committers->nr, sizeof(committers->items[0]),
-                     cmp_string_list_util_as_integral);
+       QSORT(authors->items, authors->nr,
+             cmp_string_list_util_as_integral);
+       QSORT(committers->items, committers->nr,
+             cmp_string_list_util_as_integral);
 
        credit_people(out, authors, 'a');
        credit_people(out, committers, 'c');
index 4a8b4aebbac7d3992082b4440957f11aeffc39f5..0a27bab11b6b5a1b162c4dfef34c645c7d5f14ad 100644 (file)
@@ -1190,10 +1190,8 @@ static void resolve_deltas(void)
                return;
 
        /* Sort deltas by base SHA1/offset for fast searching */
-       qsort(ofs_deltas, nr_ofs_deltas, sizeof(struct ofs_delta_entry),
-             compare_ofs_delta_entry);
-       qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
-             compare_ref_delta_entry);
+       QSORT(ofs_deltas, nr_ofs_deltas, compare_ofs_delta_entry);
+       QSORT(ref_deltas, nr_ref_deltas, compare_ref_delta_entry);
 
        if (verbose || show_resolving_progress)
                progress = start_progress(_("Resolving deltas"),
@@ -1356,7 +1354,7 @@ static void fix_unresolved_deltas(struct sha1file *f)
        ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas);
        for (i = 0; i < nr_ref_deltas; i++)
                sorted_by_pos[i] = &ref_deltas[i];
-       qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare);
+       QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
        for (i = 0; i < nr_ref_deltas; i++) {
                struct ref_delta_entry *d = sorted_by_pos[i];
@@ -1533,8 +1531,7 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
                opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]);
        }
 
-       if (1 < opts->anomaly_nr)
-               qsort(opts->anomaly, opts->anomaly_nr, sizeof(uint32_t), cmp_uint32);
+       QSORT(opts->anomaly, opts->anomaly_nr, cmp_uint32);
 }
 
 static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
index 4282b62c595edd987a87eb23ba679079211e0835..de9b40fc63b0a4f76401a111aba806c64ce36ee9 100644 (file)
@@ -46,7 +46,7 @@ static void write_tree(unsigned char *sha1)
        size_t size;
        int i;
 
-       qsort(entries, used, sizeof(*entries), ent_compare);
+       QSORT(entries, used, ent_compare);
        for (size = i = 0; i < used; i++)
                size += 32 + entries[i]->len;
 
index 57be35faf583d5bcb112c9f487b67ae4df8bf368..cd89d48b65e8f70819f73ef6e2cfd0634752b284 100644 (file)
@@ -195,8 +195,7 @@ static const char *get_exact_ref_match(const struct object *o)
                return NULL;
 
        if (!tip_table.sorted) {
-               qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
-                     tipcmp);
+               QSORT(tip_table.table, tip_table.nr, tipcmp);
                tip_table.sorted = 1;
        }
 
index 166e52c700f5c89eca6287741ad85912c9c91741..1e7c2a98a5617b8b42422c22051c5f61b2510751 100644 (file)
@@ -23,6 +23,7 @@
 #include "reachable.h"
 #include "sha1-array.h"
 #include "argv-array.h"
+#include "mru.h"
 
 static const char *pack_usage[] = {
        N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@ -994,7 +995,7 @@ static int want_object_in_pack(const unsigned char *sha1,
                               struct packed_git **found_pack,
                               off_t *found_offset)
 {
-       struct packed_git *p;
+       struct mru_entry *entry;
        int want;
 
        if (!exclude && local && has_loose_object_nonlocal(sha1))
@@ -1011,7 +1012,8 @@ static int want_object_in_pack(const unsigned char *sha1,
                        return want;
        }
 
-       for (p = packed_git; p; p = p->next) {
+       for (entry = packed_git_mru->head; entry; entry = entry->next) {
+               struct packed_git *p = entry->item;
                off_t offset;
 
                if (p == *found_pack)
@@ -1027,6 +1029,8 @@ static int want_object_in_pack(const unsigned char *sha1,
                                *found_pack = p;
                        }
                        want = want_found_object(exclude, p);
+                       if (!exclude && want > 0)
+                               mru_mark(packed_git_mru, entry);
                        if (want != -1)
                                return want;
                }
@@ -1527,6 +1531,83 @@ static int pack_offset_sort(const void *_a, const void *_b)
                        (a->in_pack_offset > b->in_pack_offset);
 }
 
+/*
+ * Drop an on-disk delta we were planning to reuse. Naively, this would
+ * just involve blanking out the "delta" field, but we have to deal
+ * with some extra book-keeping:
+ *
+ *   1. Removing ourselves from the delta_sibling linked list.
+ *
+ *   2. Updating our size/type to the non-delta representation. These were
+ *      either not recorded initially (size) or overwritten with the delta type
+ *      (type) when check_object() decided to reuse the delta.
+ */
+static void drop_reused_delta(struct object_entry *entry)
+{
+       struct object_entry **p = &entry->delta->delta_child;
+       struct object_info oi = OBJECT_INFO_INIT;
+
+       while (*p) {
+               if (*p == entry)
+                       *p = (*p)->delta_sibling;
+               else
+                       p = &(*p)->delta_sibling;
+       }
+       entry->delta = NULL;
+
+       oi.sizep = &entry->size;
+       oi.typep = &entry->type;
+       if (packed_object_info(entry->in_pack, entry->in_pack_offset, &oi) < 0) {
+               /*
+                * We failed to get the info from this pack for some reason;
+                * fall back to sha1_object_info, which may find another copy.
+                * And if that fails, the error will be recorded in entry->type
+                * and dealt with in prepare_pack().
+                */
+               entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+       }
+}
+
+/*
+ * Follow the chain of deltas from this entry onward, throwing away any links
+ * that cause us to hit a cycle (as determined by the DFS state flags in
+ * the entries).
+ */
+static void break_delta_chains(struct object_entry *entry)
+{
+       /* If it's not a delta, it can't be part of a cycle. */
+       if (!entry->delta) {
+               entry->dfs_state = DFS_DONE;
+               return;
+       }
+
+       switch (entry->dfs_state) {
+       case DFS_NONE:
+               /*
+                * This is the first time we've seen the object. We mark it as
+                * part of the active potential cycle and recurse.
+                */
+               entry->dfs_state = DFS_ACTIVE;
+               break_delta_chains(entry->delta);
+               entry->dfs_state = DFS_DONE;
+               break;
+
+       case DFS_DONE:
+               /* object already examined, and not part of a cycle */
+               break;
+
+       case DFS_ACTIVE:
+               /*
+                * We found a cycle that needs broken. It would be correct to
+                * break any link in the chain, but it's convenient to
+                * break this one.
+                */
+               drop_reused_delta(entry);
+               entry->dfs_state = DFS_DONE;
+               break;
+       }
+}
+
 static void get_object_details(void)
 {
        uint32_t i;
@@ -1535,7 +1616,7 @@ static void get_object_details(void)
        sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));
        for (i = 0; i < to_pack.nr_objects; i++)
                sorted_by_offset[i] = to_pack.objects + i;
-       qsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);
+       QSORT(sorted_by_offset, to_pack.nr_objects, pack_offset_sort);
 
        for (i = 0; i < to_pack.nr_objects; i++) {
                struct object_entry *entry = sorted_by_offset[i];
@@ -1544,6 +1625,13 @@ static void get_object_details(void)
                        entry->no_try_delta = 1;
        }
 
+       /*
+        * This must happen in a second pass, since we rely on the delta
+        * information for the whole list being completed.
+        */
+       for (i = 0; i < to_pack.nr_objects; i++)
+               break_delta_chains(&to_pack.objects[i]);
+
        free(sorted_by_offset);
 }
 
@@ -2257,7 +2345,7 @@ static void prepare_pack(int window, int depth)
                if (progress)
                        progress_state = start_progress(_("Compressing objects"),
                                                        nr_deltas);
-               qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
+               QSORT(delta_list, n, type_size_sort);
                ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
                stop_progress(&progress_state);
                if (nr_done != nr_deltas)
@@ -2449,8 +2537,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        }
 
        if (in_pack.nr) {
-               qsort(in_pack.array, in_pack.nr, sizeof(in_pack.array[0]),
-                     ofscmp);
+               QSORT(in_pack.array, in_pack.nr, ofscmp);
                for (i = 0; i < in_pack.nr; i++) {
                        struct object *o = in_pack.array[i].object;
                        add_object_entry(o->oid.hash, o->type, "", 0);
index 896b16f2cceba73a44529f3b3d4f6ecdc33e892c..f7cd1802524e2ba41a41d6479e69534dc042b404 100644 (file)
@@ -268,9 +268,10 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
        return 0;
 }
 
-static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
+static int show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 {
        show_ref(".have", sha1);
+       return 0;
 }
 
 static void collect_one_alternate_ref(const struct ref *ref, void *data)
index 9f6a6b3a9cea036d9a58c52f7d17c2b043a18f33..e52cf3925b2388008221de6f7cbedeecd6cdd010 100644 (file)
@@ -1197,8 +1197,7 @@ static int show(int argc, const char **argv)
 
                info.width = info.width2 = 0;
                for_each_string_list(&states.push, add_push_to_show_info, &info);
-               qsort(info.list->items, info.list->nr,
-                       sizeof(*info.list->items), cmp_string_with_push);
+               QSORT(info.list->items, info.list->nr, cmp_string_with_push);
                if (info.list->nr)
                        printf_ln(Q_("  Local ref configured for 'git push'%s:",
                                     "  Local refs configured for 'git push'%s:",
index 76cf05e2ade4348b8ad59b1541b58f671e53e1ce..4da1f1da25b48c1027a3d255f1f86bb260c1fd21 100644 (file)
@@ -298,14 +298,30 @@ static int try_parent_shorthands(const char *arg)
        unsigned char sha1[20];
        struct commit *commit;
        struct commit_list *parents;
-       int parents_only;
-
-       if ((dotdot = strstr(arg, "^!")))
-               parents_only = 0;
-       else if ((dotdot = strstr(arg, "^@")))
-               parents_only = 1;
-
-       if (!dotdot || dotdot[2])
+       int parent_number;
+       int include_rev = 0;
+       int include_parents = 0;
+       int exclude_parent = 0;
+
+       if ((dotdot = strstr(arg, "^!"))) {
+               include_rev = 1;
+               if (dotdot[2])
+                       return 0;
+       } else if ((dotdot = strstr(arg, "^@"))) {
+               include_parents = 1;
+               if (dotdot[2])
+                       return 0;
+       } else if ((dotdot = strstr(arg, "^-"))) {
+               include_rev = 1;
+               exclude_parent = 1;
+
+               if (dotdot[2]) {
+                       char *end;
+                       exclude_parent = strtoul(dotdot + 2, &end, 10);
+                       if (*end != '\0' || !exclude_parent)
+                               return 0;
+               }
+       } else
                return 0;
 
        *dotdot = 0;
@@ -314,12 +330,24 @@ static int try_parent_shorthands(const char *arg)
                return 0;
        }
 
-       if (!parents_only)
-               show_rev(NORMAL, sha1, arg);
        commit = lookup_commit_reference(sha1);
-       for (parents = commit->parents; parents; parents = parents->next)
-               show_rev(parents_only ? NORMAL : REVERSED,
-                               parents->item->object.oid.hash, arg);
+       if (exclude_parent &&
+           exclude_parent > commit_list_count(commit->parents)) {
+               *dotdot = '^';
+               return 0;
+       }
+
+       if (include_rev)
+               show_rev(NORMAL, sha1, arg);
+       for (parents = commit->parents, parent_number = 1;
+            parents;
+            parents = parents->next, parent_number++) {
+               if (exclude_parent && parent_number != exclude_parent)
+                       continue;
+
+               show_rev(include_parents ? NORMAL : REVERSED,
+                        parents->item->object.oid.hash, arg);
+       }
 
        *dotdot = '^';
        return 1;
index 25fa8a6aed72bb91c3b99121059a90cc976976a5..ba0e1154a9f0b95f60e5bcb6cdb716bf0f6ef366 100644 (file)
@@ -308,7 +308,7 @@ void shortlog_output(struct shortlog *log)
        struct strbuf sb = STRBUF_INIT;
 
        if (log->sort_by_number)
-               qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
+               QSORT(log->list.items, log->list.nr,
                      log->summary ? compare_by_counter : compare_by_list);
        for (i = 0; i < log->list.nr; i++) {
                const struct string_list_item *item = &log->list.items[i];
index 623ca563a2781ff20a3c209a3a69e26795aeb977..974f3403abe76288dc98797e3ceac21a070828ec 100644 (file)
@@ -353,8 +353,7 @@ static int compare_ref_name(const void *a_, const void *b_)
 
 static void sort_ref_range(int bottom, int top)
 {
-       qsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),
-             compare_ref_name);
+       QSORT(ref_name + bottom, top - bottom, compare_ref_name);
 }
 
 static int append_ref(const char *refname, const struct object_id *oid,
@@ -540,8 +539,7 @@ static void append_one_rev(const char *av)
                if (saved_matches == ref_name_cnt &&
                    ref_name_cnt < MAX_REVS)
                        error(_("no matching refs with %s"), av);
-               if (saved_matches + 1 < ref_name_cnt)
-                       sort_ref_range(saved_matches, ref_name_cnt);
+               sort_ref_range(saved_matches, ref_name_cnt);
                return;
        }
        die("bad sha1 reference %s", av);
index fd72c90442dca5f1be7bb822ed0579813557c739..6182eb3197848dc613da1d02280fce0de003addc 100644 (file)
@@ -748,7 +748,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                if (suc->recursive_prefix)
                        strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name);
                else
-                       strbuf_addf(&sb, "%s", ce->name);
+                       strbuf_addstr(&sb, ce->name);
                strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf);
                strbuf_addch(out, '\n');
                goto cleanup;
diff --git a/cache.h b/cache.h
index 5d36ffa1f253d1c007bfa41be270593612d44a6e..0dc39a998cb246b8eb22bc6def8a0900a14e2ee2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1206,6 +1206,11 @@ struct object_context {
 #define GET_SHA1_FOLLOW_SYMLINKS 0100
 #define GET_SHA1_ONLY_TO_DIE    04000
 
+#define GET_SHA1_DISAMBIGUATORS \
+       (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
+       GET_SHA1_TREE | GET_SHA1_TREEISH | \
+       GET_SHA1_BLOB)
+
 extern int get_sha1(const char *str, unsigned char *sha1);
 extern int get_sha1_commit(const char *str, unsigned char *sha1);
 extern int get_sha1_committish(const char *str, unsigned char *sha1);
@@ -1220,6 +1225,8 @@ extern int get_oid(const char *str, struct object_id *oid);
 typedef int each_abbrev_fn(const unsigned char *sha1, void *);
 extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
 
+extern int set_disambiguate_hint_config(const char *var, const char *value);
+
 /*
  * Try to read a SHA1 in hexadecimal format from the 40 characters
  * starting at hex.  Write the 20-byte result to sha1 in binary form.
@@ -1625,7 +1632,15 @@ struct object_info {
                } packed;
        } u;
 };
+
+/*
+ * Initializer for a "struct object_info" that wants no items. You may
+ * also memset() the memory to all-zeroes.
+ */
+#define OBJECT_INFO_INIT {NULL}
+
 extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
+extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
 
 /* Dumb servers support */
 extern int update_server_info(int);
index 32e1a113e589f10ab3080dc173295083cd0c84ca..afd14f318c0c4d18f3a03781d3229f2fdc0f64c6 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -267,6 +267,8 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *);
 extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
+extern struct commit_list *get_shallow_commits_by_rev_list(
+               int ac, const char **av, int shallow_flag, int not_shallow_flag);
 extern void set_alternate_shallow_file(const char *path, int override);
 extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
                                 const struct sha1_array *extra);
index 1e4b6178f79587c9519ecbc9dd90c1e0b72af952..83fdecb1bc9f6f31bc625c79c1d3c12ba5b27f77 100644 (file)
--- a/config.c
+++ b/config.c
@@ -841,6 +841,9 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.disambiguate"))
+               return set_disambiguate_hint_config(var, value);
+
        if (!strcmp(var, "core.loosecompression")) {
                int level = git_config_int(var, value);
                if (level == -1)
index aa9c91d20d86fad78c6e2f85d281ad417975a189..3a43b7a5b35008829f3d63df009d9bd76a62fcc8 100644 (file)
@@ -528,16 +528,6 @@ AC_CHECK_LIB([curl], [curl_global_init],
 [NO_CURL=],
 [NO_CURL=YesPlease])
 
-if test -z "${NO_CURL}" && test -z "${NO_OPENSSL}"; then
-
-AC_CHECK_LIB([curl], [Curl_ssl_init],
-[NEEDS_SSL_WITH_CURL=YesPlease],
-[NEEDS_SSL_WITH_CURL=])
-
-GIT_CONF_SUBST([NEEDS_SSL_WITH_CURL])
-
-fi
-
 GIT_UNSTASH_FLAGS($CURLDIR)
 
 GIT_CONF_SUBST([NO_CURL])
@@ -550,6 +540,17 @@ AC_CHECK_PROG([CURL_CONFIG], [curl-config],
 
 if test $CURL_CONFIG != no; then
     GIT_CONF_SUBST([CURL_CONFIG])
+    if test -z "${NO_OPENSSL}"; then
+      AC_MSG_CHECKING([if Curl supports SSL])
+      if test $(curl-config --features|grep SSL) = SSL; then
+         NEEDS_SSL_WITH_CURL=YesPlease
+         AC_MSG_RESULT([yes])
+      else
+         NEEDS_SSL_WITH_CURL=
+         AC_MSG_RESULT([no])
+      fi
+      GIT_CONF_SUBST([NEEDS_SSL_WITH_CURL])
+    fi
 fi
 
 fi
diff --git a/contrib/coccinelle/.gitignore b/contrib/coccinelle/.gitignore
new file mode 100644 (file)
index 0000000..d3f2964
--- /dev/null
@@ -0,0 +1 @@
+*.patch*
diff --git a/contrib/coccinelle/qsort.cocci b/contrib/coccinelle/qsort.cocci
new file mode 100644 (file)
index 0000000..22b93a9
--- /dev/null
@@ -0,0 +1,37 @@
+@@
+expression base, nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(*base), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(base[0]), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+type T;
+T *base;
+expression nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(T), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb)
+    QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb > 0)
+    QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb > 1)
+    QSORT(base, nmemb, compar);
index 7932d48cdf4a909e929dd601712f46a815e0c671..63995f22ff29f717a360f300e626ddea91d59fa2 100644 (file)
@@ -1,5 +1,40 @@
+@ strbuf_addf_with_format_only @
+expression E;
+constant fmt;
+@@
+  strbuf_addf(E,
+(
+  fmt
+|
+  _(fmt)
+)
+  );
+
+@ script:python @
+fmt << strbuf_addf_with_format_only.fmt;
+@@
+cocci.include_match("%" not in fmt)
+
+@ extends strbuf_addf_with_format_only @
+@@
+- strbuf_addf
++ strbuf_addstr
+  (E,
+(
+  fmt
+|
+  _(fmt)
+)
+  );
+
 @@
 expression E1, E2;
 @@
-- strbuf_addf(E1, E2);
+- strbuf_addf(E1, "%s", E2);
 + strbuf_addstr(E1, E2);
+
+@@
+expression E1, E2, E3;
+@@
+- strbuf_addstr(E1, find_unique_abbrev(E2, E3));
++ strbuf_add_unique_abbrev(E1, E2, E3);
index 9c8f7380d0f1ad76f0163209e5005adfe46ec916..21016bf8dfe87572fb53a05488ba05bb3c08ed97 100644 (file)
@@ -338,7 +338,7 @@ __git_tags ()
 __git_refs ()
 {
        local i hash dir="$(__gitdir "${1-}")" track="${2-}"
-       local format refs
+       local format refs pfx
        if [ -d "$dir" ]; then
                case "$cur" in
                refs|refs/*)
@@ -347,14 +347,15 @@ __git_refs ()
                        track=""
                        ;;
                *)
+                       [[ "$cur" == ^* ]] && pfx="^"
                        for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
-                               if [ -e "$dir/$i" ]; then echo $i; fi
+                               if [ -e "$dir/$i" ]; then echo $pfx$i; fi
                        done
                        format="refname:short"
                        refs="refs/tags refs/heads refs/remotes"
                        ;;
                esac
-               git --git-dir="$dir" for-each-ref --format="%($format)" \
+               git --git-dir="$dir" for-each-ref --format="$pfx%($format)" \
                        $refs
                if [ -n "$track" ]; then
                        # employ the heuristic used by git checkout
diff --git a/diff.c b/diff.c
index a178ed39bc77988dcc6282d252002f5446305628..1d304e0550766edf4679f089aafac4de14258482 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2019,7 +2019,7 @@ static void show_dirstat(struct diff_options *options)
                return;
 
        /* Show all directories with more than x% of the changes */
-       qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
+       QSORT(dir.files, dir.nr, dirstat_compare);
        gather_dirstat(options, &dir, changed, "", 0);
 }
 
@@ -2063,7 +2063,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
                return;
 
        /* Show all directories with more than x% of the changes */
-       qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
+       QSORT(dir.files, dir.nr, dirstat_compare);
        gather_dirstat(options, &dir, changed, "", 0);
 }
 
@@ -3109,7 +3109,7 @@ static void fill_metainfo(struct strbuf *msg,
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
                            find_unique_abbrev(one->oid.hash, abbrev));
-               strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
+               strbuf_add_unique_abbrev(msg, two->oid.hash, abbrev);
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@ -4923,7 +4923,7 @@ static int diffnamecmp(const void *a_, const void *b_)
 void diffcore_fix_diff_index(struct diff_options *options)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
-       qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
+       QSORT(q->queue, q->nr, diffnamecmp);
 }
 
 void diffcore_std(struct diff_options *options)
index 4159748a70ccc0ddee7cf2fcf82976629dfa6867..2ebedb32d18abb3e50d8b4e4b523cf2f8205a695 100644 (file)
@@ -158,10 +158,7 @@ static struct spanhash_top *hash_chars(struct diff_filespec *one)
                n = 0;
                accum1 = accum2 = 0;
        }
-       qsort(hash->data,
-               1ul << hash->alloc_log2,
-               sizeof(hash->data[0]),
-               spanhash_cmp);
+       QSORT(hash->data, 1ul << hash->alloc_log2, spanhash_cmp);
        return hash;
 }
 
index 69d41f7a5781a10e43cf0e74f20be85a0d1fc5be..1957f822a5120a463ea4dff503fd8fe78f63b663 100644 (file)
@@ -101,7 +101,7 @@ void order_objects(const char *orderfile, obj_path_fn_t obj_path,
                objs[i].orig_order = i;
                objs[i].order = match_order(obj_path(objs[i].obj));
        }
-       qsort(objs, nr, sizeof(*objs), compare_objs_order);
+       QSORT(objs, nr, compare_objs_order);
 }
 
 static const char *pair_pathtwo(void *obj)
index 73d003a08ae72d0c8f18b16172bc09c86022eb8f..54a2396653df230d8f527d2eac5a2f75398f0b13 100644 (file)
@@ -580,7 +580,7 @@ void diffcore_rename(struct diff_options *options)
        stop_progress(&progress);
 
        /* cost matrix sorted by most to least similar pair */
-       qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare);
+       QSORT(mx, dst_cnt * NUM_CANDIDATE_PER_DST, score_compare);
 
        rename_count += find_renames(mx, dst_cnt, minimum_score, 0);
        if (detect_rename == DIFF_DETECT_COPY)
diff --git a/dir.c b/dir.c
index 9e09bcbd02831b85ee772208469c0b677649682f..3bad1ade8d59f2ae5a02d50ae7db85642f9dcb0e 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2005,8 +2005,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
        if (!len || treat_leading_path(dir, path, len, simplify))
                read_directory_recursive(dir, path, len, untracked, 0, simplify);
        free_simplify(simplify);
-       qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
-       qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
+       QSORT(dir->entries, dir->nr, cmp_name);
+       QSORT(dir->ignored, dir->ignored_nr, cmp_name);
        if (dir->untracked) {
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
                trace_printf_key(&trace_untracked_stats,
index bf53ac95da04327aa2b83ff2943d51e6d0c731db..cb545d7df514b73a5ad9358b614ea9b4751e3c1e 100644 (file)
@@ -1460,9 +1460,9 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b)
        unsigned int i;
 
        if (!v)
-               qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
+               QSORT(t->entries, t->entry_count, tecmp0);
        else
-               qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp1);
+               QSORT(t->entries, t->entry_count, tecmp1);
 
        for (i = 0; i < t->entry_count; i++) {
                if (t->entries[i]->versions[v].mode)
index 413937e7404d163883d5d0456ebefc4e1504cd87..cb45c346ea97059b66642b458c7ca372ece67aaa 100644 (file)
@@ -21,6 +21,8 @@ static int fetch_unpack_limit = -1;
 static int unpack_limit = 100;
 static int prefer_ofs_delta = 1;
 static int no_done;
+static int deepen_since_ok;
+static int deepen_not_ok;
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
 static int agent_supported;
@@ -50,6 +52,21 @@ static int non_common_revs, multi_ack, use_sideband;
 #define ALLOW_REACHABLE_SHA1   02
 static unsigned int allow_unadvertised_object_request;
 
+__attribute__((format (printf, 2, 3)))
+static inline void print_verbose(const struct fetch_pack_args *args,
+                                const char *fmt, ...)
+{
+       va_list params;
+
+       if (!args->verbose)
+               return;
+
+       va_start(params, fmt);
+       vfprintf(stderr, fmt, params);
+       va_end(params);
+       fputc('\n', stderr);
+}
+
 static void rev_list_push(struct commit *commit, int mark)
 {
        if (!(commit->object.flags & mark)) {
@@ -182,7 +199,7 @@ enum ack_type {
 
 static void consume_shallow_list(struct fetch_pack_args *args, int fd)
 {
-       if (args->stateless_rpc && args->depth > 0) {
+       if (args->stateless_rpc && args->deepen) {
                /* If we sent a depth we will get back "duplicate"
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
@@ -193,7 +210,7 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
                                continue;
                        if (starts_with(line, "unshallow "))
                                continue;
-                       die("git fetch-pack: expected shallow list");
+                       die(_("git fetch-pack: expected shallow list"));
                }
        }
 }
@@ -205,7 +222,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
        const char *arg;
 
        if (!len)
-               die("git fetch-pack: expected ACK/NAK, got EOF");
+               die(_("git fetch-pack: expected ACK/NAK, got EOF"));
        if (!strcmp(line, "NAK"))
                return NAK;
        if (skip_prefix(line, "ACK ", &arg)) {
@@ -223,7 +240,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
                        return ACK;
                }
        }
-       die("git fetch_pack: expected ACK/NAK, got '%s'", line);
+       die(_("git fetch_pack: expected ACK/NAK, got '%s'"), line);
 }
 
 static void send_request(struct fetch_pack_args *args,
@@ -275,7 +292,7 @@ static int find_common(struct fetch_pack_args *args,
        size_t state_len = 0;
 
        if (args->stateless_rpc && multi_ack == 1)
-               die("--stateless-rpc requires multi_ack_detailed");
+               die(_("--stateless-rpc requires multi_ack_detailed"));
        if (marked)
                for_each_ref(clear_marks, NULL);
        marked = 1;
@@ -312,10 +329,13 @@ static int find_common(struct fetch_pack_args *args,
                        if (no_done)            strbuf_addstr(&c, " no-done");
                        if (use_sideband == 2)  strbuf_addstr(&c, " side-band-64k");
                        if (use_sideband == 1)  strbuf_addstr(&c, " side-band");
+                       if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
                        if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
                        if (args->no_progress)   strbuf_addstr(&c, " no-progress");
                        if (args->include_tag)   strbuf_addstr(&c, " include-tag");
                        if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+                       if (deepen_since_ok)    strbuf_addstr(&c, " deepen-since");
+                       if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
                        if (agent_supported)    strbuf_addf(&c, " agent=%s",
                                                            git_user_agent_sanitized());
                        packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
@@ -335,10 +355,21 @@ static int find_common(struct fetch_pack_args *args,
                write_shallow_commits(&req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
+       if (args->deepen_since) {
+               unsigned long max_age = approxidate(args->deepen_since);
+               packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+       }
+       if (args->deepen_not) {
+               int i;
+               for (i = 0; i < args->deepen_not->nr; i++) {
+                       struct string_list_item *s = args->deepen_not->items + i;
+                       packet_buf_write(&req_buf, "deepen-not %s", s->string);
+               }
+       }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
 
-       if (args->depth > 0) {
+       if (args->deepen) {
                char *line;
                const char *arg;
                unsigned char sha1[20];
@@ -347,23 +378,23 @@ static int find_common(struct fetch_pack_args *args,
                while ((line = packet_read_line(fd[0], NULL))) {
                        if (skip_prefix(line, "shallow ", &arg)) {
                                if (get_sha1_hex(arg, sha1))
-                                       die("invalid shallow line: %s", line);
+                                       die(_("invalid shallow line: %s"), line);
                                register_shallow(sha1);
                                continue;
                        }
                        if (skip_prefix(line, "unshallow ", &arg)) {
                                if (get_sha1_hex(arg, sha1))
-                                       die("invalid unshallow line: %s", line);
+                                       die(_("invalid unshallow line: %s"), line);
                                if (!lookup_object(sha1))
-                                       die("object not found: %s", line);
+                                       die(_("object not found: %s"), line);
                                /* make sure that it is parsed as shallow */
                                if (!parse_object(sha1))
-                                       die("error in object: %s", line);
+                                       die(_("error in object: %s"), line);
                                if (unregister_shallow(sha1))
-                                       die("no shallow found: %s", line);
+                                       die(_("no shallow found: %s"), line);
                                continue;
                        }
-                       die("expected shallow/unshallow, got %s", line);
+                       die(_("expected shallow/unshallow, got %s"), line);
                }
        } else if (!args->stateless_rpc)
                send_request(args, fd[1], &req_buf);
@@ -380,8 +411,7 @@ static int find_common(struct fetch_pack_args *args,
        retval = -1;
        while ((sha1 = get_rev())) {
                packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
-               if (args->verbose)
-                       fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+               print_verbose(args, "have %s", sha1_to_hex(sha1));
                in_vain++;
                if (flush_at <= ++count) {
                        int ack;
@@ -402,9 +432,9 @@ static int find_common(struct fetch_pack_args *args,
                        consume_shallow_list(args, fd[0]);
                        do {
                                ack = get_ack(fd[0], result_sha1);
-                               if (args->verbose && ack)
-                                       fprintf(stderr, "got ack %d %s\n", ack,
-                                                       sha1_to_hex(result_sha1));
+                               if (ack)
+                                       print_verbose(args, _("got %s %d %s"), "ack",
+                                                     ack, sha1_to_hex(result_sha1));
                                switch (ack) {
                                case ACK:
                                        flushes = 0;
@@ -417,7 +447,7 @@ static int find_common(struct fetch_pack_args *args,
                                        struct commit *commit =
                                                lookup_commit(result_sha1);
                                        if (!commit)
-                                               die("invalid commit %s", sha1_to_hex(result_sha1));
+                                               die(_("invalid commit %s"), sha1_to_hex(result_sha1));
                                        if (args->stateless_rpc
                                         && ack == ACK_common
                                         && !(commit->object.flags & COMMON)) {
@@ -450,8 +480,7 @@ static int find_common(struct fetch_pack_args *args,
                        } while (ack);
                        flushes--;
                        if (got_continue && MAX_IN_VAIN < in_vain) {
-                               if (args->verbose)
-                                       fprintf(stderr, "giving up\n");
+                               print_verbose(args, _("giving up"));
                                break; /* give up */
                        }
                }
@@ -461,8 +490,7 @@ static int find_common(struct fetch_pack_args *args,
                packet_buf_write(&req_buf, "done\n");
                send_request(args, fd[1], &req_buf);
        }
-       if (args->verbose)
-               fprintf(stderr, "done\n");
+       print_verbose(args, _("done"));
        if (retval != 0) {
                multi_ack = 0;
                flushes++;
@@ -474,9 +502,8 @@ static int find_common(struct fetch_pack_args *args,
        while (flushes || multi_ack) {
                int ack = get_ack(fd[0], result_sha1);
                if (ack) {
-                       if (args->verbose)
-                               fprintf(stderr, "got ack (%d) %s\n", ack,
-                                       sha1_to_hex(result_sha1));
+                       print_verbose(args, _("got %s (%d) %s"), "ack",
+                                     ack, sha1_to_hex(result_sha1));
                        if (ack == ACK)
                                return 0;
                        multi_ack = 1;
@@ -521,9 +548,8 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
                                         unsigned long cutoff)
 {
        while (complete && cutoff <= complete->item->date) {
-               if (args->verbose)
-                       fprintf(stderr, "Marking %s as complete\n",
-                               oid_to_hex(&complete->item->object.oid));
+               print_verbose(args, _("Marking %s as complete"),
+                             oid_to_hex(&complete->item->object.oid));
                pop_most_recent_commit(&complete, COMPLETE);
        }
 }
@@ -559,7 +585,7 @@ static void filter_refs(struct fetch_pack_args *args,
                }
 
                if (!keep && args->fetch_all &&
-                   (!args->depth || !starts_with(ref->name, "refs/tags/")))
+                   (!args->deepen || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
 
                if (keep) {
@@ -629,7 +655,7 @@ static int everything_local(struct fetch_pack_args *args,
                }
        }
 
-       if (!args->depth) {
+       if (!args->deepen) {
                for_each_ref(mark_complete_oid, NULL);
                for_each_alternate_ref(mark_alternate_complete, NULL);
                commit_list_sort_by_date(&complete);
@@ -664,18 +690,12 @@ static int everything_local(struct fetch_pack_args *args,
                o = lookup_object(remote);
                if (!o || !(o->flags & COMPLETE)) {
                        retval = 0;
-                       if (!args->verbose)
-                               continue;
-                       fprintf(stderr,
-                               "want %s (%s)\n", sha1_to_hex(remote),
-                               ref->name);
+                       print_verbose(args, "want %s (%s)", sha1_to_hex(remote),
+                                     ref->name);
                        continue;
                }
-               if (!args->verbose)
-                       continue;
-               fprintf(stderr,
-                       "already have %s (%s)\n", sha1_to_hex(remote),
-                       ref->name);
+               print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote),
+                             ref->name);
        }
        return retval;
 }
@@ -712,8 +732,7 @@ static int get_pack(struct fetch_pack_args *args,
                demux.out = -1;
                demux.isolate_sigpipe = 1;
                if (start_async(&demux))
-                       die("fetch-pack: unable to fork off sideband"
-                           " demultiplexer");
+                       die(_("fetch-pack: unable to fork off sideband demultiplexer"));
        }
        else
                demux.out = xd[0];
@@ -721,7 +740,7 @@ static int get_pack(struct fetch_pack_args *args,
        if (!args->keep_pack && unpack_limit) {
 
                if (read_pack_header(demux.out, &header))
-                       die("protocol error: bad pack header");
+                       die(_("protocol error: bad pack header"));
                pass_header = 1;
                if (ntohl(header.hdr_entries) < unpack_limit)
                        do_keep = 0;
@@ -777,7 +796,7 @@ static int get_pack(struct fetch_pack_args *args,
        cmd.in = demux.out;
        cmd.git_cmd = 1;
        if (start_command(&cmd))
-               die("fetch-pack: unable to fork off %s", cmd_name);
+               die(_("fetch-pack: unable to fork off %s"), cmd_name);
        if (do_keep && pack_lockfile) {
                *pack_lockfile = index_pack_lockfile(cmd.out);
                close(cmd.out);
@@ -793,9 +812,9 @@ static int get_pack(struct fetch_pack_args *args,
                        args->check_self_contained_and_connected &&
                        ret == 0;
        else
-               die("%s failed", cmd_name);
+               die(_("%s failed"), cmd_name);
        if (use_sideband && finish_async(&demux))
-               die("error in sideband demultiplexer");
+               die(_("error in sideband demultiplexer"));
        return 0;
 }
 
@@ -819,44 +838,39 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        int agent_len;
 
        sort_ref_list(&ref, ref_compare_name);
-       qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
+       QSORT(sought, nr_sought, cmp_ref_by_name);
 
        if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
-               die("Server does not support shallow clients");
+               die(_("Server does not support shallow clients"));
+       if (args->depth > 0 || args->deepen_since || args->deepen_not)
+               args->deepen = 1;
        if (server_supports("multi_ack_detailed")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports multi_ack_detailed\n");
+               print_verbose(args, _("Server supports multi_ack_detailed"));
                multi_ack = 2;
                if (server_supports("no-done")) {
-                       if (args->verbose)
-                               fprintf(stderr, "Server supports no-done\n");
+                       print_verbose(args, _("Server supports no-done"));
                        if (args->stateless_rpc)
                                no_done = 1;
                }
        }
        else if (server_supports("multi_ack")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports multi_ack\n");
+               print_verbose(args, _("Server supports multi_ack"));
                multi_ack = 1;
        }
        if (server_supports("side-band-64k")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports side-band-64k\n");
+               print_verbose(args, _("Server supports side-band-64k"));
                use_sideband = 2;
        }
        else if (server_supports("side-band")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports side-band\n");
+               print_verbose(args, _("Server supports side-band"));
                use_sideband = 1;
        }
        if (server_supports("allow-tip-sha1-in-want")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
+               print_verbose(args, _("Server supports allow-tip-sha1-in-want"));
                allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
        }
        if (server_supports("allow-reachable-sha1-in-want")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
+               print_verbose(args, _("Server supports allow-reachable-sha1-in-want"));
                allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
        }
        if (!server_supports("thin-pack"))
@@ -865,18 +879,27 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                args->no_progress = 0;
        if (!server_supports("include-tag"))
                args->include_tag = 0;
-       if (server_supports("ofs-delta")) {
-               if (args->verbose)
-                       fprintf(stderr, "Server supports ofs-delta\n");
-       } else
+       if (server_supports("ofs-delta"))
+               print_verbose(args, _("Server supports ofs-delta"));
+       else
                prefer_ofs_delta = 0;
 
        if ((agent_feature = server_feature_value("agent", &agent_len))) {
                agent_supported = 1;
-               if (args->verbose && agent_len)
-                       fprintf(stderr, "Server version is %.*s\n",
-                               agent_len, agent_feature);
+               if (agent_len)
+                       print_verbose(args, _("Server version is %.*s"),
+                                     agent_len, agent_feature);
        }
+       if (server_supports("deepen-since"))
+               deepen_since_ok = 1;
+       else if (args->deepen_since)
+               die(_("Server does not support --shallow-since"));
+       if (server_supports("deepen-not"))
+               deepen_not_ok = 1;
+       else if (args->deepen_not)
+               die(_("Server does not support --shallow-exclude"));
+       if (!server_supports("deepen-relative") && args->deepen_relative)
+               die(_("Server does not support --deepen"));
 
        if (everything_local(args, &ref, sought, nr_sought)) {
                packet_flush(fd[1]);
@@ -887,11 +910,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                        /* When cloning, it is not unusual to have
                         * no common commit.
                         */
-                       warning("no common commits");
+                       warning(_("no common commits"));
 
        if (args->stateless_rpc)
                packet_flush(fd[1]);
-       if (args->depth > 0)
+       if (args->deepen)
                setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
                                        NULL);
        else if (si->nr_ours || si->nr_theirs)
@@ -899,7 +922,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        else
                alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
-               die("git fetch-pack: fetch failed.");
+               die(_("git fetch-pack: fetch failed."));
 
  all_done:
        return ref;
@@ -958,7 +981,7 @@ static void update_shallow(struct fetch_pack_args *args,
        int *status;
        int i;
 
-       if (args->depth > 0 && alternate_shallow_file) {
+       if (args->deepen && alternate_shallow_file) {
                if (*alternate_shallow_file == '\0') { /* --unshallow */
                        unlink_or_warn(git_path_shallow());
                        rollback_lock_file(&shallow_lock);
@@ -1061,7 +1084,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
 
        if (!ref) {
                packet_flush(fd[1]);
-               die("no matching remote head");
+               die(_("no matching remote head"));
        }
        prepare_shallow_info(&si, shallow);
        ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
index bb7fd76e5939d812f1f89b787324ae6ec519ca15..c912e3d321c6a80952dbcd266786d1042a49e579 100644 (file)
@@ -10,6 +10,9 @@ struct fetch_pack_args {
        const char *uploadpack;
        int unpacklimit;
        int depth;
+       const char *deepen_since;
+       const struct string_list *deepen_not;
+       unsigned deepen_relative:1;
        unsigned quiet:1;
        unsigned keep_pack:1;
        unsigned lock_pack:1;
@@ -25,6 +28,7 @@ struct fetch_pack_args {
        unsigned self_contained_and_connected:1;
        unsigned cloning:1;
        unsigned update_shallow:1;
+       unsigned deepen:1;
 };
 
 /*
index 0ce2cdfb98d54e35881a94e9037e7380ec5b821c..43718dabae88062b39d65f2d6c1f5e35a6eb4800 100644 (file)
@@ -985,6 +985,14 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define qsort git_qsort
 #endif
 
+#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
+static inline void sane_qsort(void *base, size_t nmemb, size_t size,
+                             int(*compar)(const void *, const void *))
+{
+       if (nmemb > 1)
+               qsort(base, nmemb, size, compar);
+}
+
 #ifndef REG_STARTEND
 #error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
 #endif
diff --git a/graph.c b/graph.c
index 06f1139f2e201e1319bece3865ae95060ae2522c..d4e8519c904df6e18869de527f7fe6d57adf2a26 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -1175,6 +1175,7 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
 {
        int i;
+       int chars_written = 0;
 
        if (graph->state != GRAPH_COMMIT) {
                graph_next_line(graph, sb);
@@ -1190,14 +1191,21 @@ static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
         */
        for (i = 0; i < graph->num_columns; i++) {
                struct column *col = &graph->columns[i];
+
                strbuf_write_column(sb, col, '|');
-               if (col->commit == graph->commit && graph->num_parents > 2)
-                       strbuf_addchars(sb, ' ', (graph->num_parents - 2) * 2);
-               else
+               chars_written++;
+
+               if (col->commit == graph->commit && graph->num_parents > 2) {
+                       int len = (graph->num_parents - 2) * 2;
+                       strbuf_addchars(sb, ' ', len);
+                       chars_written += len;
+               } else {
                        strbuf_addch(sb, ' ');
+                       chars_written++;
+               }
        }
 
-       graph_pad_horizontally(graph, sb, graph->num_columns);
+       graph_pad_horizontally(graph, sb, chars_written);
 
        /*
         * Update graph->prev_state since we have output a padding line
diff --git a/help.c b/help.c
index 2ff3b5a7745dbb7937896fb2303c9299387929de..53e2a67e0052b7abb9f01e075f76c4eb5f35cbfc 100644 (file)
--- a/help.c
+++ b/help.c
@@ -170,8 +170,7 @@ void load_command_list(const char *prefix,
 
        if (exec_path) {
                list_commands_in_dir(main_cmds, exec_path, prefix);
-               qsort(main_cmds->names, main_cmds->cnt,
-                     sizeof(*main_cmds->names), cmdname_compare);
+               QSORT(main_cmds->names, main_cmds->cnt, cmdname_compare);
                uniq(main_cmds);
        }
 
@@ -190,8 +189,7 @@ void load_command_list(const char *prefix,
                }
                free(paths);
 
-               qsort(other_cmds->names, other_cmds->cnt,
-                     sizeof(*other_cmds->names), cmdname_compare);
+               QSORT(other_cmds->names, other_cmds->cnt, cmdname_compare);
                uniq(other_cmds);
        }
        exclude_cmds(other_cmds, main_cmds);
@@ -238,8 +236,7 @@ void list_common_cmds_help(void)
                        longest = strlen(common_cmds[i].name);
        }
 
-       qsort(common_cmds, ARRAY_SIZE(common_cmds),
-               sizeof(common_cmds[0]), cmd_group_cmp);
+       QSORT(common_cmds, ARRAY_SIZE(common_cmds), cmd_group_cmp);
 
        puts(_("These are common Git commands used in various situations:"));
 
@@ -324,8 +321,7 @@ const char *help_unknown_cmd(const char *cmd)
 
        add_cmd_list(&main_cmds, &aliases);
        add_cmd_list(&main_cmds, &other_cmds);
-       qsort(main_cmds.names, main_cmds.cnt,
-             sizeof(*main_cmds.names), cmdname_compare);
+       QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare);
        uniq(&main_cmds);
 
        /* This abuses cmdname->len for levenshtein distance */
@@ -359,8 +355,7 @@ const char *help_unknown_cmd(const char *cmd)
                        levenshtein(cmd, candidate, 0, 2, 1, 3) + 1;
        }
 
-       qsort(main_cmds.names, main_cmds.cnt,
-             sizeof(*main_cmds.names), levenshtein_compare);
+       QSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare);
 
        if (!main_cmds.cnt)
                die(_("Uh oh. Your system reports no Git commands at all."));
diff --git a/http.c b/http.c
index 82ed54269059c38e1698b7ffc1af0d1000e5bfec..4c4a812fcc39509e32fbcae3db21871b97a1a5eb 100644 (file)
--- a/http.c
+++ b/http.c
@@ -90,6 +90,18 @@ static struct {
         * here, too
         */
 };
+#if LIBCURL_VERSION_NUM >= 0x071600
+static const char *curl_deleg;
+static struct {
+       const char *name;
+       long curl_deleg_param;
+} curl_deleg_levels[] = {
+       { "none", CURLGSSAPI_DELEGATION_NONE },
+       { "policy", CURLGSSAPI_DELEGATION_POLICY_FLAG },
+       { "always", CURLGSSAPI_DELEGATION_FLAG },
+};
+#endif
+
 static struct credential proxy_auth = CREDENTIAL_INIT;
 static const char *curl_proxyuserpwd;
 static const char *curl_cookie_file;
@@ -323,6 +335,15 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.delegation", var)) {
+#if LIBCURL_VERSION_NUM >= 0x071600
+               return git_config_string(&curl_deleg, var, value);
+#else
+               warning(_("Delegation control is not supported with cURL < 7.22.0"));
+               return 0;
+#endif
+       }
+
        if (!strcmp("http.pinnedpubkey", var)) {
 #if LIBCURL_VERSION_NUM >= 0x072c00
                return git_config_pathname(&ssl_pinnedkey, var, value);
@@ -351,7 +372,7 @@ static int http_options(const char *var, const char *value, void *cb)
 
 static void init_curl_http_auth(CURL *result)
 {
-       if (!http_auth.username) {
+       if (!http_auth.username || !*http_auth.username) {
                if (curl_empty_auth)
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
@@ -629,6 +650,22 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
 #endif
 
+#if LIBCURL_VERSION_NUM >= 0x071600
+       if (curl_deleg) {
+               int i;
+               for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
+                       if (!strcmp(curl_deleg, curl_deleg_levels[i].name)) {
+                               curl_easy_setopt(result, CURLOPT_GSSAPI_DELEGATION,
+                                               curl_deleg_levels[i].curl_deleg_param);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(curl_deleg_levels))
+                       warning("Unknown delegation method '%s': using default",
+                               curl_deleg);
+       }
+#endif
+
        if (http_proactive_auth)
                init_curl_http_auth(result);
 
index 0f5f4760e90de236757924683478c7f25cdb653f..adb9738c308f70190f6c3996d5781e6fbc4dea3f 100644 (file)
@@ -1410,6 +1410,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc)
        curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
        curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
 
+       strbuf_addstr(&path, server.use_ssl ? "imaps://" : "imap://");
        strbuf_addstr(&path, server.host);
        if (!path.len || path.buf[path.len - 1] != '/')
                strbuf_addch(&path, '/');
index 916e7248701dad2a8beb870887a5ed7d3874ff41..65f3558b3be695ce5259df9a2da4f28e95b35b71 100644 (file)
@@ -113,7 +113,7 @@ void sort_and_merge_range_set(struct range_set *rs)
        int i;
        int o = 0; /* output cursor */
 
-       qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp);
+       QSORT(rs->ranges, rs->nr, range_cmp);
 
        for (i = 0; i < rs->nr; i++) {
                if (rs->ranges[i].start == rs->ranges[i].end)
index f8b644263ff7e3b244bc1804b8527f87f7dbcf84..614a0067566733dc91998423f6dc05634cbedc61 100644 (file)
--- a/object.h
+++ b/object.h
@@ -31,7 +31,7 @@ struct object_array {
  * revision.h:      0---------10                                26
  * fetch-pack.c:    0---4
  * walker.c:        0-2
- * upload-pack.c:               11----------------19
+ * upload-pack.c:       4       11----------------19
  * builtin/blame.c:               12-13
  * bisect.c:                               16
  * bundle.c:                               16
index c30bcd06cbd516eb04e9b55b27b5f091ede61439..9705596014c8fcf534fd0478606d478164382913 100644 (file)
@@ -385,8 +385,7 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
 {
        unsigned int i = 0, j, next;
 
-       qsort(indexed_commits, indexed_commits_nr, sizeof(indexed_commits[0]),
-             date_compare);
+       QSORT(indexed_commits, indexed_commits_nr, date_compare);
 
        if (writer.show_progress)
                writer.progress = start_progress("Selecting bitmap commits", 0);
index c5c7763323156232748314723e39deb92b93fa4e..27f70d345fbf1b339cb4bad1011253cece1bf619 100644 (file)
@@ -96,7 +96,7 @@ static int verify_packfile(struct packed_git *p,
                entries[i].offset = nth_packed_object_offset(p, i);
                entries[i].nr = i;
        }
-       qsort(entries, nr_objects, sizeof(*entries), compare_entries);
+       QSORT(entries, nr_objects, compare_entries);
 
        for (i = 0; i < nr_objects; i++) {
                void *data;
index d1b98b30ffc468421a4eb82661d037f90cf580f2..cc9b9a9b90ca62e7a6e638928511a465fbb9974b 100644 (file)
@@ -27,6 +27,15 @@ struct object_entry {
        unsigned no_try_delta:1;
        unsigned tagged:1; /* near the very tip of refs */
        unsigned filled:1; /* assigned write-order */
+
+       /*
+        * State flags for depth-first search used for analyzing delta cycles.
+        */
+       enum {
+               DFS_NONE = 0,
+               DFS_ACTIVE,
+               DFS_DONE
+       } dfs_state;
 };
 
 struct packing_data {
index ea0b78813081236401efbf5f102e7f89a7d5ed7b..88bc7f9f7d03557b040b7246cab7d10c66ec380d 100644 (file)
@@ -61,8 +61,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                        if (objects[i]->offset > last_obj_offset)
                                last_obj_offset = objects[i]->offset;
                }
-               qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
-                     sha1_compare);
+               QSORT(sorted_by_sha, nr_objects, sha1_compare);
        }
        else
                sorted_by_sha = list = last = NULL;
index 49a53607bb004e8ccc34e6a18eadc8dfc2e7879d..86f2b449b1b43d7abb0917c07676304a666056a9 100644 (file)
@@ -446,8 +446,7 @@ void parse_pathspec(struct pathspec *pathspec,
        if (pathspec->magic & PATHSPEC_MAXDEPTH) {
                if (flags & PATHSPEC_KEEP_ORDER)
                        die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible");
-               qsort(pathspec->items, pathspec->nr,
-                     sizeof(struct pathspec_item), pathspec_item_cmp);
+               QSORT(pathspec->items, pathspec->nr, pathspec_item_cmp);
        }
 }
 
index 493edb0a446ec0019e39e8f4f2ca8a4e108c40f5..25efbcac9206e82343f954405176fbf477055903 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1072,7 +1072,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = want_color(c->pretty_ctx->color);
-                       if (c->auto_color)
+                       if (c->auto_color && sb->len)
                                strbuf_addstr(sb, GIT_COLOR_RESET);
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
index 9adbb8af3eaa138ccf161a91ce5b00e366fae7fa..d4c2931f3aab14accaa5720c87b2fc58aed83faa 100644 (file)
@@ -235,7 +235,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
 {
        const char *sp;
        const char *arg;
-       int i, at;
+       int i, at, atom_len;
 
        sp = atom;
        if (*sp == '*' && sp < ep)
@@ -250,19 +250,19 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
                        return i;
        }
 
+       /*
+        * If the atom name has a colon, strip it and everything after
+        * it off - it specifies the format for this entry, and
+        * shouldn't be used for checking against the valid_atom
+        * table.
+        */
+       arg = memchr(sp, ':', ep - sp);
+       atom_len = (arg ? arg : ep) - sp;
+
        /* Is the atom a valid one? */
        for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
                int len = strlen(valid_atom[i].name);
-
-               /*
-                * If the atom name has a colon, strip it and everything after
-                * it off - it specifies the format for this entry, and
-                * shouldn't be used for checking against the valid_atom
-                * table.
-                */
-               arg = memchr(sp, ':', ep - sp);
-               if (len == (arg ? arg : ep) - sp &&
-                   !memcmp(valid_atom[i].name, sp, len))
+               if (len == atom_len && !memcmp(valid_atom[i].name, sp, len))
                        break;
        }
 
@@ -1573,7 +1573,7 @@ static int compare_refs(const void *a_, const void *b_)
 void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 {
        ref_sorting = sorting;
-       qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
+       QSORT(array->items, array->nr, compare_refs);
 }
 
 static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
diff --git a/refs.c b/refs.c
index 5ffdd778d9766cad21e251e9020e2be2ce677acf..abc721b607cd8ff4916cce7f8381521fbb58fb5d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -419,6 +419,13 @@ static char *substitute_branch_name(const char **string, int *len)
 int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
        char *last_branch = substitute_branch_name(&str, &len);
+       int   refs_found  = expand_ref(str, len, sha1, ref);
+       free(last_branch);
+       return refs_found;
+}
+
+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
        const char **p, *r;
        int refs_found = 0;
 
@@ -444,7 +451,6 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
                        warning("ignoring broken ref %s.", fullref);
                }
        }
-       free(last_branch);
        return refs_found;
 }
 
diff --git a/refs.h b/refs.h
index fe51280fc61d24eccca0e38f8fc75ecee7fcb751..69478439137d64f6dab7f20d4f157f6c7a982ac2 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -94,6 +94,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
  */
 int refname_match(const char *abbrev_name, const char *full_name);
 
+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);
 int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 
index 0709f60b8e8c43fa830c0e678fdf7b83435535bb..d16feb19c57b1b74594947f8730b984e94c43def 100644 (file)
@@ -501,7 +501,7 @@ static void sort_ref_dir(struct ref_dir *dir)
        if (dir->sorted == dir->nr)
                return;
 
-       qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
+       QSORT(dir->entries, dir->nr, ref_entry_cmp);
 
        /* Remove any duplicates: */
        for (i = 0, j = 0; j < dir->nr; j++) {
index 6b83b7783e9c62fcf623acd0ba034dd1c0b9bd10..f14c41f4c0d6d6c9bbfd18d6417d16f0e6068c1d 100644 (file)
@@ -20,6 +20,8 @@ static struct strbuf url = STRBUF_INIT;
 struct options {
        int verbosity;
        unsigned long depth;
+       char *deepen_since;
+       struct string_list deepen_not;
        unsigned progress : 1,
                check_self_contained_and_connected : 1,
                cloning : 1,
@@ -28,7 +30,8 @@ struct options {
                dry_run : 1,
                thin : 1,
                /* One of the SEND_PACK_PUSH_CERT_* constants. */
-               push_cert : 2;
+               push_cert : 2,
+               deepen_relative : 1;
 };
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -60,6 +63,23 @@ static int set_option(const char *name, const char *value)
                options.depth = v;
                return 0;
        }
+       else if (!strcmp(name, "deepen-since")) {
+               options.deepen_since = xstrdup(value);
+               return 0;
+       }
+       else if (!strcmp(name, "deepen-not")) {
+               string_list_append(&options.deepen_not, value);
+               return 0;
+       }
+       else if (!strcmp(name, "deepen-relative")) {
+               if (!strcmp(value, "true"))
+                       options.deepen_relative = 1;
+               else if (!strcmp(value, "false"))
+                       options.deepen_relative = 0;
+               else
+                       return -1;
+               return 0;
+       }
        else if (!strcmp(name, "followtags")) {
                if (!strcmp(value, "true"))
                        options.followtags = 1;
@@ -725,8 +745,8 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
        int ret, i;
 
        ALLOC_ARRAY(targets, nr_heads);
-       if (options.depth)
-               die("dumb http transport does not support --depth");
+       if (options.depth || options.deepen_since)
+               die("dumb http transport does not support shallow capabilities");
        for (i = 0; i < nr_heads; i++)
                targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
 
@@ -751,38 +771,35 @@ static int fetch_git(struct discovery *heads,
 {
        struct rpc_state rpc;
        struct strbuf preamble = STRBUF_INIT;
-       char *depth_arg = NULL;
-       int argc = 0, i, err;
-       const char *argv[17];
-
-       argv[argc++] = "fetch-pack";
-       argv[argc++] = "--stateless-rpc";
-       argv[argc++] = "--stdin";
-       argv[argc++] = "--lock-pack";
+       int i, err;
+       struct argv_array args = ARGV_ARRAY_INIT;
+
+       argv_array_pushl(&args, "fetch-pack", "--stateless-rpc",
+                        "--stdin", "--lock-pack", NULL);
        if (options.followtags)
-               argv[argc++] = "--include-tag";
+               argv_array_push(&args, "--include-tag");
        if (options.thin)
-               argv[argc++] = "--thin";
-       if (options.verbosity >= 3) {
-               argv[argc++] = "-v";
-               argv[argc++] = "-v";
-       }
+               argv_array_push(&args, "--thin");
+       if (options.verbosity >= 3)
+               argv_array_pushl(&args, "-v", "-v", NULL);
        if (options.check_self_contained_and_connected)
-               argv[argc++] = "--check-self-contained-and-connected";
+               argv_array_push(&args, "--check-self-contained-and-connected");
        if (options.cloning)
-               argv[argc++] = "--cloning";
+               argv_array_push(&args, "--cloning");
        if (options.update_shallow)
-               argv[argc++] = "--update-shallow";
+               argv_array_push(&args, "--update-shallow");
        if (!options.progress)
-               argv[argc++] = "--no-progress";
-       if (options.depth) {
-               struct strbuf buf = STRBUF_INIT;
-               strbuf_addf(&buf, "--depth=%lu", options.depth);
-               depth_arg = strbuf_detach(&buf, NULL);
-               argv[argc++] = depth_arg;
-       }
-       argv[argc++] = url.buf;
-       argv[argc++] = NULL;
+               argv_array_push(&args, "--no-progress");
+       if (options.depth)
+               argv_array_pushf(&args, "--depth=%lu", options.depth);
+       if (options.deepen_since)
+               argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since);
+       for (i = 0; i < options.deepen_not.nr; i++)
+               argv_array_pushf(&args, "--shallow-exclude=%s",
+                                options.deepen_not.items[i].string);
+       if (options.deepen_relative && options.depth)
+               argv_array_push(&args, "--deepen-relative");
+       argv_array_push(&args, url.buf);
 
        for (i = 0; i < nr_heads; i++) {
                struct ref *ref = to_fetch[i];
@@ -795,7 +812,7 @@ static int fetch_git(struct discovery *heads,
 
        memset(&rpc, 0, sizeof(rpc));
        rpc.service_name = "git-upload-pack",
-       rpc.argv = argv;
+       rpc.argv = args.argv;
        rpc.stdin_preamble = &preamble;
        rpc.gzip_request = 1;
 
@@ -804,7 +821,7 @@ static int fetch_git(struct discovery *heads,
                write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
        strbuf_release(&preamble);
-       free(depth_arg);
+       argv_array_clear(&args);
        return err;
 }
 
@@ -998,6 +1015,7 @@ int cmd_main(int argc, const char **argv)
        options.verbosity = 1;
        options.progress = !!isatty(2);
        options.thin = 1;
+       string_list_init(&options.deepen_not, 1);
 
        remote = remote_get(argv[1]);
 
index 969b3d149f2327a00b159e17fa7f63005aa90dff..b37dbec378f3ae1a33d00d591c1042c609e45fa4 100644 (file)
@@ -1289,12 +1289,14 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
        }
 }
 
-static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
+static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
+                           int exclude_parent)
 {
        unsigned char sha1[20];
        struct object *it;
        struct commit *commit;
        struct commit_list *parents;
+       int parent_number;
        const char *arg = arg_;
 
        if (*arg == '^') {
@@ -1316,7 +1318,15 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
        if (it->type != OBJ_COMMIT)
                return 0;
        commit = (struct commit *)it;
-       for (parents = commit->parents; parents; parents = parents->next) {
+       if (exclude_parent &&
+           exclude_parent > commit_list_count(commit->parents))
+               return 0;
+       for (parents = commit->parents, parent_number = 1;
+            parents;
+            parents = parents->next, parent_number++) {
+               if (exclude_parent && parent_number != exclude_parent)
+                       continue;
+
                it = &parents->item->object;
                it->flags |= flags;
                add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
@@ -1519,17 +1529,33 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                }
                *dotdot = '.';
        }
+
        dotdot = strstr(arg, "^@");
        if (dotdot && !dotdot[2]) {
                *dotdot = 0;
-               if (add_parents_only(revs, arg, flags))
+               if (add_parents_only(revs, arg, flags, 0))
                        return 0;
                *dotdot = '^';
        }
        dotdot = strstr(arg, "^!");
        if (dotdot && !dotdot[2]) {
                *dotdot = 0;
-               if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM)))
+               if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
+                       *dotdot = '^';
+       }
+       dotdot = strstr(arg, "^-");
+       if (dotdot) {
+               int exclude_parent = 1;
+
+               if (dotdot[2]) {
+                       char *end;
+                       exclude_parent = strtoul(dotdot + 2, &end, 10);
+                       if (*end != '\0' || !exclude_parent)
+                               return -1;
+               }
+
+               *dotdot = 0;
+               if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
                        *dotdot = '^';
        }
 
index 75dd6774136258c677fe4f4bbe2124c83b4cea68..7bc4e75d22ce099ca68aa1fac50c9123546ec5b7 100644 (file)
@@ -229,7 +229,7 @@ static void init_pack_info(const char *infofile, int force)
        }
 
        /* renumber them */
-       qsort(info, num_pack, sizeof(info[0]), compare_info);
+       QSORT(info, num_pack, compare_info);
        for (i = 0; i < num_pack; i++)
                info[i]->new_num = i;
 }
index e06b2c1311f72d1023b34c937ab48cd56ae8e206..c3a2b5ad17c74613811a0ce850586fa1b9e099bd 100644 (file)
@@ -230,8 +230,7 @@ cmp_string (const void *pstr1, const void *pstr2)
 static inline void
 string_list_sort (string_list_ty *slp)
 {
-  if (slp->nitems > 0)
-    qsort (slp->item, slp->nitems, sizeof (slp->item[0]), cmp_string);
+  QSORT(slp->item, slp->nitems, cmp_string);
 }
 
 /* Test whether a sorted string list contains a given string.  */
index 6f4a2246c9a912d06cf1a6995e07bf2008ec749d..c1cc25cd95da66aec1c695f4bba9680f5cb834f6 100644 (file)
@@ -16,7 +16,7 @@ static int void_hashcmp(const void *a, const void *b)
 
 static void sha1_array_sort(struct sha1_array *array)
 {
-       qsort(array->sha1, array->nr, sizeof(*array->sha1), void_hashcmp);
+       QSORT(array->sha1, array->nr, void_hashcmp);
        array->sorted = 1;
 }
 
@@ -42,7 +42,7 @@ void sha1_array_clear(struct sha1_array *array)
        array->sorted = 0;
 }
 
-void sha1_array_for_each_unique(struct sha1_array *array,
+int sha1_array_for_each_unique(struct sha1_array *array,
                                for_each_sha1_fn fn,
                                void *data)
 {
@@ -52,8 +52,12 @@ void sha1_array_for_each_unique(struct sha1_array *array,
                sha1_array_sort(array);
 
        for (i = 0; i < array->nr; i++) {
+               int ret;
                if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
                        continue;
-               fn(array->sha1[i], data);
+               ret = fn(array->sha1[i], data);
+               if (ret)
+                       return ret;
        }
+       return 0;
 }
index 72bb33bec6c173633c2b5cd04db57b562fa23a9e..b3230be0dd6eedf871c5c337a79333a7ebf33cb7 100644 (file)
@@ -14,10 +14,10 @@ void sha1_array_append(struct sha1_array *array, const unsigned char *sha1);
 int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
 void sha1_array_clear(struct sha1_array *array);
 
-typedef void (*for_each_sha1_fn)(const unsigned char sha1[20],
-                                void *data);
-void sha1_array_for_each_unique(struct sha1_array *array,
-                               for_each_sha1_fn fn,
+typedef int (*for_each_sha1_fn)(const unsigned char sha1[20],
                                void *data);
+int sha1_array_for_each_unique(struct sha1_array *array,
+                              for_each_sha1_fn fn,
+                              void *data);
 
 #endif /* SHA1_ARRAY_H */
index 064651947dcab37b014309796aa32df0ed944750..266152de36a099bfa75bfaa8429f0234c52a80b1 100644 (file)
@@ -1852,11 +1852,9 @@ static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
 
 int parse_sha1_header(const char *hdr, unsigned long *sizep)
 {
-       struct object_info oi;
+       struct object_info oi = OBJECT_INFO_INIT;
 
        oi.sizep = sizep;
-       oi.typename = NULL;
-       oi.typep = NULL;
        return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
 }
 
@@ -2094,8 +2092,8 @@ static enum object_type packed_to_object_type(struct packed_git *p,
        goto out;
 }
 
-static int packed_object_info(struct packed_git *p, off_t obj_offset,
-                             struct object_info *oi)
+int packed_object_info(struct packed_git *p, off_t obj_offset,
+                      struct object_info *oi)
 {
        struct pack_window *w_curs = NULL;
        unsigned long size;
@@ -2866,7 +2864,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
        enum object_type type;
-       struct object_info oi = {NULL};
+       struct object_info oi = OBJECT_INFO_INIT;
 
        oi.typep = &type;
        oi.sizep = sizep;
index defbb3eb05aa3d82c86549337f9cc56e1f6076bd..409283614679b625f1cb6d191f1a2a32423bbcda 100644 (file)
@@ -7,15 +7,20 @@
 #include "refs.h"
 #include "remote.h"
 #include "dir.h"
+#include "sha1-array.h"
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
 typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
 
 struct disambiguate_state {
+       int len; /* length of prefix in hex chars */
+       char hex_pfx[GIT_SHA1_HEXSZ + 1];
+       unsigned char bin_pfx[GIT_SHA1_RAWSZ];
+
        disambiguate_hint_fn fn;
        void *cb_data;
-       unsigned char candidate[20];
+       unsigned char candidate[GIT_SHA1_RAWSZ];
        unsigned candidate_exists:1;
        unsigned candidate_checked:1;
        unsigned candidate_ok:1;
@@ -72,10 +77,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        /* otherwise, current can be discarded and candidate is still good */
 }
 
-static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
+static void find_short_object_filename(struct disambiguate_state *ds)
 {
        struct alternate_object_database *alt;
-       char hex[40];
+       char hex[GIT_SHA1_HEXSZ];
        static struct alternate_object_database *fakeent;
 
        if (!fakeent) {
@@ -90,13 +95,13 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa
        }
        fakeent->next = alt_odb_list;
 
-       xsnprintf(hex, sizeof(hex), "%.2s", hex_pfx);
+       xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx);
        for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
                struct strbuf *buf = alt_scratch_buf(alt);
                struct dirent *de;
                DIR *dir;
 
-               strbuf_addf(buf, "%.2s/", hex_pfx);
+               strbuf_addf(buf, "%.2s/", ds->hex_pfx);
                dir = opendir(buf->buf);
                if (!dir)
                        continue;
@@ -106,7 +111,7 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa
 
                        if (strlen(de->d_name) != 38)
                                continue;
-                       if (memcmp(de->d_name, hex_pfx + 2, len - 2))
+                       if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2))
                                continue;
                        memcpy(hex + 2, de->d_name, 38);
                        if (!get_sha1_hex(hex, sha1))
@@ -131,9 +136,7 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
        return 1;
 }
 
-static void unique_in_pack(int len,
-                         const unsigned char *bin_pfx,
-                          struct packed_git *p,
+static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
        uint32_t num, last, i, first = 0;
@@ -148,7 +151,7 @@ static void unique_in_pack(int len,
                int cmp;
 
                current = nth_packed_object_sha1(p, mid);
-               cmp = hashcmp(bin_pfx, current);
+               cmp = hashcmp(ds->bin_pfx, current);
                if (!cmp) {
                        first = mid;
                        break;
@@ -167,20 +170,19 @@ static void unique_in_pack(int len,
         */
        for (i = first; i < num && !ds->ambiguous; i++) {
                current = nth_packed_object_sha1(p, i);
-               if (!match_sha(len, bin_pfx, current))
+               if (!match_sha(ds->len, ds->bin_pfx, current))
                        break;
                update_candidates(ds, current);
        }
 }
 
-static void find_short_packed_object(int len, const unsigned char *bin_pfx,
-                                    struct disambiguate_state *ds)
+static void find_short_packed_object(struct disambiguate_state *ds)
 {
        struct packed_git *p;
 
        prepare_packed_git();
        for (p = packed_git; p && !ds->ambiguous; p = p->next)
-               unique_in_pack(len, bin_pfx, p, ds);
+               unique_in_pack(p, ds);
 }
 
 #define SHORT_NAME_NOT_FOUND (-1)
@@ -262,7 +264,7 @@ static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_un
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(sha1), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
@@ -274,14 +276,46 @@ static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unuse
        return kind == OBJ_BLOB;
 }
 
-static int prepare_prefixes(const char *name, int len,
-                           unsigned char *bin_pfx,
-                           char *hex_pfx)
+static disambiguate_hint_fn default_disambiguate_hint;
+
+int set_disambiguate_hint_config(const char *var, const char *value)
+{
+       static const struct {
+               const char *name;
+               disambiguate_hint_fn fn;
+       } hints[] = {
+               { "none", NULL },
+               { "commit", disambiguate_commit_only },
+               { "committish", disambiguate_committish_only },
+               { "tree", disambiguate_tree_only },
+               { "treeish", disambiguate_treeish_only },
+               { "blob", disambiguate_blob_only }
+       };
+       int i;
+
+       if (!value)
+               return config_error_nonbool(var);
+
+       for (i = 0; i < ARRAY_SIZE(hints); i++) {
+               if (!strcasecmp(value, hints[i].name)) {
+                       default_disambiguate_hint = hints[i].fn;
+                       return 0;
+               }
+       }
+
+       return error("unknown hint type for '%s': %s", var, value);
+}
+
+static int init_object_disambiguation(const char *name, int len,
+                                     struct disambiguate_state *ds)
 {
        int i;
 
-       hashclr(bin_pfx);
-       memset(hex_pfx, 'x', 40);
+       if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ)
+               return -1;
+
+       memset(ds, 0, sizeof(*ds));
+
        for (i = 0; i < len ;i++) {
                unsigned char c = name[i];
                unsigned char val;
@@ -295,11 +329,47 @@ static int prepare_prefixes(const char *name, int len,
                }
                else
                        return -1;
-               hex_pfx[i] = c;
+               ds->hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               bin_pfx[i >> 1] |= val;
+               ds->bin_pfx[i >> 1] |= val;
        }
+
+       ds->len = len;
+       ds->hex_pfx[len] = '\0';
+       prepare_alt_odb();
+       return 0;
+}
+
+static int show_ambiguous_object(const unsigned char *sha1, void *data)
+{
+       const struct disambiguate_state *ds = data;
+       struct strbuf desc = STRBUF_INIT;
+       int type;
+
+       if (ds->fn && !ds->fn(sha1, ds->cb_data))
+               return 0;
+
+       type = sha1_object_info(sha1, NULL);
+       if (type == OBJ_COMMIT) {
+               struct commit *commit = lookup_commit(sha1);
+               if (commit) {
+                       struct pretty_print_context pp = {0};
+                       pp.date_mode.type = DATE_SHORT;
+                       format_commit_message(commit, " %ad - %s", &desc, &pp);
+               }
+       } else if (type == OBJ_TAG) {
+               struct tag *tag = lookup_tag(sha1);
+               if (!parse_tag(tag) && tag->tag)
+                       strbuf_addf(&desc, " %s", tag->tag);
+       }
+
+       advise("  %s %s%s",
+              find_unique_abbrev(sha1, DEFAULT_ABBREV),
+              typename(type) ? typename(type) : "unknown type",
+              desc.buf);
+
+       strbuf_release(&desc);
        return 0;
 }
 
@@ -307,19 +377,15 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                          unsigned flags)
 {
        int status;
-       char hex_pfx[40];
-       unsigned char bin_pfx[20];
        struct disambiguate_state ds;
        int quietly = !!(flags & GET_SHA1_QUIETLY);
 
-       if (len < MINIMUM_ABBREV || len > 40)
-               return -1;
-       if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
+       if (init_object_disambiguation(name, len, &ds) < 0)
                return -1;
 
-       prepare_alt_odb();
+       if (HAS_MULTI_BITS(flags & GET_SHA1_DISAMBIGUATORS))
+               die("BUG: multiple get_short_sha1 disambiguator flags");
 
-       memset(&ds, 0, sizeof(ds));
        if (flags & GET_SHA1_COMMIT)
                ds.fn = disambiguate_commit_only;
        else if (flags & GET_SHA1_COMMITTISH)
@@ -330,38 +396,56 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                ds.fn = disambiguate_treeish_only;
        else if (flags & GET_SHA1_BLOB)
                ds.fn = disambiguate_blob_only;
+       else
+               ds.fn = default_disambiguate_hint;
 
-       find_short_object_filename(len, hex_pfx, &ds);
-       find_short_packed_object(len, bin_pfx, &ds);
+       find_short_object_filename(&ds);
+       find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, sha1);
 
-       if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-               return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
+       if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
+               error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
+
+               /*
+                * We may still have ambiguity if we simply saw a series of
+                * candidates that did not satisfy our hint function. In
+                * that case, we still want to show them, so disable the hint
+                * function entirely.
+                */
+               if (!ds.ambiguous)
+                       ds.fn = NULL;
+
+               advise(_("The candidates are:"));
+               for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
+       }
+
        return status;
 }
 
+static int collect_ambiguous(const unsigned char *sha1, void *data)
+{
+       sha1_array_append(data, sha1);
+       return 0;
+}
+
 int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
 {
-       char hex_pfx[40];
-       unsigned char bin_pfx[20];
+       struct sha1_array collect = SHA1_ARRAY_INIT;
        struct disambiguate_state ds;
-       int len = strlen(prefix);
+       int ret;
 
-       if (len < MINIMUM_ABBREV || len > 40)
-               return -1;
-       if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
+       if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
                return -1;
 
-       prepare_alt_odb();
-
-       memset(&ds, 0, sizeof(ds));
        ds.always_call_fn = 1;
-       ds.cb_data = cb_data;
-       ds.fn = fn;
+       ds.fn = collect_ambiguous;
+       ds.cb_data = &collect;
+       find_short_object_filename(&ds);
+       find_short_packed_object(&ds);
 
-       find_short_object_filename(len, hex_pfx, &ds);
-       find_short_packed_object(len, bin_pfx, &ds);
-       return ds.ambiguous;
+       ret = sha1_array_for_each_unique(&collect, fn, cb_data);
+       sha1_array_clear(&collect);
+       return ret;
 }
 
 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
@@ -670,12 +754,12 @@ struct object *peel_to_type(const char *name, int namelen,
        }
 }
 
-static int peel_onion(const char *name, int len, unsigned char *sha1)
+static int peel_onion(const char *name, int len, unsigned char *sha1,
+                     unsigned lookup_flags)
 {
        unsigned char outer[20];
        const char *sp;
        unsigned int expected_type = 0;
-       unsigned lookup_flags = 0;
        struct object *o;
 
        /*
@@ -715,10 +799,11 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        else
                return -1;
 
+       lookup_flags &= ~GET_SHA1_DISAMBIGUATORS;
        if (expected_type == OBJ_COMMIT)
-               lookup_flags = GET_SHA1_COMMITTISH;
+               lookup_flags |= GET_SHA1_COMMITTISH;
        else if (expected_type == OBJ_TREE)
-               lookup_flags = GET_SHA1_TREEISH;
+               lookup_flags |= GET_SHA1_TREEISH;
 
        if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
@@ -819,7 +904,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
                return get_nth_ancestor(name, len1, sha1, num);
        }
 
-       ret = peel_onion(name, len, sha1);
+       ret = peel_onion(name, len, sha1, lookup_flags);
        if (!ret)
                return 0;
 
@@ -1375,6 +1460,9 @@ static int get_sha1_with_context_1(const char *name,
        const char *cp;
        int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
 
+       if (only_to_die)
+               flags |= GET_SHA1_QUIETLY;
+
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
        ret = get_sha1_1(name, namelen, sha1, flags);
@@ -1451,7 +1539,12 @@ static int get_sha1_with_context_1(const char *name,
        if (*cp == ':') {
                unsigned char tree_sha1[20];
                int len = cp - name;
-               if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) {
+               unsigned sub_flags = flags;
+
+               sub_flags &= ~GET_SHA1_DISAMBIGUATORS;
+               sub_flags |= GET_SHA1_TREEISH;
+
+               if (!get_sha1_1(name, len, tree_sha1, sub_flags)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
 
index 54e2db73349ef22d644552c6b122ae5ed6ba2ce1..2531e3af3b0d87e1f78fe09208cb85917ceff207 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -10,6 +10,8 @@
 #include "diff.h"
 #include "revision.h"
 #include "commit-slab.h"
+#include "revision.h"
+#include "list-objects.h"
 
 static int is_shallow = -1;
 static struct stat_validity shallow_stat;
@@ -137,6 +139,82 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
        return result;
 }
 
+static void show_commit(struct commit *commit, void *data)
+{
+       commit_list_insert(commit, data);
+}
+
+/*
+ * Given rev-list arguments, run rev-list. All reachable commits
+ * except border ones are marked with not_shallow_flag. Border commits
+ * are marked with shallow_flag. The list of border/shallow commits
+ * are also returned.
+ */
+struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
+                                                   int shallow_flag,
+                                                   int not_shallow_flag)
+{
+       struct commit_list *result = NULL, *p;
+       struct commit_list *not_shallow_list = NULL;
+       struct rev_info revs;
+       int both_flags = shallow_flag | not_shallow_flag;
+
+       /*
+        * SHALLOW (excluded) and NOT_SHALLOW (included) should not be
+        * set at this point. But better be safe than sorry.
+        */
+       clear_object_flags(both_flags);
+
+       is_repository_shallow(); /* make sure shallows are read */
+
+       init_revisions(&revs, NULL);
+       save_commit_buffer = 0;
+       setup_revisions(ac, av, &revs, NULL);
+
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       traverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);
+
+       /* Mark all reachable commits as NOT_SHALLOW */
+       for (p = not_shallow_list; p; p = p->next)
+               p->item->object.flags |= not_shallow_flag;
+
+       /*
+        * mark border commits SHALLOW + NOT_SHALLOW.
+        * We cannot clear NOT_SHALLOW right now. Imagine border
+        * commit A is processed first, then commit B, whose parent is
+        * A, later. If NOT_SHALLOW on A is cleared at step 1, B
+        * itself is considered border at step 2, which is incorrect.
+        */
+       for (p = not_shallow_list; p; p = p->next) {
+               struct commit *c = p->item;
+               struct commit_list *parent;
+
+               if (parse_commit(c))
+                       die("unable to parse commit %s",
+                           oid_to_hex(&c->object.oid));
+
+               for (parent = c->parents; parent; parent = parent->next)
+                       if (!(parent->item->object.flags & not_shallow_flag)) {
+                               c->object.flags |= shallow_flag;
+                               commit_list_insert(c, &result);
+                               break;
+                       }
+       }
+       free_commit_list(not_shallow_list);
+
+       /*
+        * Now we can clean up NOT_SHALLOW on border commits. Having
+        * both flags set can confuse the caller.
+        */
+       for (p = result; p; p = p->next) {
+               struct object *o = &p->item->object;
+               if ((o->flags & both_flags) == both_flags)
+                       o->flags &= ~not_shallow_flag;
+       }
+       return result;
+}
+
 static void check_shallow_file_for_update(void)
 {
        if (is_shallow == -1)
index 3f017a1c058fb94bc3220be09c384e53e565fb40..9afa66b8be6c3b5481e4542c033bfebdb2abbfda 100644 (file)
@@ -135,7 +135,7 @@ struct git_istream *open_istream(const unsigned char *sha1,
                                 struct stream_filter *filter)
 {
        struct git_istream *st;
-       struct object_info oi = {NULL};
+       struct object_info oi = OBJECT_INFO_INIT;
        const unsigned char *real = lookup_replace_object(sha1);
        enum input_source src = istream_source(real, type, &oi);
 
index 62d20846cbead5aba6a4b3859475b454ad606205..8c83cac189e94c327327e47cfaadfd621d59b6b9 100644 (file)
@@ -225,7 +225,7 @@ static int cmp_items(const void *a, const void *b)
 void string_list_sort(struct string_list *list)
 {
        compare_for_qsort = list->cmp ? list->cmp : strcmp;
-       qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
+       QSORT(list->items, list->nr, cmp_items);
 }
 
 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
index 8b3274a9dcf0854b79475b7a69c35b2a7c2b3bc5..733332035b3c19279885175e0689665a34dc13ef 100644 (file)
@@ -375,7 +375,7 @@ static void show_submodule_header(FILE *f, const char *path,
                        find_unique_abbrev(one->hash, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
-       strbuf_addf(&sb, "%s", find_unique_abbrev(two->hash, DEFAULT_ABBREV));
+       strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
        if (message)
                strbuf_addf(&sb, " %s%s\n", message, reset);
        else
@@ -707,9 +707,10 @@ void check_for_new_submodule_commits(unsigned char new_sha1[20])
        sha1_array_append(&ref_tips_after_fetch, new_sha1);
 }
 
-static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
+static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
 {
        argv_array_push(data, sha1_to_hex(sha1));
+       return 0;
 }
 
 static void calculate_changed_submodule_paths(void)
index 50112cc8586c75cf9f9b5fac65ea2f7dbc1a69e2..f752532ffbcd130c3c3cb25ae20da41d4f74ae68 100644 (file)
@@ -18,10 +18,8 @@ static int compare_dir(const void *a_, const void *b_)
 static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
 {
        int i, len;
-       qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked),
-             compare_untracked);
-       qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs),
-             compare_dir);
+       QSORT(ucd->untracked, ucd->untracked_nr, compare_untracked);
+       QSORT(ucd->dirs, ucd->dirs_nr, compare_dir);
        len = base->len;
        strbuf_addf(base, "%s/", ucd->name);
        printf("%s %s", base->buf,
index 09f77909716326bdb76a79d3c32d10b74702e1d5..f7a53c4ad64c18903f53c5bd1d4766f54bc22e49 100644 (file)
@@ -1,9 +1,10 @@
 #include "cache.h"
 #include "sha1-array.h"
 
-static void print_sha1(const unsigned char sha1[20], void *data)
+static int print_sha1(const unsigned char sha1[20], void *data)
 {
        puts(sha1_to_hex(sha1));
+       return 0;
 }
 
 int cmd_main(int argc, const char **argv)
index e221167cfbe3fb34cf5aba88cd04f6a1dc9eede5..7c659eb5852e986dc5605e082f773e68e746c789 100755 (executable)
@@ -264,6 +264,13 @@ test_expect_success 'ambiguous commit-ish' '
        test_must_fail git log 000000000...
 '
 
+# There are three objects with this prefix: a blob, a tree, and a tag. We know
+# the blob will not pass as a treeish, but the tree and tag should (and thus
+# cause an error).
+test_expect_success 'ambiguous tags peel to treeish' '
+       test_must_fail git rev-parse 0000000000f^{tree}
+'
+
 test_expect_success 'rev-parse --disambiguate' '
        # The test creates 16 objects that share the prefix and two
        # commits created by commit-tree in earlier tests share a
@@ -273,6 +280,13 @@ test_expect_success 'rev-parse --disambiguate' '
        test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
 '
 
+test_expect_success 'rev-parse --disambiguate drops duplicates' '
+       git rev-parse --disambiguate=000000000 >expect &&
+       git pack-objects .git/objects/pack/pack <expect &&
+       git rev-parse --disambiguate=000000000 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'ambiguous 40-hex ref' '
        TREE=$(git mktree </dev/null) &&
        REF=$(git rev-parse HEAD) &&
@@ -291,4 +305,60 @@ test_expect_success 'ambiguous short sha1 ref' '
        grep "refname.*${REF}.*ambiguous" err
 '
 
+test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' '
+       test_must_fail git rev-parse 00000 2>stderr &&
+       grep "is ambiguous" stderr >errors &&
+       test_line_count = 1 errors
+'
+
+test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' '
+       test_must_fail git rev-parse 00000:foo 2>stderr &&
+       grep "is ambiguous" stderr >errors &&
+       test_line_count = 1 errors
+'
+
+test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' '
+       test_must_fail git rev-parse 00000^{commit} 2>stderr &&
+       grep "is ambiguous" stderr >errors &&
+       test_line_count = 1 errors
+'
+
+test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '
+       test_must_fail git rev-parse 000000000 2>stderr &&
+       grep ^hint: stderr >hints &&
+       # 16 candidates, plus one intro line
+       test_line_count = 17 hints
+'
+
+test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '
+       test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
+       grep ^hint: stderr >hints &&
+       # 5 commits, 1 tag (which is a commitish), plus intro line
+       test_line_count = 7 hints
+'
+
+test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' '
+       # these two blobs share the same prefix "ee3d", but neither
+       # will pass for a commit
+       echo 851 | git hash-object --stdin -w &&
+       echo 872 | git hash-object --stdin -w &&
+       test_must_fail git rev-parse ee3d^{commit} 2>stderr &&
+       grep ^hint: stderr >hints &&
+       test_line_count = 3 hints
+'
+
+test_expect_success 'core.disambiguate config can prefer types' '
+       # ambiguous between tree and tag
+       sha1=0000000000f &&
+       test_must_fail git rev-parse $sha1 &&
+       git rev-parse $sha1^{commit} &&
+       git -c core.disambiguate=committish rev-parse $sha1
+'
+
+test_expect_success 'core.disambiguate does not override context' '
+       # treeish ambiguous between tag and tree
+       test_must_fail \
+               git -c core.disambiguate=committish rev-parse $sha1^{tree}
+'
+
 test_done
diff --git a/t/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh
new file mode 100755 (executable)
index 0000000..f7dbdfb
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+test_description='test handling of inter-pack delta cycles during repack
+
+The goal here is to create a situation where we have two blobs, A and B, with A
+as a delta against B in one pack, and vice versa in the other. Then if we can
+persuade a full repack to find A from one pack and B from the other, that will
+give us a cycle when we attempt to reuse those deltas.
+
+The trick is in the "persuade" step, as it depends on the internals of how
+pack-objects picks which pack to reuse the deltas from. But we can assume
+that it does so in one of two general strategies:
+
+ 1. Using a static ordering of packs. In this case, no inter-pack cycles can
+    happen. Any objects with a delta relationship must be present in the same
+    pack (i.e., no "--thin" packs on disk), so we will find all related objects
+    from that pack. So assuming there are no cycles within a single pack (and
+    we avoid generating them via pack-objects or importing them via
+    index-pack), then our result will have no cycles.
+
+    So this case should pass the tests no matter how we arrange things.
+
+ 2. Picking the next pack to examine based on locality (i.e., where we found
+    something else recently).
+
+    In this case, we want to make sure that we find the delta versions of A and
+    B and not their base versions. We can do this by putting two blobs in each
+    pack. The first is a "dummy" blob that can only be found in the pack in
+    question.  And then the second is the actual delta we want to find.
+
+    The two blobs must be present in the same tree, not present in other trees,
+    and the dummy pathname must sort before the delta path.
+
+The setup below focuses on case 2. We have two commits HEAD and HEAD^, each
+which has two files: "dummy" and "file". Then we can make two packs which
+contain:
+
+  [pack one]
+  HEAD:dummy
+  HEAD:file  (as delta against HEAD^:file)
+  HEAD^:file (as base)
+
+  [pack two]
+  HEAD^:dummy
+  HEAD^:file (as delta against HEAD:file)
+  HEAD:file  (as base)
+
+Then no matter which order we start looking at the packs in, we know that we
+will always find a delta for "file", because its lookup will always come
+immediately after the lookup for "dummy".
+'
+. ./test-lib.sh
+
+
+
+# Create a pack containing the the tree $1 and blob $1:file, with
+# the latter stored as a delta against $2:file.
+#
+# We convince pack-objects to make the delta in the direction of our choosing
+# by marking $2 as a preferred-base edge. That results in $1:file as a thin
+# delta, and index-pack completes it by adding $2:file as a base.
+#
+# Note that the two variants of "file" must be similar enough to convince git
+# to create the delta.
+make_pack () {
+       {
+               printf '%s\n' "-$(git rev-parse $2)"
+               printf '%s dummy\n' "$(git rev-parse $1:dummy)"
+               printf '%s file\n' "$(git rev-parse $1:file)"
+       } |
+       git pack-objects --stdout |
+       git index-pack --stdin --fix-thin
+}
+
+test_expect_success 'setup' '
+       test-genrandom base 4096 >base &&
+       for i in one two
+       do
+               # we want shared content here to encourage deltas...
+               cp base file &&
+               echo $i >>file &&
+
+               # ...whereas dummy should be short, because we do not want
+               # deltas that would create duplicates when we --fix-thin
+               echo $i >dummy &&
+
+               git add file dummy &&
+               test_tick &&
+               git commit -m $i ||
+               return 1
+       done &&
+
+       make_pack HEAD^ HEAD &&
+       make_pack HEAD HEAD^
+'
+
+test_expect_success 'repack' '
+       # We first want to check that we do not have any internal errors,
+       # and also that we do not hit the last-ditch cycle-breaking code
+       # in write_object(), which will issue a warning to stderr.
+       >expect &&
+       git repack -ad 2>stderr &&
+       test_cmp expect stderr &&
+
+       # And then double-check that the resulting pack is usable (i.e.,
+       # we did not fail to notice any cycles). We know we are accessing
+       # the objects via the new pack here, because "repack -d" will have
+       # removed the others.
+       git cat-file blob HEAD:file >/dev/null &&
+       git cat-file blob HEAD^:file >/dev/null
+'
+
+test_done
index 82d913a6a87c45dd8141e3c7384df9943f9b83cb..505e1b4a7f421d377aa3798e94c90c14249bce91 100755 (executable)
@@ -652,4 +652,72 @@ test_expect_success MINGW 'fetch-pack --diag-url c:repo' '
        check_prot_path c:repo file c:repo
 '
 
+test_expect_success 'clone shallow since ...' '
+       test_create_repo shallow-since &&
+       (
+       cd shallow-since &&
+       GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+       GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+       GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+       git clone --shallow-since "300000000 +0700" "file://$(pwd)/." ../shallow11 &&
+       git -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&
+       echo three >expected &&
+       test_cmp expected actual
+       )
+'
+
+test_expect_success 'fetch shallow since ...' '
+       git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+       git -C shallow11 log --pretty=tformat:%s origin/master >actual &&
+       cat >expected <<-\EOF &&
+       three
+       two
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'shallow clone exclude tag two' '
+       test_create_repo shallow-exclude &&
+       (
+       cd shallow-exclude &&
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       git clone --shallow-exclude two "file://$(pwd)/." ../shallow12 &&
+       git -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&
+       echo three >expected &&
+       test_cmp expected actual
+       )
+'
+
+test_expect_success 'fetch exclude tag one' '
+       git -C shallow12 fetch --shallow-exclude one origin &&
+       git -C shallow12 log --pretty=tformat:%s origin/master >actual &&
+       test_write_lines three two >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'fetching deepen' '
+       test_create_repo shallow-deepen &&
+       (
+       cd shallow-deepen &&
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       git clone --depth 1 "file://$(pwd)/." deepen &&
+       test_commit four &&
+       git -C deepen log --pretty=tformat:%s master >actual &&
+       echo three >expected &&
+       test_cmp expected actual &&
+       git -C deepen fetch --deepen=1 &&
+       git -C deepen log --pretty=tformat:%s origin/master >actual &&
+       cat >expected <<-\EOF &&
+       four
+       three
+       two
+       EOF
+       test_cmp expected actual
+       )
+'
+
 test_done
index 37a433504e27f8dffc9bbbb9e94d550de20ff035..5fbf67c4468897184f46ef9ea151f1dd6ad1d8d0 100755 (executable)
@@ -73,5 +73,78 @@ test_expect_success 'no shallow lines after receiving ACK ready' '
        )
 '
 
+test_expect_success 'clone shallow since ...' '
+       test_create_repo shallow-since &&
+       (
+       cd shallow-since &&
+       GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+       GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+       GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+       mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-since.git" &&
+       git clone --shallow-since "300000000 +0700" $HTTPD_URL/smart/shallow-since.git ../shallow11 &&
+       git -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&
+       echo three >expected &&
+       test_cmp expected actual
+       )
+'
+
+test_expect_success 'fetch shallow since ...' '
+       git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+       git -C shallow11 log --pretty=tformat:%s origin/master >actual &&
+       cat >expected <<-\EOF &&
+       three
+       two
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'shallow clone exclude tag two' '
+       test_create_repo shallow-exclude &&
+       (
+       cd shallow-exclude &&
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-exclude.git" &&
+       git clone --shallow-exclude two $HTTPD_URL/smart/shallow-exclude.git ../shallow12 &&
+       git -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&
+       echo three >expected &&
+       test_cmp expected actual
+       )
+'
+
+test_expect_success 'fetch exclude tag one' '
+       git -C shallow12 fetch --shallow-exclude one origin &&
+       git -C shallow12 log --pretty=tformat:%s origin/master >actual &&
+       test_write_lines three two >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'fetching deepen' '
+       test_create_repo shallow-deepen &&
+       (
+       cd shallow-deepen &&
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" &&
+       git clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen &&
+       mv "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" .git &&
+       test_commit four &&
+       git -C deepen log --pretty=tformat:%s master >actual &&
+       echo three >expected &&
+       test_cmp expected actual &&
+       mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" &&
+       git -C deepen fetch --deepen=1 &&
+       git -C deepen log --pretty=tformat:%s origin/master >actual &&
+       cat >expected <<-\EOF &&
+       four
+       three
+       two
+       EOF
+       test_cmp expected actual
+       )
+'
+
 stop_httpd
 test_done
index f6020cd2aa9b726f11ef0b93ecb2ce8227a1017c..a1dcdb81d789cfa2d0e8fcc178311a807a608771 100755 (executable)
@@ -225,7 +225,7 @@ test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
 
 test_expect_success '%C(auto) respects --color' '
        git log --color --format="%C(auto)%H" -1 >actual &&
-       printf "\\033[m\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
+       printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
        test_cmp expect actual
 '
 
index 1c6952d04936b190c9cf42024519273b00747113..64a9850e3197dc2cbff00593f04a5fa196dbd632 100755 (executable)
@@ -102,4 +102,98 @@ test_expect_success 'short SHA-1 works' '
        test_cmp_rev_output start "git rev-parse ${start%?}"
 '
 
+# rev^- tests; we can use a simpler setup for these
+
+test_expect_success 'setup for rev^- tests' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+
+       # Merge in a branch for testing rev^-
+       git checkout -b branch &&
+       git checkout HEAD^^ &&
+       git merge -m merge --no-edit --no-ff branch &&
+       git checkout -b merge
+'
+
+# The merged branch has 2 commits + the merge
+test_expect_success 'rev-list --count merge^- = merge^..merge' '
+       git rev-list --count merge^..merge >expect &&
+       echo 3 >actual &&
+       test_cmp expect actual
+'
+
+# All rev^- rev-parse tests
+
+test_expect_success 'rev-parse merge^- = merge^..merge' '
+       git rev-parse merge^..merge >expect &&
+       git rev-parse merge^- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse merge^-1 = merge^..merge' '
+       git rev-parse merge^1..merge >expect &&
+       git rev-parse merge^-1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse merge^-2 = merge^2..merge' '
+       git rev-parse merge^2..merge >expect &&
+       git rev-parse merge^-2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse merge^-0 (invalid parent)' '
+       test_must_fail git rev-parse merge^-0
+'
+
+test_expect_success 'rev-parse merge^-3 (invalid parent)' '
+       test_must_fail git rev-parse merge^-3
+'
+
+test_expect_success 'rev-parse merge^-^ (garbage after ^-)' '
+       test_must_fail git rev-parse merge^-^
+'
+
+test_expect_success 'rev-parse merge^-1x (garbage after ^-1)' '
+       test_must_fail git rev-parse merge^-1x
+'
+
+# All rev^- rev-list tests (should be mostly the same as rev-parse; the reason
+# for the duplication is that rev-parse and rev-list use different parsers).
+
+test_expect_success 'rev-list merge^- = merge^..merge' '
+       git rev-list merge^..merge >expect &&
+       git rev-list merge^- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list merge^-1 = merge^1..merge' '
+       git rev-list merge^1..merge >expect &&
+       git rev-list merge^-1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list merge^-2 = merge^2..merge' '
+       git rev-list merge^2..merge >expect &&
+       git rev-list merge^-2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list merge^-0 (invalid parent)' '
+       test_must_fail git rev-list merge^-0
+'
+
+test_expect_success 'rev-list merge^-3 (invalid parent)' '
+       test_must_fail git rev-list merge^-3
+'
+
+test_expect_success 'rev-list merge^-^ (garbage after ^-)' '
+       test_must_fail git rev-list merge^-^
+'
+
+test_expect_success 'rev-list merge^-1x (garbage after ^-1)' '
+       test_must_fail git rev-list merge^-1x
+'
+
 test_done
index db2f930c740348bbe55525a2e471857b6dc463f1..91aed35ebbc3cbb4fb9acbad55ab3de69e62d9f5 100644 (file)
@@ -258,8 +258,51 @@ static const char *boolean_options[] = {
        TRANS_OPT_THIN,
        TRANS_OPT_KEEP,
        TRANS_OPT_FOLLOWTAGS,
+       TRANS_OPT_DEEPEN_RELATIVE
        };
 
+static int strbuf_set_helper_option(struct helper_data *data,
+                                   struct strbuf *buf)
+{
+       int ret;
+
+       sendline(data, buf);
+       if (recvline(data, buf))
+               exit(128);
+
+       if (!strcmp(buf->buf, "ok"))
+               ret = 0;
+       else if (starts_with(buf->buf, "error"))
+               ret = -1;
+       else if (!strcmp(buf->buf, "unsupported"))
+               ret = 1;
+       else {
+               warning("%s unexpectedly said: '%s'", data->name, buf->buf);
+               ret = 1;
+       }
+       return ret;
+}
+
+static int string_list_set_helper_option(struct helper_data *data,
+                                        const char *name,
+                                        struct string_list *list)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int i, ret = 0;
+
+       for (i = 0; i < list->nr; i++) {
+               strbuf_addf(&buf, "option %s ", name);
+               quote_c_style(list->items[i].string, &buf, NULL, 0);
+               strbuf_addch(&buf, '\n');
+
+               if ((ret = strbuf_set_helper_option(data, &buf)))
+                       break;
+               strbuf_reset(&buf);
+       }
+       strbuf_release(&buf);
+       return ret;
+}
+
 static int set_helper_option(struct transport *transport,
                          const char *name, const char *value)
 {
@@ -272,6 +315,10 @@ static int set_helper_option(struct transport *transport,
        if (!data->option)
                return 1;
 
+       if (!strcmp(name, "deepen-not"))
+               return string_list_set_helper_option(data, name,
+                                                    (struct string_list *)value);
+
        for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
                if (!strcmp(name, unsupported_options[i]))
                        return 1;
@@ -291,20 +338,7 @@ static int set_helper_option(struct transport *transport,
                quote_c_style(value, &buf, NULL, 0);
        strbuf_addch(&buf, '\n');
 
-       sendline(data, &buf);
-       if (recvline(data, &buf))
-               exit(128);
-
-       if (!strcmp(buf.buf, "ok"))
-               ret = 0;
-       else if (starts_with(buf.buf, "error")) {
-               ret = -1;
-       } else if (!strcmp(buf.buf, "unsupported"))
-               ret = 1;
-       else {
-               warning("%s unexpectedly said: '%s'", data->name, buf.buf);
-               ret = 1;
-       }
+       ret = strbuf_set_helper_option(data, &buf);
        strbuf_release(&buf);
        return ret;
 }
index 4bc4eeae54ea96bbe48658eb3e6718590fee4350..079499dbafd749aaaebcac96d717893c8919ef23 100644 (file)
@@ -151,6 +151,15 @@ static int set_git_option(struct git_transport_options *opts,
                                die(_("transport: invalid depth option '%s'"), value);
                }
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {
+               opts->deepen_since = value;
+               return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
+               opts->deepen_not = (const struct string_list *)value;
+               return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
+               opts->deepen_relative = !!value;
+               return 0;
        }
        return 1;
 }
@@ -211,6 +220,9 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.quiet = (transport->verbose < 0);
        args.no_progress = !transport->progress;
        args.depth = data->options.depth;
+       args.deepen_since = data->options.deepen_since;
+       args.deepen_not = data->options.deepen_not;
+       args.deepen_relative = data->options.deepen_relative;
        args.check_self_contained_and_connected =
                data->options.check_self_contained_and_connected;
        args.cloning = transport->cloning;
index 6fe3485325dfccfba3b018c8c25b89a6d4643a12..68669f14d08f52d15f476fe97a813044a0d07dda 100644 (file)
@@ -5,6 +5,8 @@
 #include "run-command.h"
 #include "remote.h"
 
+struct string_list;
+
 struct git_transport_options {
        unsigned thin : 1;
        unsigned keep : 1;
@@ -12,7 +14,10 @@ struct git_transport_options {
        unsigned check_self_contained_and_connected : 1;
        unsigned self_contained_and_connected : 1;
        unsigned update_shallow : 1;
+       unsigned deepen_relative : 1;
        int depth;
+       const char *deepen_since;
+       const struct string_list *deepen_not;
        const char *uploadpack;
        const char *receivepack;
        struct push_cas_option *cas;
@@ -186,6 +191,15 @@ int transport_restrict_protocols(void);
 /* Limit the depth of the fetch if not null */
 #define TRANS_OPT_DEPTH "depth"
 
+/* Limit the depth of the fetch based on time if not null */
+#define TRANS_OPT_DEEPEN_SINCE "deepen-since"
+
+/* Limit the depth of the fetch based on revs if not null */
+#define TRANS_OPT_DEEPEN_NOT "deepen-not"
+
+/* Limit the deepen of the fetch if not null */
+#define TRANS_OPT_DEEPEN_RELATIVE "deepen-relative"
+
 /* Aggressively fetch annotated tags if possible */
 #define TRANS_OPT_FOLLOWTAGS "followtags"
 
diff --git a/tree.c b/tree.c
index 2b5a5a8663b6c1a8dcd92271aaf145c09352a43c..ce345c551117215882062f99c931b2182a63931a 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -180,8 +180,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match)
         * Sort the cache entry -- we need to nuke the cache tree, though.
         */
        cache_tree_free(&active_cache_tree);
-       qsort(active_cache, active_nr, sizeof(active_cache[0]),
-             cmp_cache_name_compare);
+       QSORT(active_cache, active_nr, cmp_cache_name_compare);
        return 0;
 }
 
index ca7f9417800bfb27a7dbc0559df8bec772c267fa..5ec21e61d916999f4639162b8086115ad382f877 100644 (file)
@@ -15,6 +15,7 @@
 #include "version.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "argv-array.h"
 
 static const char * const upload_pack_usage[] = {
        N_("git upload-pack [<options>] <dir>"),
@@ -35,6 +36,7 @@ static const char * const upload_pack_usage[] = {
 
 static unsigned long oldest_have;
 
+static int deepen_relative;
 static int multi_ack;
 static int no_done;
 static int use_thin_pack, use_ofs_delta, use_include_tag;
@@ -281,7 +283,7 @@ static void create_pack_file(void)
        die("git upload-pack: %s", abort_msg);
 }
 
-static int got_sha1(char *hex, unsigned char *sha1)
+static int got_sha1(const char *hex, unsigned char *sha1)
 {
        struct object *o;
        int we_knew_they_have = 0;
@@ -387,6 +389,8 @@ static int get_common_commits(void)
 
        for (;;) {
                char *line = packet_read_line(0, NULL);
+               const char *arg;
+
                reset_timeout();
 
                if (!line) {
@@ -408,8 +412,8 @@ static int get_common_commits(void)
                        got_other = 0;
                        continue;
                }
-               if (starts_with(line, "have ")) {
-                       switch (got_sha1(line+5, sha1)) {
+               if (skip_prefix(line, "have ", &arg)) {
+                       switch (got_sha1(arg, sha1)) {
                        case -1: /* they have what we do not */
                                got_other = 1;
                                if (multi_ack && ok_to_give_up()) {
@@ -454,73 +458,136 @@ static int is_our_ref(struct object *o)
        return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
 }
 
-static void check_non_tip(void)
+/*
+ * on successful case, it's up to the caller to close cmd->out
+ */
+static int do_reachable_revlist(struct child_process *cmd,
+                               struct object_array *src,
+                               struct object_array *reachable)
 {
        static const char *argv[] = {
                "rev-list", "--stdin", NULL,
        };
-       static struct child_process cmd = CHILD_PROCESS_INIT;
        struct object *o;
        char namebuf[42]; /* ^ + SHA-1 + LF */
        int i;
 
-       /*
-        * In the normal in-process case without
-        * uploadpack.allowReachableSHA1InWant,
-        * non-tip requests can never happen.
-        */
-       if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
-               goto error;
-
-       cmd.argv = argv;
-       cmd.git_cmd = 1;
-       cmd.no_stderr = 1;
-       cmd.in = -1;
-       cmd.out = -1;
-
-       if (start_command(&cmd))
-               goto error;
+       cmd->argv = argv;
+       cmd->git_cmd = 1;
+       cmd->no_stderr = 1;
+       cmd->in = -1;
+       cmd->out = -1;
 
        /*
-        * If rev-list --stdin encounters an unknown commit, it
-        * terminates, which will cause SIGPIPE in the write loop
+        * If the next rev-list --stdin encounters an unknown commit,
+        * it terminates, which will cause SIGPIPE in the write loop
         * below.
         */
        sigchain_push(SIGPIPE, SIG_IGN);
 
+       if (start_command(cmd))
+               goto error;
+
        namebuf[0] = '^';
        namebuf[41] = '\n';
        for (i = get_max_object_index(); 0 < i; ) {
                o = get_indexed_object(--i);
                if (!o)
                        continue;
+               if (reachable && o->type == OBJ_COMMIT)
+                       o->flags &= ~TMP_MARK;
                if (!is_our_ref(o))
                        continue;
                memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd.in, namebuf, 42) < 0)
+               if (write_in_full(cmd->in, namebuf, 42) < 0)
                        goto error;
        }
        namebuf[40] = '\n';
-       for (i = 0; i < want_obj.nr; i++) {
-               o = want_obj.objects[i].item;
-               if (is_our_ref(o))
+       for (i = 0; i < src->nr; i++) {
+               o = src->objects[i].item;
+               if (is_our_ref(o)) {
+                       if (reachable)
+                               add_object_array(o, NULL, reachable);
                        continue;
+               }
+               if (reachable && o->type == OBJ_COMMIT)
+                       o->flags |= TMP_MARK;
                memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
-               if (write_in_full(cmd.in, namebuf, 41) < 0)
+               if (write_in_full(cmd->in, namebuf, 41) < 0)
                        goto error;
        }
-       close(cmd.in);
+       close(cmd->in);
+       cmd->in = -1;
+       sigchain_pop(SIGPIPE);
 
+       return 0;
+
+error:
        sigchain_pop(SIGPIPE);
 
+       if (cmd->in >= 0)
+               close(cmd->in);
+       if (cmd->out >= 0)
+               close(cmd->out);
+       return -1;
+}
+
+static int get_reachable_list(struct object_array *src,
+                             struct object_array *reachable)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       int i;
+       struct object *o;
+       char namebuf[42]; /* ^ + SHA-1 + LF */
+
+       if (do_reachable_revlist(&cmd, src, reachable) < 0)
+               return -1;
+
+       while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {
+               struct object_id sha1;
+
+               if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1))
+                       break;
+
+               o = lookup_object(sha1.hash);
+               if (o && o->type == OBJ_COMMIT) {
+                       o->flags &= ~TMP_MARK;
+               }
+       }
+       for (i = get_max_object_index(); 0 < i; i--) {
+               o = get_indexed_object(i - 1);
+               if (o && o->type == OBJ_COMMIT &&
+                   (o->flags & TMP_MARK)) {
+                       add_object_array(o, NULL, reachable);
+                               o->flags &= ~TMP_MARK;
+               }
+       }
+       close(cmd.out);
+
+       if (finish_command(&cmd))
+               return -1;
+
+       return 0;
+}
+
+static int has_unreachable(struct object_array *src)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       char buf[1];
+       int i;
+
+       if (do_reachable_revlist(&cmd, src, NULL) < 0)
+               return 1;
+
        /*
         * The commits out of the rev-list are not ancestors of
         * our ref.
         */
-       i = read_in_full(cmd.out, namebuf, 1);
+       i = read_in_full(cmd.out, buf, 1);
        if (i)
                goto error;
        close(cmd.out);
+       cmd.out = -1;
 
        /*
         * rev-list may have died by encountering a bad commit
@@ -531,23 +598,142 @@ static void check_non_tip(void)
                goto error;
 
        /* All the non-tip ones are ancestors of what we advertised */
-       return;
+       return 0;
+
+error:
+       sigchain_pop(SIGPIPE);
+       if (cmd.out >= 0)
+               close(cmd.out);
+       return 1;
+}
+
+static void check_non_tip(void)
+{
+       int i;
+
+       /*
+        * In the normal in-process case without
+        * uploadpack.allowReachableSHA1InWant,
+        * non-tip requests can never happen.
+        */
+       if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
+               goto error;
+       if (!has_unreachable(&want_obj))
+               /* All the non-tip ones are ancestors of what we advertised */
+               return;
 
 error:
        /* Pick one of them (we know there at least is one) */
        for (i = 0; i < want_obj.nr; i++) {
-               o = want_obj.objects[i].item;
+               struct object *o = want_obj.objects[i].item;
                if (!is_our_ref(o))
                        die("git upload-pack: not our ref %s",
                            oid_to_hex(&o->oid));
        }
 }
 
+static void send_shallow(struct commit_list *result)
+{
+       while (result) {
+               struct object *object = &result->item->object;
+               if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
+                       packet_write(1, "shallow %s",
+                                    oid_to_hex(&object->oid));
+                       register_shallow(object->oid.hash);
+                       shallow_nr++;
+               }
+               result = result->next;
+       }
+}
+
+static void send_unshallow(const struct object_array *shallows)
+{
+       int i;
+
+       for (i = 0; i < shallows->nr; i++) {
+               struct object *object = shallows->objects[i].item;
+               if (object->flags & NOT_SHALLOW) {
+                       struct commit_list *parents;
+                       packet_write(1, "unshallow %s",
+                                    oid_to_hex(&object->oid));
+                       object->flags &= ~CLIENT_SHALLOW;
+                       /*
+                        * We want to _register_ "object" as shallow, but we
+                        * also need to traverse object's parents to deepen a
+                        * shallow clone. Unregister it for now so we can
+                        * parse and add the parents to the want list, then
+                        * re-register it.
+                        */
+                       unregister_shallow(object->oid.hash);
+                       object->parsed = 0;
+                       parse_commit_or_die((struct commit *)object);
+                       parents = ((struct commit *)object)->parents;
+                       while (parents) {
+                               add_object_array(&parents->item->object,
+                                                NULL, &want_obj);
+                               parents = parents->next;
+                       }
+                       add_object_array(object, NULL, &extra_edge_obj);
+               }
+               /* make sure commit traversal conforms to client */
+               register_shallow(object->oid.hash);
+       }
+}
+
+static void deepen(int depth, int deepen_relative,
+                  struct object_array *shallows)
+{
+       if (depth == INFINITE_DEPTH && !is_repository_shallow()) {
+               int i;
+
+               for (i = 0; i < shallows->nr; i++) {
+                       struct object *object = shallows->objects[i].item;
+                       object->flags |= NOT_SHALLOW;
+               }
+       } else if (deepen_relative) {
+               struct object_array reachable_shallows = OBJECT_ARRAY_INIT;
+               struct commit_list *result;
+
+               get_reachable_list(shallows, &reachable_shallows);
+               result = get_shallow_commits(&reachable_shallows,
+                                            depth + 1,
+                                            SHALLOW, NOT_SHALLOW);
+               send_shallow(result);
+               free_commit_list(result);
+               object_array_clear(&reachable_shallows);
+       } else {
+               struct commit_list *result;
+
+               result = get_shallow_commits(&want_obj, depth,
+                                            SHALLOW, NOT_SHALLOW);
+               send_shallow(result);
+               free_commit_list(result);
+       }
+
+       send_unshallow(shallows);
+       packet_flush(1);
+}
+
+static void deepen_by_rev_list(int ac, const char **av,
+                              struct object_array *shallows)
+{
+       struct commit_list *result;
+
+       result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
+       send_shallow(result);
+       free_commit_list(result);
+       send_unshallow(shallows);
+       packet_flush(1);
+}
+
 static void receive_needs(void)
 {
        struct object_array shallows = OBJECT_ARRAY_INIT;
+       struct string_list deepen_not = STRING_LIST_INIT_DUP;
        int depth = 0;
        int has_non_tip = 0;
+       unsigned long deepen_since = 0;
+       int deepen_rev_list = 0;
 
        shallow_nr = 0;
        for (;;) {
@@ -555,14 +741,16 @@ static void receive_needs(void)
                const char *features;
                unsigned char sha1_buf[20];
                char *line = packet_read_line(0, NULL);
+               const char *arg;
+
                reset_timeout();
                if (!line)
                        break;
 
-               if (starts_with(line, "shallow ")) {
+               if (skip_prefix(line, "shallow ", &arg)) {
                        unsigned char sha1[20];
                        struct object *object;
-                       if (get_sha1_hex(line + 8, sha1))
+                       if (get_sha1_hex(arg, sha1))
                                die("invalid shallow line: %s", line);
                        object = parse_object(sha1);
                        if (!object)
@@ -575,20 +763,42 @@ static void receive_needs(void)
                        }
                        continue;
                }
-               if (starts_with(line, "deepen ")) {
-                       char *end;
-                       depth = strtol(line + 7, &end, 0);
-                       if (end == line + 7 || depth <= 0)
+               if (skip_prefix(line, "deepen ", &arg)) {
+                       char *end = NULL;
+                       depth = strtol(arg, &end, 0);
+                       if (!end || *end || depth <= 0)
                                die("Invalid deepen: %s", line);
                        continue;
                }
-               if (!starts_with(line, "want ") ||
-                   get_sha1_hex(line+5, sha1_buf))
+               if (skip_prefix(line, "deepen-since ", &arg)) {
+                       char *end = NULL;
+                       deepen_since = strtoul(arg, &end, 0);
+                       if (!end || *end || !deepen_since ||
+                           /* revisions.c's max_age -1 is special */
+                           deepen_since == -1)
+                               die("Invalid deepen-since: %s", line);
+                       deepen_rev_list = 1;
+                       continue;
+               }
+               if (skip_prefix(line, "deepen-not ", &arg)) {
+                       char *ref = NULL;
+                       unsigned char sha1[20];
+                       if (expand_ref(arg, strlen(arg), sha1, &ref) != 1)
+                               die("git upload-pack: ambiguous deepen-not: %s", line);
+                       string_list_append(&deepen_not, ref);
+                       free(ref);
+                       deepen_rev_list = 1;
+                       continue;
+               }
+               if (!skip_prefix(line, "want ", &arg) ||
+                   get_sha1_hex(arg, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
 
-               features = line + 45;
+               features = arg + 40;
 
+               if (parse_feature_request(features, "deepen-relative"))
+                       deepen_relative = 1;
                if (parse_feature_request(features, "multi_ack_detailed"))
                        multi_ack = 2;
                else if (parse_feature_request(features, "multi_ack"))
@@ -633,55 +843,35 @@ static void receive_needs(void)
        if (!use_sideband && daemon_mode)
                no_progress = 1;
 
-       if (depth == 0 && shallows.nr == 0)
+       if (depth == 0 && !deepen_rev_list && shallows.nr == 0)
                return;
-       if (depth > 0) {
-               struct commit_list *result = NULL, *backup = NULL;
+       if (depth > 0 && deepen_rev_list)
+               die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
+       if (depth > 0)
+               deepen(depth, deepen_relative, &shallows);
+       else if (deepen_rev_list) {
+               struct argv_array av = ARGV_ARRAY_INIT;
                int i;
-               if (depth == INFINITE_DEPTH && !is_repository_shallow())
-                       for (i = 0; i < shallows.nr; i++) {
-                               struct object *object = shallows.objects[i].item;
-                               object->flags |= NOT_SHALLOW;
-                       }
-               else
-                       backup = result =
-                               get_shallow_commits(&want_obj, depth,
-                                                   SHALLOW, NOT_SHALLOW);
-               while (result) {
-                       struct object *object = &result->item->object;
-                       if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
-                               packet_write(1, "shallow %s",
-                                               oid_to_hex(&object->oid));
-                               register_shallow(object->oid.hash);
-                               shallow_nr++;
+
+               argv_array_push(&av, "rev-list");
+               if (deepen_since)
+                       argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+               if (deepen_not.nr) {
+                       argv_array_push(&av, "--not");
+                       for (i = 0; i < deepen_not.nr; i++) {
+                               struct string_list_item *s = deepen_not.items + i;
+                               argv_array_push(&av, s->string);
                        }
-                       result = result->next;
+                       argv_array_push(&av, "--not");
                }
-               free_commit_list(backup);
-               for (i = 0; i < shallows.nr; i++) {
-                       struct object *object = shallows.objects[i].item;
-                       if (object->flags & NOT_SHALLOW) {
-                               struct commit_list *parents;
-                               packet_write(1, "unshallow %s",
-                                       oid_to_hex(&object->oid));
-                               object->flags &= ~CLIENT_SHALLOW;
-                               /* make sure the real parents are parsed */
-                               unregister_shallow(object->oid.hash);
-                               object->parsed = 0;
-                               parse_commit_or_die((struct commit *)object);
-                               parents = ((struct commit *)object)->parents;
-                               while (parents) {
-                                       add_object_array(&parents->item->object,
-                                                       NULL, &want_obj);
-                                       parents = parents->next;
-                               }
-                               add_object_array(object, NULL, &extra_edge_obj);
-                       }
-                       /* make sure commit traversal conforms to client */
-                       register_shallow(object->oid.hash);
+               for (i = 0; i < want_obj.nr; i++) {
+                       struct object *o = want_obj.objects[i].item;
+                       argv_array_push(&av, oid_to_hex(&o->oid));
                }
-               packet_flush(1);
-       } else
+               deepen_by_rev_list(av.argc, av.argv, &shallows);
+               argv_array_clear(&av);
+       }
+       else
                if (shallows.nr > 0) {
                        int i;
                        for (i = 0; i < shallows.nr; i++)
@@ -729,8 +919,8 @@ static int send_ref(const char *refname, const struct object_id *oid,
                    int flag, void *cb_data)
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
-               " side-band-64k ofs-delta shallow no-progress"
-               " include-tag multi_ack_detailed";
+               " side-band-64k ofs-delta shallow deepen-since deepen-not"
+               " deepen-relative no-progress include-tag multi_ack_detailed";
        const char *refname_nons = strip_namespace(refname);
        struct object_id peeled;
 
index 9628c1d5d75ca3ff4d3bc6ccc66152f371d9483e..99d1b0a8180f34bb928ad92612b6a11922907e27 100644 (file)
@@ -1383,8 +1383,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        if (!strcmp(cb->buf.buf, "HEAD")) {
                /* HEAD is relative. Resolve it to the right reflog entry. */
                strbuf_reset(&cb->buf);
-               strbuf_addstr(&cb->buf,
-                             find_unique_abbrev(nsha1, DEFAULT_ABBREV));
+               strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
        }
        return 1;
 }