]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'mh/check-attr-relative'
authorJunio C Hamano <gitster@pobox.com>
Thu, 18 Aug 2011 00:36:22 +0000 (17:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 18 Aug 2011 00:36:22 +0000 (17:36 -0700)
* mh/check-attr-relative: (29 commits)
  test-path-utils: Add subcommand "prefix_path"
  test-path-utils: Add subcommand "absolute_path"
  git-check-attr: Normalize paths
  git-check-attr: Demonstrate problems with relative paths
  git-check-attr: Demonstrate problems with unnormalized paths
  git-check-attr: test that no output is written to stderr
  Rename git_checkattr() to git_check_attr()
  git-check-attr: Fix command-line handling to match docs
  git-check-attr: Drive two tests using the same raw data
  git-check-attr: Add an --all option to show all attributes
  git-check-attr: Error out if no pathnames are specified
  git-check-attr: Process command-line args more systematically
  git-check-attr: Handle each error separately
  git-check-attr: Extract a function error_with_usage()
  git-check-attr: Introduce a new variable
  git-check-attr: Extract a function output_attr()
  Allow querying all attributes on a file
  Remove redundant check
  Remove redundant call to bootstrap_attr_stack()
  Extract a function collect_all_attrs()
  ...

128 files changed:
Documentation/.gitignore
Documentation/Makefile
Documentation/RelNotes/1.7.6.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.7.txt
Documentation/git-archive.txt
Documentation/git-bisect.txt
Documentation/git-cherry-pick.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-grep.txt
Documentation/git-gui.txt
Documentation/git-http-backend.txt
Documentation/git-log.txt
Documentation/git-merge-file.txt
Documentation/git-push.txt
Documentation/git-receive-pack.txt
Documentation/git-remote-fd.txt
Documentation/git-remote-helpers.txt
Documentation/git-revert.txt
Documentation/git-rm.txt
Documentation/git-send-pack.txt
Documentation/git-show.txt
Documentation/git-submodule.txt
Documentation/git-tar-tree.txt
Documentation/git-upload-pack.txt
Documentation/git-whatchanged.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitnamespaces.txt [new file with mode: 0644]
Documentation/technical/api-builtin.txt
Documentation/technical/index-format.txt
Makefile
bisect.c
bisect.h
builtin/bisect--helper.c
builtin/checkout-index.c
builtin/clone.c
builtin/commit.c
builtin/fast-export.c
builtin/grep.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/reflog.c
builtin/reset.c
builtin/send-pack.c
cache.h
combine-diff.c
config.c
connect.c
contrib/completion/git-completion.bash
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
diff-lib.c
diff.c
environment.c
fast-import.c
generate-cmdlist.sh
git-am.sh
git-bisect.sh
git-compat-util.h
git-filter-branch.sh
git-mergetool--lib.sh
git-pull.sh
git-rebase.sh
git-remote-testgit.py
git-submodule.sh
git.c
git_remote_helpers/git/exporter.py
git_remote_helpers/git/importer.py
git_remote_helpers/git/non_local.py
git_remote_helpers/git/repo.py
git_remote_helpers/util.py
gitweb/INSTALL
gitweb/Makefile
gitweb/README
gitweb/gitweb.perl
grep.c
grep.h
http.c
merge-recursive.c
refs.c
refs.h
remote-curl.c
remote.c
remote.h
run-command.c
setup.c
sha1_file.c
streaming.c
t/lib-diff-alternative.sh [new file with mode: 0644]
t/t1412-reflog-loop.sh
t/t3103-ls-tree-misc.sh [new file with mode: 0755]
t/t3307-notes-man.sh
t/t4012-diff-binary.sh
t/t4033-diff-patience.sh
t/t4050-diff-histogram.sh [new file with mode: 0755]
t/t5000-tar-tree.sh
t/t5509-fetch-push-namespaces.sh [new file with mode: 0755]
t/t5707-clone-detached.sh [new file with mode: 0755]
t/t5800-remote-helpers.sh
t/t6023-merge-file.sh
t/t6027-merge-binary.sh
t/t6030-bisect-porcelain.sh
t/t7102-reset.sh
t/t7400-submodule-basic.sh
t/t7810-grep.sh
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9800-git-p4.sh
t/test-binary-1.png [moved from t/test4012.png with 100% similarity]
t/test-binary-2.png [moved from t/test9200b.png with 100% similarity]
t/test9200a.png [deleted file]
transport-helper.c
transport.c
unpack-trees.c
upload-pack.c
usage.c
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xdiffi.h
xdiff/xhistogram.c [new file with mode: 0644]
xdiff/xpatience.c
xdiff/xprepare.c
xdiff/xutils.c
xdiff/xutils.h

index 1c3a9fead579a9b52037f1bbe245998db8a2f40b..d62aebd848b2a44f977ad4d7c4b75b6ff72b2163 100644 (file)
@@ -3,6 +3,7 @@
 *.[1-8]
 *.made
 *.texi
+*.pdf
 git.info
 gitman.info
 howto-index.txt
index 36989b7f6541cb8444385b64d2c955ae2a1f6d1b..6346a75dda72533667a96eea2bd65bb280383f99 100644 (file)
@@ -6,7 +6,7 @@ MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \
        gitrepository-layout.txt
 MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
        gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
-       gitdiffcore.txt gitrevisions.txt gitworkflows.txt
+       gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -232,6 +232,7 @@ cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
 clean:
        $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
        $(RM) *.texi *.texi+ *.texi++ git.info gitman.info
+       $(RM) *.pdf
        $(RM) howto-index.txt howto/*.html doc.dep
        $(RM) technical/api-*.html technical/api-index.txt
        $(RM) $(cmds_txt) *.made
diff --git a/Documentation/RelNotes/1.7.6.1.txt b/Documentation/RelNotes/1.7.6.1.txt
new file mode 100644 (file)
index 0000000..95905e4
--- /dev/null
@@ -0,0 +1,52 @@
+Git v1.7.6.1 Release Notes
+==========================
+
+Fixes since v1.7.6
+------------------
+
+ * Various codepaths that invoked zlib deflate/inflate assumed that these
+   functions can compress or uncompress more than 4GB data in one call on
+   platforms with 64-bit long, which has been corrected.
+
+ * "git unexecutable" reported that "unexecutable" was not found, even
+   though the actual error was that "unexecutable" was found but did
+   not have a proper she-bang line to be executed.
+
+ * "git checkout -b <branch>" was confused when attempting to create a
+   branch whose name ends with "-g" followed by hexadecimal digits,
+   and refused to work.
+
+ * "git checkout -b <branch>" sometimes wrote a bogus reflog entry,
+   causing later "git checkout -" to fail.
+
+ * "git diff --cc" learned to correctly ignore binary files.
+
+ * "git fast-export" forgot to quote pathnames with unsafe characters
+   in its output.
+
+ * "git fetch" did not recurse into submodules in subdirectories.
+
+ * "git ls-tree" did not error out when asked to show a corrupt tree.
+
+ * "git pull" without any argument left an extra whitespace after the
+   command name in its reflog.
+
+ * "git rebase -i -p" incorrectly dropped commits from side branches.
+
+ * "git reset [<commit>] paths..." did not reset the index entry correctly
+   for unmerged paths.
+
+ * "git submodule add" did not allow a relative repository path when
+   the superproject did not have any default remote url.
+
+ * "git submodule foreach" failed to correctly give the standard input to
+   the user-supplied command it invoked.
+
+ * submodules that the user has never showed interest in by running
+   "git submodule init" was incorrectly marked as interesting by "git
+   submodule sync".
+
+ * "git submodule update --quiet" was not really quiet.
+
+  * "git tag -l <glob>..." did not take multiple glob patterns from the
+   command line.
index 6d3bfd19680faaee7272d2451e12cea4e7440cdb..2ac3d3f2976c357e46a93f8e8338d2cd285e08c7 100644 (file)
@@ -6,7 +6,14 @@ Updates since v1.7.6
 
  * The scripting part of the codebase is getting prepared for i18n/l10n.
 
- * Interix and Cygwin ports got updated.
+ * Interix, Cygwin and Minix ports got updated.
+
+ * A handful of patches to update git-p4 (in contrib/).
+
+ * Gitweb learned to read from /etc/gitweb-common.conf when it exists,
+   before reading from gitweb_config.perl or from /etc/gitweb.conf
+   (this last one is read only when per-repository gitweb_config.perl
+   does not exist).
 
  * Various codepaths that invoked zlib deflate/inflate assumed that these
    functions can compress or uncompress more than 4GB data in one call on
@@ -18,11 +25,16 @@ Updates since v1.7.6
  * "git checkout" (both the code to update the files upon checking out a
    different branch, the code to checkout specific set of files) learned
    to stream the data from object store when possible, without having to
-   read the entire contents of a file in memory first.
+   read the entire contents of a file in memory first. An earlier round
+   of this code that is not in any released version had a large leak but
+   now it has been plugged.
 
  * "git clone" can now take "--config key=value" option to set the
    repository configuration options that affect the initial checkout.
 
+ * "git commit <paths>..." now lets you feed relative pathspecs that
+   refer outside your current subdirectory.
+
  * "git diff --stat" learned --stat-count option to limit the output of
    diffstat report.
 
@@ -34,6 +46,9 @@ Updates since v1.7.6
  * "git grep" learned --break and --heading options, to let users mimic
    output format of "ack".
 
+ * "git grep" learned "-W" option that shows wider context using the same
+   logic used by "git diff" to determine the hunk header.
+
  * "git rebase master topci" no longer spews usage hints after giving
    "fatal: no such branch: topci" error message.
 
@@ -59,40 +74,10 @@ Fixes since v1.7.6
 Unless otherwise noted, all the fixes in 1.7.6.X maintenance track are
 included in this release.
 
- * "git checkout -b <branch>" sometimes wrote a bogus reflog entry,
-   causing later "git checkout -" fail.
-   (merge 71ee7fd jc/checkout-reflog-fix~1 later).
-
- * "git diff --cc" learned to correctly ignore binary files.
-   (merge 0508fe5 jk/combine-diff-binary-etc later)
-
- * "git fetch" did not recurse into submodules in subdirectories.
-   (merge ea2d325 jl/maint-fetch-recursive-fix later)
-
- * "git rebase -i -p" incorrectly dropped commits from side branches.
-   (merge 12bf828 aw/rebase-i-p later)
-
- * "git submodule add" did not allow a relative repository path when
-   the superproject did not have any default remote url.
-   (merge f22a17e8 jl/submodule-add-relurl-wo-upstream later)
-
- * "git submodule foreach" failed to correctly give the standard input to
-   the user-supplied command it invoked.
-   (merge 4dca1aa bc/submodule-foreach-stdin-fix-1.7.4 later)
-
- * submodules that the user has never showed interest in by running
-   "git submodule init" was incorrectly marked as interesting by "git
-   submodule sync".
-   (merge 2cd9de3 jc/submodule-sync-no-auto-vivify later)
-
- * "git tag -l <glob>..." did not take multiple glob patterns from the
-   command line.
-   (merge 588d0e8 jk/tag-list-multiple-patterns later)
-
 --
 exec >/var/tmp/1
 echo O=$(git describe master)
-O=v1.7.6-344-g22f4128
+O=v1.7.6-471-g50b68ae
 git log --first-parent --oneline $O..master
 echo
 git shortlog --no-merges ^maint ^$O master
index 1320c873ad61281ef33891170da8683dc785f011..ac7006e6400d85c31bf31a3022a38225373fe3f7 100644 (file)
@@ -142,41 +142,41 @@ while archiving any tree in your `$GIT_DIR/info/attributes` file.
 
 EXAMPLES
 --------
-git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
+`git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)`::
 
        Create a tar archive that contains the contents of the
        latest commit on the current branch, and extract it in the
        `/var/tmp/junk` directory.
 
-git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
+`git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz`::
 
        Create a compressed tarball for v1.4.0 release.
 
-git archive --format=tar.gz --prefix=git-1.4.0/ v1.4.0 >git-1.4.0.tar.gz::
+`git archive --format=tar.gz --prefix=git-1.4.0/ v1.4.0 >git-1.4.0.tar.gz`::
 
        Same as above, but using the builtin tar.gz handling.
 
-git archive --prefix=git-1.4.0/ -o git-1.4.0.tar.gz v1.4.0::
+`git archive --prefix=git-1.4.0/ -o git-1.4.0.tar.gz v1.4.0`::
 
        Same as above, but the format is inferred from the output file.
 
-git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
+`git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz`::
 
        Create a compressed tarball for v1.4.0 release, but without a
        global extended pax header.
 
-git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
+`git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip`::
 
        Put everything in the current head's Documentation/ directory
        into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
 
-git archive -o latest.zip HEAD::
+`git archive -o latest.zip HEAD`::
 
        Create a Zip archive that contains the contents of the latest
        commit on the current branch. Note that the output format is
        inferred by the extension of the output file.
 
-git config tar.tar.xz.command "xz -c"::
+`git config tar.tar.xz.command "xz -c"`::
 
        Configure a "tar.xz" format for making LZMA-compressed tarfiles.
        You can use it specifying `--format=tar.xz`, or by creating an
index ab60a1847042a8ef28e019c8ad1a620ef5fd41d7..e4f46bc18dba1e55da83e4af76b8a7f30a7f40be 100644 (file)
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
 on the subcommand:
 
  git bisect help
- git bisect start [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
  git bisect bad [<rev>]
  git bisect good [<rev>...]
  git bisect skip [(<rev>|<range>)...]
@@ -263,6 +263,19 @@ rewind the tree to the pristine state.  Finally the script should exit
 with the status of the real test to let the "git bisect run" command loop
 determine the eventual outcome of the bisect session.
 
+OPTIONS
+-------
+--no-checkout::
++
+Do not checkout the new working tree at each iteration of the bisection
+process. Instead just update a special reference named 'BISECT_HEAD' to make
+it point to the commit that should be tested.
++
+This option may be useful when the test you would perform in each step
+does not require a checked out tree.
++
+If the repository is bare, `--no-checkout` is assumed.
+
 EXAMPLES
 --------
 
@@ -343,6 +356,25 @@ $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
 This shows that you can do without a run script if you write the test
 on a single line.
 
+* Locate a good region of the object graph in a damaged repository
++
+------------
+$ git bisect start HEAD <known-good-commit> [ <boundary-commit> ... ] --no-checkout
+$ git bisect run sh -c '
+       GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) &&
+       git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ &&
+       git pack-objects --stdout >/dev/null <tmp.$$
+       rc=$?
+       rm -f tmp.$$
+       test $rc = 0'
+
+------------
++
+In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+has at least one parent whose reachable graph is fully traversable in the sense
+required by 'git pack objects'.
+
+
 SEE ALSO
 --------
 link:git-bisect-lk2009.html[Fighting regressions with git bisect],
index 6c9c2cb3831698bb2d262344ab76baa601e8dde2..7cfa3d92ac8dc7a90068311c0047c667f98515a8 100644 (file)
@@ -112,31 +112,31 @@ effect to your index in a row.
 
 EXAMPLES
 --------
-git cherry-pick master::
+`git cherry-pick master`::
 
        Apply the change introduced by the commit at the tip of the
        master branch and create a new commit with this change.
 
-git cherry-pick ..master::
-git cherry-pick ^HEAD master::
+`git cherry-pick ..master`::
+`git cherry-pick ^HEAD master`::
 
        Apply the changes introduced by all commits that are ancestors
        of master but not of HEAD to produce new commits.
 
-git cherry-pick master{tilde}4 master{tilde}2::
+`git cherry-pick master{tilde}4 master{tilde}2`::
 
        Apply the changes introduced by the fifth and third last
        commits pointed to by master and create 2 new commits with
        these changes.
 
-git cherry-pick -n master~1 next::
+`git cherry-pick -n master~1 next`::
 
        Apply to the working tree and the index the changes introduced
        by the second last commit pointed to by master and by the last
        commit pointed to by next, but do not create any commit with
        these changes.
 
-git cherry-pick --ff ..next::
+`git cherry-pick --ff ..next`::
 
        If history is linear and HEAD is an ancestor of next, update
        the working tree and advance the HEAD pointer to match next.
@@ -144,7 +144,7 @@ git cherry-pick --ff ..next::
        are in next but not HEAD to the current branch, creating a new
        commit for each new change.
 
-git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
+`git rev-list --reverse master \-- README | git cherry-pick -n --stdin`::
 
        Apply the changes introduced by all commits on the master
        branch that touched README to the working tree and index,
index a29ac021d9c7ff62e3a761ffa23b7808420b5faa..f37eada63a449995861f41e5020c04f26d40c6f2 100644 (file)
@@ -83,6 +83,10 @@ marks the same across runs.
        allow that.  So fake a tagger to be able to fast-import the
        output.
 
+--use-done-feature::
+       Start the stream with a 'feature done' stanza, and terminate
+       it with a 'done' command.
+
 --no-data::
        Skip output of blob objects and instead refer to blobs via
        their original SHA-1 hash.  This is useful when rewriting the
index 95e480ef7968af9d3bb055e8fd9750b08d620622..2969388880a6d827a9970dd9d67fd81224a5bc34 100644 (file)
@@ -102,6 +102,12 @@ OPTIONS
        when the `cat-blob` command is encountered in the stream.
        The default behaviour is to write to `stdout`.
 
+--done::
+       Require a `done` command at the end of the stream.
+       This option might be useful for detecting errors that
+       cause the frontend to terminate before it has started to
+       write a stream.
+
 --export-pack-edges=<file>::
        After creating a packfile, print a line of data to
        <file> listing the filename of the packfile and the last
@@ -331,6 +337,11 @@ and control the current import process.  More detailed discussion
        standard output.  This command is optional and is not needed
        to perform an import.
 
+`done`::
+       Marks the end of the stream. This command is optional
+       unless the `done` feature was requested using the
+       `--done` command line option or `feature done` command.
+
 `cat-blob`::
        Causes fast-import to print a blob in 'cat-file --batch'
        format to the file descriptor set with `--cat-blob-fd` or
@@ -1021,6 +1032,11 @@ notes::
        Versions of fast-import not supporting notes will exit
        with a message indicating so.
 
+done::
+       Error out if the stream ends without a 'done' command.
+       Without this feature, errors causing the frontend to end
+       abruptly at a convenient point in the stream can go
+       undetected.
 
 `option`
 ~~~~~~~~
@@ -1050,6 +1066,15 @@ not be passed as option:
 * cat-blob-fd
 * force
 
+`done`
+~~~~~~
+If the `done` feature is not in use, treated as if EOF was read.
+This can be used to tell fast-import to finish early.
+
+If the `--done` command line option or `feature done` command is
+in use, the `done` command is mandatory and marks the end of the
+stream.
+
 Crash Reports
 -------------
 If fast-import is supplied invalid input it will terminate with a
index 07b3c6a0866c5b4ff0b4653b462b332ea0d348c9..e44a4988b7a7ed7100da6e2ba098d7fda17a2f83 100644 (file)
@@ -155,15 +155,6 @@ OPTIONS
        Show the filename above the matches in that file instead of
        at the start of each shown line.
 
--[ABC] <context>::
-       Show `context` trailing (`A` -- after), or leading (`B`
-       -- before), or both (`C` -- context) lines, and place a
-       line containing `--` between contiguous groups of
-       matches.
-
--<num>::
-       A shortcut for specifying `-C<num>`.
-
 -p::
 --show-function::
        Show the preceding line that contains the function name of
@@ -172,6 +163,29 @@ OPTIONS
        patch hunk headers (see 'Defining a custom hunk-header' in
        linkgit:gitattributes[5]).
 
+-<num>::
+-C <num>::
+--context <num>::
+       Show <num> leading and trailing lines, and place a line
+       containing `--` between contiguous groups of matches.
+
+-A <num>::
+--after-context <num>::
+       Show <num> trailing lines, and place a line containing
+       `--` between contiguous groups of matches.
+
+-B <num>::
+--before-context <num>::
+       Show <num> leading lines, and place a line containing
+       `--` between contiguous groups of matches.
+
+-W::
+--function-context::
+       Show the surrounding text from the previous line containing a
+       function name up to the one before the next function name,
+       effectively showing the whole function in which the match was
+       found.
+
 -f <file>::
        Read patterns from <file>, one per line.
 
@@ -215,15 +229,15 @@ OPTIONS
 Examples
 --------
 
-git grep {apostrophe}time_t{apostrophe} \-- {apostrophe}*.[ch]{apostrophe}::
+`git grep {apostrophe}time_t{apostrophe} \-- {apostrophe}*.[ch]{apostrophe}`::
        Looks for `time_t` in all tracked .c and .h files in the working
        directory and its subdirectories.
 
-git grep -e {apostrophe}#define{apostrophe} --and \( -e MAX_PATH -e PATH_MAX \)::
+`git grep -e {apostrophe}#define{apostrophe} --and \( -e MAX_PATH -e PATH_MAX \)`::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
 
-git grep --all-match -e NODE -e Unexpected::
+`git grep --all-match -e NODE -e Unexpected`::
        Looks for a line that has `NODE` or `Unexpected` in
        files that have lines that match both.
 
index 18f713b67a629ecbfd54e6aab1ef4fde89745d2f..0041994443c42033ee7c378ffad8983b0163489e 100644 (file)
@@ -50,7 +50,7 @@ version::
 
 Examples
 --------
-git gui blame Makefile::
+`git gui blame Makefile`::
 
        Show the contents of the file 'Makefile' in the current
        working directory, and provide annotations for both the
@@ -59,41 +59,41 @@ git gui blame Makefile::
        uncommitted changes (if any) are explicitly attributed to
        'Not Yet Committed'.
 
-git gui blame v0.99.8 Makefile::
+`git gui blame v0.99.8 Makefile`::
 
        Show the contents of 'Makefile' in revision 'v0.99.8'
        and provide annotations for each line.  Unlike the above
        example the file is read from the object database and not
        the working directory.
 
-git gui blame --line=100 Makefile::
+`git gui blame --line=100 Makefile`::
 
        Loads annotations as described above and automatically
        scrolls the view to center on line '100'.
 
-git gui citool::
+`git gui citool`::
 
        Make one commit and return to the shell when it is complete.
        This command returns a non-zero exit code if the window was
        closed in any way other than by making a commit.
 
-git gui citool --amend::
+`git gui citool --amend`::
 
        Automatically enter the 'Amend Last Commit' mode of
        the interface.
 
-git gui citool --nocommit::
+`git gui citool --nocommit`::
 
        Behave as normal citool, but instead of making a commit
        simply terminate with a zero exit code. It still checks
        that the index does not contain any unmerged entries, so
        you can use it as a GUI version of linkgit:git-mergetool[1]
 
-git citool::
+`git citool`::
 
        Same as `git gui citool` (above).
 
-git gui browser maint::
+`git gui browser maint`::
 
        Show a browser for the tree of the 'maint' branch.  Files
        selected in the browser can be viewed with the internal
index 277d9e141bf81bddb4ba661e43763b7774a1417d..f4e0741c115905f61a0e92c5255658f4b8e25711 100644 (file)
@@ -119,6 +119,14 @@ ScriptAliasMatch \
 
 ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
 ----------------------------------------------------------------
++
+To serve multiple repositories from different linkgit:gitnamespaces[7] in a
+single repository:
++
+----------------------------------------------------------------
+SetEnvIf Request_URI "^/git/([^/]*)" GIT_NAMESPACE=$1
+ScriptAliasMatch ^/git/[^/]*(.*) /usr/libexec/git-core/git-http-backend/storage.git$1
+----------------------------------------------------------------
 
 Accelerated static Apache 2.x::
        Similar to the above, but Apache can be used to return static
index 59f8be0be372acc26c98d162437702d19fd8ed36..6c934660d7bf0b5822bf2a7e6f8eb68cecbfc501 100644 (file)
@@ -88,45 +88,45 @@ include::diff-generate-patch.txt[]
 
 Examples
 --------
-git log --no-merges::
+`git log --no-merges`::
 
        Show the whole commit history, but skip any merges
 
-git log v2.6.12.. include/scsi drivers/scsi::
+`git log v2.6.12.. include/scsi drivers/scsi`::
 
        Show all commits since version 'v2.6.12' that changed any file
        in the include/scsi or drivers/scsi subdirectories
 
-git log --since="2 weeks ago" \-- gitk::
+`git log --since="2 weeks ago" \-- gitk`::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
        'gitk'
 
-git log --name-status release..test::
+`git log --name-status release..test`::
 
        Show the commits that are in the "test" branch but not yet
        in the "release" branch, along with the list of paths
        each commit modifies.
 
-git log --follow builtin-rev-list.c::
+`git log --follow builtin-rev-list.c`::
 
        Shows the commits that changed builtin-rev-list.c, including
        those commits that occurred before the file was given its
        present name.
 
-git log --branches --not --remotes=origin::
+`git log --branches --not --remotes=origin`::
 
        Shows all commits that are in any of local branches but not in
        any of remote-tracking branches for 'origin' (what you have that
        origin doesn't).
 
-git log master --not --remotes=*/master::
+`git log master --not --remotes=*/master`::
 
        Shows all commits that are in local master but not in any remote
        repository master branches.
 
-git log -p -m --first-parent::
+`git log -p -m --first-parent`::
 
        Shows the history including change diffs, but only from the
        "main branch" perspective, skipping commits that come from merged
index 635c66956e90452c58938f2d48e84454983f22e6..d7db2a3737fbdb032747bc6dd2740be1a56dfced 100644 (file)
@@ -76,12 +76,12 @@ OPTIONS
 EXAMPLES
 --------
 
-git merge-file README.my README README.upstream::
+`git merge-file README.my README README.upstream`::
 
        combines the changes of README.my and README.upstream since README,
        tries to merge them and writes the result into README.my.
 
-git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345::
+`git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345`::
 
        merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
        `a` and `c` instead of `tmp/a123` and `tmp/c345`.
index 88acfcd4cc8e0eeafc809df0d72b37daded6f8a6..49c6e9fa51daea75272ea7eec8a360bfd324a467 100644 (file)
@@ -327,12 +327,12 @@ a case where you do mean to lose history.
 Examples
 --------
 
-git push::
+`git push`::
        Works like `git push <remote>`, where <remote> is the
        current branch's remote (or `origin`, if no remote is
        configured for the current branch).
 
-git push origin::
+`git push origin`::
        Without additional configuration, works like
        `git push origin :`.
 +
@@ -344,45 +344,45 @@ use `git config remote.origin.push HEAD`.  Any valid <refspec> (like
 the ones in the examples below) can be configured as the default for
 `git push origin`.
 
-git push origin :::
+`git push origin :`::
        Push "matching" branches to `origin`. See
        <refspec> in the <<OPTIONS,OPTIONS>> section above for a
        description of "matching" branches.
 
-git push origin master::
+`git push origin master`::
        Find a ref that matches `master` in the source repository
        (most likely, it would find `refs/heads/master`), and update
        the same ref (e.g. `refs/heads/master`) in `origin` repository
        with it.  If `master` did not exist remotely, it would be
        created.
 
-git push origin HEAD::
+`git push origin HEAD`::
        A handy way to push the current branch to the same name on the
        remote.
 
-git push origin master:satellite/master dev:satellite/dev::
+`git push origin master:satellite/master dev:satellite/dev`::
        Use the source ref that matches `master` (e.g. `refs/heads/master`)
        to update the ref that matches `satellite/master` (most probably
        `refs/remotes/satellite/master`) in the `origin` repository, then
        do the same for `dev` and `satellite/dev`.
 
-git push origin HEAD:master::
+`git push origin HEAD:master`::
        Push the current branch to the remote ref matching `master` in the
        `origin` repository. This form is convenient to push the current
        branch without thinking about its local name.
 
-git push origin master:refs/heads/experimental::
+`git push origin master:refs/heads/experimental`::
        Create the branch `experimental` in the `origin` repository
        by copying the current `master` branch.  This form is only
        needed to create a new branch or tag in the remote repository when
        the local name and the remote name are different; otherwise,
        the ref name on its own will work.
 
-git push origin :experimental::
+`git push origin :experimental`::
        Find a ref that matches `experimental` in the `origin` repository
        (e.g. `refs/heads/experimental`), and delete it.
 
-git push origin {plus}dev:master::
+`git push origin {plus}dev:master`::
        Update the origin repository's master branch with the dev branch,
        allowing non-fast-forward updates.  *This can leave unreferenced
        commits dangling in the origin repository.*  Consider the
index 459c08598f31fccf1b5db31f2f1cd9680654b389..a3a1d8eea3ec733d155475f2d6216e1e71245f10 100644 (file)
@@ -9,7 +9,7 @@ git-receive-pack - Receive what is pushed into the repository
 SYNOPSIS
 --------
 [verse]
-'git-receive-pack' <directory>
+'git-receive-pack' [--quiet] <directory>
 
 DESCRIPTION
 -----------
@@ -35,6 +35,9 @@ are not fast-forwards.
 
 OPTIONS
 -------
+--quiet::
+       Print only error messages.
+
 <directory>::
        The repository to sync into.
 
@@ -150,7 +153,7 @@ if the repository is packed and is served via a dumb transport.
 
 SEE ALSO
 --------
-linkgit:git-send-pack[1]
+linkgit:git-send-pack[1], linkgit:gitnamespaces[7]
 
 GIT
 ---
index 4aecd4d1878e0af6c3e6c87a63df1375606ff65a..f095d57d09f215e48e1f832ca0698e5666119f2b 100644 (file)
@@ -35,19 +35,19 @@ GIT_TRANSLOOP_DEBUG::
 
 EXAMPLES
 --------
-git fetch fd::17 master::
+`git fetch fd::17 master`::
        Fetch master, using file descriptor #17 to communicate with
        git-upload-pack.
 
-git fetch fd::17/foo master::
+`git fetch fd::17/foo master`::
        Same as above.
 
-git push fd::7,8 master (as URL)::
+`git push fd::7,8 master (as URL)`::
        Push master, using file descriptor #7 to read data from
        git-receive-pack and file descriptor #8 to write data to
        same service.
 
-git push fd::7,8/bar master::
+`git push fd::7,8/bar master`::
        Same as above.
 
 Documentation
index 930b4034ac5dc60481afe34f9cd82fd756639190..4f83dea5a39ca2f8e695a2426e90cf0450533696 100644 (file)
@@ -48,6 +48,9 @@ arguments. The first argument specifies a remote repository as in git;
 it is either the name of a configured remote or a URL. The second
 argument specifies a URL; it is usually of the form
 '<transport>://<address>', but any arbitrary string is possible.
+The 'GIT_DIR' environment variable is set up for the remote helper
+and can be used to determine where to store additional data or from
+which directory to invoke auxiliary git commands.
 
 When git encounters a URL of the form '<transport>://<address>', where
 '<transport>' is a protocol that it cannot handle natively, it
index 3d0a7d1dac5614d6644a0ea226d65bd9f667143a..b311d59c7c06dd696e3c667c97a5982137bdd87a 100644 (file)
@@ -93,12 +93,12 @@ effect to your index in a row.
 
 EXAMPLES
 --------
-git revert HEAD~3::
+`git revert HEAD~3`::
 
        Revert the changes specified by the fourth last commit in HEAD
        and create a new commit with the reverted changes.
 
-git revert -n master{tilde}5..master{tilde}2::
+`git revert -n master{tilde}5..master{tilde}2`::
 
        Revert the changes done by commits from the fifth last commit
        in master (included) to the third last commit in master
index da0215d20c61c11307ffd4347026cc295b0dc685..665ad4ddab14da173142914407680605dad9dae6 100644 (file)
@@ -137,7 +137,7 @@ git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
 
 EXAMPLES
 --------
-git rm Documentation/\*.txt::
+`git rm Documentation/\*.txt`::
        Removes all `*.txt` files from the index that are under the
        `Documentation` directory and any of its subdirectories.
 +
@@ -145,7 +145,7 @@ Note that the asterisk `*` is quoted from the shell in this
 example; this lets git, and not the shell, expand the pathnames
 of files and subdirectories under the `Documentation/` directory.
 
-git rm -f git-*.sh::
+`git rm -f git-*.sh`::
        Because this example lets the shell expand the asterisk
        (i.e. you are listing the files explicitly), it
        does not remove `subdir/git-foo.sh`.
index bd3eaa69bfb6e788d297b3e7d2c871d25a478f80..bed9e1f097d3aa144a62765129bf3b599d365dcf 100644 (file)
@@ -9,7 +9,7 @@ git-send-pack - Push objects over git protocol to another repository
 SYNOPSIS
 --------
 [verse]
-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
+'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--quiet] [--verbose] [--thin] [<host>:]<directory> [<ref>...]
 
 DESCRIPTION
 -----------
@@ -45,6 +45,9 @@ OPTIONS
        the remote repository can lose commits; use it with
        care.
 
+--quiet::
+       Print only error messages.
+
 --verbose::
        Run verbosely.
 
index 1f0e30b912f3fe4aa487129fd81ddc1dfa47a88e..1e38819e67cda4282ec7ede004ee5668d0a478c8 100644 (file)
@@ -48,23 +48,23 @@ include::pretty-formats.txt[]
 EXAMPLES
 --------
 
-git show v1.0.0::
+`git show v1.0.0`::
        Shows the tag `v1.0.0`, along with the object the tags
        points at.
 
-git show v1.0.0^\{tree\}::
+`git show v1.0.0^\{tree\}`::
        Shows the tree pointed to by the tag `v1.0.0`.
 
-git show -s --format=%s v1.0.0^\{commit\}::
+`git show -s --format=%s v1.0.0^\{commit\}`::
        Shows the subject of the commit pointed to by the
        tag `v1.0.0`.
 
-git show next~10:Documentation/README::
+`git show next~10:Documentation/README`::
        Shows the contents of the file `Documentation/README` as
        they were current in the 10th last commit of the branch
        `next`.
 
-git show master:Makefile master:t/Makefile::
+`git show master:Makefile master:t/Makefile`::
        Concatenates the contents of said Makefiles in the head
        of the branch `master`.
 
index 0ec85742ddc1c360d066659fb562898a259e48f7..67cf5f0f8b8a280891e47ecb7705d260bfc4232b 100644 (file)
@@ -15,7 +15,8 @@ SYNOPSIS
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
              [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
+             [commit] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
 'git submodule' [--quiet] sync [--] [<path>...]
 
@@ -108,8 +109,13 @@ status::
        repository and `U` if the submodule has merge conflicts.
        This command is the default command for 'git submodule'.
 +
-If '--recursive' is specified, this command will recurse into nested
+If `--recursive` is specified, this command will recurse into nested
 submodules, and show their status as well.
++
+If you are only interested in changes of the currently initialized
+submodules with respect to the commit recorded in the index or the HEAD,
+linkgit:git-status[1] and linkgit:git-diff[1] will provide that information
+too (and can also report changes to a submodule's work tree).
 
 init::
        Initialize the submodules, i.e. register each submodule name
@@ -125,26 +131,29 @@ init::
 update::
        Update the registered submodules, i.e. clone missing submodules and
        checkout the commit specified in the index of the containing repository.
-       This will make the submodules HEAD be detached unless '--rebase' or
-       '--merge' is specified or the key `submodule.$name.update` is set to
+       This will make the submodules HEAD be detached unless `--rebase` or
+       `--merge` is specified or the key `submodule.$name.update` is set to
        `rebase` or `merge`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
-submodule with the --init option.
+submodule with the `--init` option.
 +
-If '--recursive' is specified, this command will recurse into the
+If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
        working tree/index. For a submodule in question, a series of commits
        in the submodule between the given super project commit and the
-       index or working tree (switched by --cached) are shown. If the option
-       --files is given, show the series of commits in the submodule between
+       index or working tree (switched by `--cached`) are shown. If the option
+       `--files` is given, show the series of commits in the submodule between
        the index of the super project and the working tree of the submodule
-       (this option doesn't allow to use the --cached option or to provide an
+       (this option doesn't allow to use the `--cached` option or to provide an
        explicit commit).
++
+Using the `--submodule=log` option with linkgit:git-diff[1] will provide that
+information too.
 
 foreach::
        Evaluates an arbitrary shell command in each checked out submodule.
@@ -155,9 +164,9 @@ foreach::
        superproject, $sha1 is the commit as recorded in the superproject,
        and $toplevel is the absolute path to the top-level of the superproject.
        Any submodules defined in the superproject but not checked out are
-       ignored by this command. Unless given --quiet, foreach prints the name
+       ignored by this command. Unless given `--quiet`, foreach prints the name
        of each submodule before evaluating the command.
-       If --recursive is given, submodules are traversed recursively (i.e.
+       If `--recursive` is given, submodules are traversed recursively (i.e.
        the given shell command is evaluated in nested submodules as well).
        A non-zero return from the command in any submodule causes
        the processing to terminate. This can be overridden by adding '|| :'
@@ -237,13 +246,18 @@ OPTIONS
        If the key `submodule.$name.update` is set to `rebase`, this option is
        implicit.
 
+--init::
+       This option is only valid for the update command.
+       Initialize all submodules for which "git submodule init" has not been
+       called so far before updating.
+
 --reference <repository>::
        This option is only valid for add and update commands.  These
        commands sometimes need to clone a remote repository. In this case,
        this option will be passed to the linkgit:git-clone[1] command.
 +
 *NOTE*: Do *not* use this option unless you have read the note
-for linkgit:git-clone[1]'s --reference and --shared options carefully.
+for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
 
 --recursive::
        This option is only valid for foreach, update and status commands.
index 95b135d8ad9c8e6d5f136d526c8a7a86541b4d35..346e7a20797c22a29733f09e5fe241c84a46bfa9 100644 (file)
@@ -53,26 +53,26 @@ tar.umask::
 
 EXAMPLES
 --------
-git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)::
+`git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)`::
 
        Create a tar archive that contains the contents of the
        latest commit on the current branch, and extracts it in
        `/var/tmp/junk` directory.
 
-git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz::
+`git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz`::
 
        Create a tarball for v1.4.0 release.
 
-git tar-tree v1.4.0{caret}\{tree\} git-1.4.0 | gzip >git-1.4.0.tar.gz::
+`git tar-tree v1.4.0{caret}\{tree\} git-1.4.0 | gzip >git-1.4.0.tar.gz`::
 
        Create a tarball for v1.4.0 release, but without a
        global extended pax header.
 
-git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar::
+`git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar`::
 
        Get a tarball v1.4.0 from example.com.
 
-git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar::
+`git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar`::
 
        Put everything in the current head's Documentation/ directory
        into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'.
index a58e90ca8dada4a9847886374968635ea6bc3f9f..71f16083d6b2c366a54db1d966e17ab2aeda1ef1 100644 (file)
@@ -34,6 +34,10 @@ OPTIONS
 <directory>::
        The repository to sync from.
 
+SEE ALSO
+--------
+linkgit:gitnamespaces[7]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 99388bd3744dd593a97dbf34c022d4d5125bba6c..76c7f7eec5bb8ba01262762909939070b55ef5c9 100644 (file)
@@ -53,12 +53,12 @@ include::pretty-formats.txt[]
 
 Examples
 --------
-git whatchanged -p v2.6.12.. include/scsi drivers/scsi::
+`git whatchanged -p v2.6.12.. include/scsi drivers/scsi`::
 
        Show as patches the commits since version 'v2.6.12' that changed
        any file in the include/scsi or drivers/scsi subdirectories
 
-git whatchanged --since="2 weeks ago" \-- gitk::
+`git whatchanged --since="2 weeks ago" \-- gitk`::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index 0172cd70149914897862fbf0f3f221c7ef31ea5c..710d750cfd7a67bacedaa851afaeed4469d38a77 100644 (file)
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
-    [-p|--paginate|--no-pager] [--no-replace-objects]
-    [--bare] [--git-dir=<path>] [--work-tree=<path>]
+    [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
+    [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
     [-c <name>=<value>]
     [--help] <command> [<args>]
 
@@ -330,6 +330,11 @@ help ...`.
        variable (see core.worktree in linkgit:git-config[1] for a
        more detailed discussion).
 
+--namespace=<path>::
+       Set the git namespace.  See linkgit:gitnamespaces[7] for more
+       details.  Equivalent to setting the `GIT_NAMESPACE` environment
+       variable.
+
 --bare::
        Treat the repository as a bare repository.  If GIT_DIR
        environment is not set, it is set to the current working
@@ -593,6 +598,10 @@ git so take care if using Cogito etc.
        This can also be controlled by the '--work-tree' command line
        option and the core.worktree configuration variable.
 
+'GIT_NAMESPACE'::
+       Set the git namespace; see linkgit:gitnamespaces[7] for details.
+       The '--namespace' command-line option also sets this value.
+
 'GIT_CEILING_DIRECTORIES'::
        This should be a colon-separated list of absolute paths.
        If set, it is a list of directories that git should not chdir
index 3d8643437e7f451b2675ae52902bc68a58f2b02f..25e46aeb7a32287c7dc2666d2633c4787ae1c59a 100644 (file)
@@ -79,7 +79,7 @@ Attributes for all users on a system should be placed in the
 `$(prefix)/etc/gitattributes` file.
 
 Sometimes you would need to override an setting of an attribute
-for a path to `unspecified` state.  This can be done by listing
+for a path to `Unspecified` state.  This can be done by listing
 the name of the attribute prefixed with an exclamation point `!`.
 
 
@@ -868,7 +868,7 @@ If this attribute is not set or has an invalid value, the value of the
 (See linkgit:git-config[1]).
 
 
-USING ATTRIBUTE MACROS
+USING MACRO ATTRIBUTES
 ----------------------
 
 You do not want any end-of-line conversions applied to, nor textual diffs
@@ -879,24 +879,27 @@ produced for, any binary file you track.  You would need to specify e.g.
 ------------
 
 but that may become cumbersome, when you have many attributes.  Using
-attribute macros, you can specify groups of attributes set or unset at
-the same time.  The system knows a built-in attribute macro, `binary`:
+macro attributes, you can define an attribute that, when set, also
+sets or unsets a number of other attributes at the same time.  The
+system knows a built-in macro attribute, `binary`:
 
 ------------
 *.jpg binary
 ------------
 
-which is equivalent to the above.  Note that the attribute macros can only
-be "Set" (see the above example that sets "binary" macro as if it were an
-ordinary attribute --- setting it in turn unsets "text" and "diff").
+Setting the "binary" attribute also unsets the "text" and "diff"
+attributes as above.  Note that macro attributes can only be "Set",
+though setting one might have the effect of setting or unsetting other
+attributes or even returning other attributes to the "Unspecified"
+state.
 
 
-DEFINING ATTRIBUTE MACROS
+DEFINING MACRO ATTRIBUTES
 -------------------------
 
-Custom attribute macros can be defined only in the `.gitattributes` file
-at the toplevel (i.e. not in any subdirectory).  The built-in attribute
-macro "binary" is equivalent to:
+Custom macro attributes can be defined only in the `.gitattributes`
+file at the toplevel (i.e. not in any subdirectory).  The built-in
+macro attribute "binary" is equivalent to:
 
 ------------
 [attr]binary -diff -text
diff --git a/Documentation/gitnamespaces.txt b/Documentation/gitnamespaces.txt
new file mode 100644 (file)
index 0000000..ed8924e
--- /dev/null
@@ -0,0 +1,75 @@
+gitnamespaces(7)
+================
+
+NAME
+----
+gitnamespaces - Git namespaces
+
+DESCRIPTION
+-----------
+
+Git supports dividing the refs of a single repository into multiple
+namespaces, each of which has its own branches, tags, and HEAD.  Git can
+expose each namespace as an independent repository to pull from and push
+to, while sharing the object store, and exposing all the refs to
+operations such as linkgit:git-gc[1].
+
+Storing multiple repositories as namespaces of a single repository
+avoids storing duplicate copies of the same objects, such as when
+storing multiple branches of the same source.  The alternates mechanism
+provides similar support for avoiding duplicates, but alternates do not
+prevent duplication between new objects added to the repositories
+without ongoing maintenance, while namespaces do.
+
+To specify a namespace, set the `GIT_NAMESPACE` environment variable to
+the namespace.  For each ref namespace, git stores the corresponding
+refs in a directory under `refs/namespaces/`.  For example,
+`GIT_NAMESPACE=foo` will store refs under `refs/namespaces/foo/`.  You
+can also specify namespaces via the `--namespace` option to
+linkgit:git[1].
+
+Note that namespaces which include a `/` will expand to a hierarchy of
+namespaces; for example, `GIT_NAMESPACE=foo/bar` will store refs under
+`refs/namespaces/foo/refs/namespaces/bar/`.  This makes paths in
+`GIT_NAMESPACE` behave hierarchically, so that cloning with
+`GIT_NAMESPACE=foo/bar` produces the same result as cloning with
+`GIT_NAMESPACE=foo` and cloning from that repo with `GIT_NAMESPACE=bar`.  It
+also avoids ambiguity with strange namespace paths such as `foo/refs/heads/`,
+which could otherwise generate directory/file conflicts within the `refs`
+directory.
+
+linkgit:git-upload-pack[1] and linkgit:git-receive-pack[1] rewrite the
+names of refs as specified by `GIT_NAMESPACE`.  git-upload-pack and
+git-receive-pack will ignore all references outside the specified
+namespace.
+
+The smart HTTP server, linkgit:git-http-backend[1], will pass
+GIT_NAMESPACE through to the backend programs; see
+linkgit:git-http-backend[1] for sample configuration to expose
+repository namespaces as repositories.
+
+For a simple local test, you can use linkgit:git-remote-ext[1]:
+
+----------
+git clone ext::'git --namespace=foo %s /tmp/prefixed.git'
+----------
+
+SECURITY
+--------
+
+Anyone with access to any namespace within a repository can potentially
+access objects from any other namespace stored in the same repository.
+You can't directly say "give me object ABCD" if you don't have a ref to
+it, but you can do some other sneaky things like:
+
+. Claiming to push ABCD, at which point the server will optimize out the
+  need for you to actually send it. Now you have a ref to ABCD and can
+  fetch it (claiming not to have it, of course).
+
+. Requesting other refs, claiming that you have ABCD, at which point the
+  server may generate deltas against ABCD.
+
+None of this causes a problem if you only host public repositories, or
+if everyone who may read one namespace may also read everything in every
+other namespace (for instance, if everyone in an organization has read
+permission to every repository).
index 5cb2b0590abb1fc4e4685097ba3cdd3d0683a95f..b0cafe87bee046dc9b2d4e61e0d057e787a6f1f6 100644 (file)
@@ -49,6 +49,8 @@ Additionally, if `foo` is a new command, there are 3 more things to do:
 
 . Add an entry for `git-foo` to `command-list.txt`.
 
+. Add an entry for `/git-foo` to `.gitignore`.
+
 
 How a built-in is called
 ------------------------
index 7b233ca19676c2f89b1fb0231d7085e70c7eaaf6..8930b3fabceebfc7e03ddabdf5f7bfb228babf6b 100644 (file)
@@ -147,8 +147,9 @@ GIT index format
   - 160-bit object name for the object that would result from writing
     this span of index as a tree.
 
-  An entry can be in an invalidated state and is represented by having -1
-  in the entry_count field.
+  An entry can be in an invalidated state and is represented by having
+  -1 in the entry_count field. In this case, there is no object name
+  and the next entry starts immediately after the newline.
 
   The entries are written out in the top-down, depth-first order.  The
   first entry represents the root level of the repository, followed by the
index 4ed7996f7bcc3a044b0da54e8319fc0339ff009d..89cc6245a721a1cb807e197fb3346cec02367a51 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -30,15 +30,15 @@ all::
 # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
-# Define NO_CURL if you do not have libcurl installed.  git-http-pull and
+# Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
 # git-http-push are not built, and you cannot use http:// and https://
-# transports.
+# transports (neither smart nor dumb).
 #
 # Define CURLDIR=/foo/bar if your curl header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
-# not built, and you cannot push using http:// and https:// transports.
+# not built, and you cannot push using http:// and https:// transports (dumb).
 #
 # Define EXPATDIR=/foo/bar if your expat header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
@@ -115,6 +115,10 @@ all::
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
 #
+# Define NEEDS_SSL_WITH_CURL if you need -lssl with -lcurl (Minix).
+#
+# Define NEEDS_IDN_WITH_CURL if you need -lidn when using -lcurl (Minix).
+#
 # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
 #
 # Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
@@ -1149,6 +1153,20 @@ ifeq ($(uname_S),Interix)
                NO_FNMATCH_CASEFOLD = YesPlease
        endif
 endif
+ifeq ($(uname_S),Minix)
+       NO_IPV6 = YesPlease
+       NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
+       NO_NSEC = YesPlease
+       NEEDS_LIBGEN =
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
+       NEEDS_IDN_WITH_CURL = YesPlease
+       NEEDS_SSL_WITH_CURL = YesPlease
+       NEEDS_RESOLV =
+       NO_HSTRERROR = YesPlease
+       NO_MMAP = YesPlease
+       NO_CURL =
+       NO_EXPAT =
+endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
@@ -1293,6 +1311,16 @@ else
        else
                CURL_LIBCURL = -lcurl
        endif
+       ifdef NEEDS_SSL_WITH_CURL
+               CURL_LIBCURL += -lssl
+               ifdef NEEDS_CRYPTO_WITH_SSL
+                       CURL_LIBCURL += -lcrypto
+               endif
+       endif
+       ifdef NEEDS_IDN_WITH_CURL
+               CURL_LIBCURL += -lidn
+       endif
+
        REMOTE_CURL_PRIMARY = git-remote-http$X
        REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
        REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
@@ -1329,7 +1357,7 @@ ifndef NO_OPENSSL
                OPENSSL_LINK =
        endif
        ifdef NEEDS_CRYPTO_WITH_SSL
-               OPENSSL_LINK += -lcrypto
+               OPENSSL_LIBSSL += -lcrypto
        endif
 else
        BASIC_CFLAGS += -DNO_OPENSSL
@@ -1848,7 +1876,7 @@ ifndef NO_CURL
        GIT_OBJS += http.o http-walker.o remote-curl.o
 endif
 XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
-       xdiff/xmerge.o xdiff/xpatience.o
+       xdiff/xmerge.o xdiff/xpatience.o xdiff/xhistogram.o
 VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
        vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
 VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
index dd7e8ed69bc0c1268dc3b0a6590501ba9c10d7db..c7b7d7913dc444926b0260211e8bc3aa75eeffdb 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -24,6 +24,7 @@ struct argv_array {
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
+static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
 
 /* bits #0-15 in revision.h */
 
@@ -707,16 +708,23 @@ static void mark_expected_rev(char *bisect_rev_hex)
                die("closing file %s: %s", filename, strerror(errno));
 }
 
-static int bisect_checkout(char *bisect_rev_hex)
+static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
 {
        int res;
 
        mark_expected_rev(bisect_rev_hex);
 
        argv_checkout[2] = bisect_rev_hex;
-       res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
-       if (res)
-               exit(res);
+       if (no_checkout) {
+               argv_update_ref[3] = bisect_rev_hex;
+               if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
+                       die("update-ref --no-deref HEAD failed on %s",
+                           bisect_rev_hex);
+       } else {
+               res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
+               if (res)
+                       exit(res);
+       }
 
        argv_show_branch[1] = bisect_rev_hex;
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
@@ -788,7 +796,7 @@ static void handle_skipped_merge_base(const unsigned char *mb)
  * - If one is "skipped", we can't know but we should warn.
  * - If we don't know, we should check it out and ask the user to test.
  */
-static void check_merge_bases(void)
+static void check_merge_bases(int no_checkout)
 {
        struct commit_list *result;
        int rev_nr;
@@ -806,7 +814,7 @@ static void check_merge_bases(void)
                        handle_skipped_merge_base(mb);
                } else {
                        printf("Bisecting: a merge base must be tested\n");
-                       exit(bisect_checkout(sha1_to_hex(mb)));
+                       exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
                }
        }
 
@@ -849,7 +857,7 @@ static int check_ancestors(const char *prefix)
  * If a merge base must be tested by the user, its source code will be
  * checked out to be tested by the user and we will exit.
  */
-static void check_good_are_ancestors_of_bad(const char *prefix)
+static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
        const char *filename = git_path("BISECT_ANCESTORS_OK");
        struct stat st;
@@ -868,7 +876,7 @@ static void check_good_are_ancestors_of_bad(const char *prefix)
 
        /* Check if all good revs are ancestor of the bad rev. */
        if (check_ancestors(prefix))
-               check_merge_bases();
+               check_merge_bases(no_checkout);
 
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@ -908,8 +916,11 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
  * We use the convention that exiting with an exit code 10 means that
  * the bisection process finished successfully.
  * In this case the calling shell script should exit 0.
+ *
+ * If no_checkout is non-zero, the bisection process does not
+ * checkout the trial commit but instead simply updates BISECT_HEAD.
  */
-int bisect_next_all(const char *prefix)
+int bisect_next_all(const char *prefix, int no_checkout)
 {
        struct rev_info revs;
        struct commit_list *tried;
@@ -920,7 +931,7 @@ int bisect_next_all(const char *prefix)
        if (read_bisect_refs())
                die("reading bisect refs failed");
 
-       check_good_are_ancestors_of_bad(prefix);
+       check_good_are_ancestors_of_bad(prefix, no_checkout);
 
        bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
        revs.limited = 1;
@@ -966,6 +977,6 @@ int bisect_next_all(const char *prefix)
               "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
               steps, (steps == 1 ? "" : "s"));
 
-       return bisect_checkout(bisect_rev_hex);
+       return bisect_checkout(bisect_rev_hex, no_checkout);
 }
 
index 0862ce56d76e9b08ab913e6a472fac590974340e..22f2e4db2d1d05184dc2eeec2ff87021a67e01f2 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -27,7 +27,7 @@ struct rev_list_info {
        const char *header_prefix;
 };
 
-extern int bisect_next_all(const char *prefix);
+extern int bisect_next_all(const char *prefix, int no_checkout);
 
 extern int estimate_bisect_steps(int all);
 
index 5b226399e1c30b23a7b5d226a3f45efe9dbedf45..8d325a5179f68d2810b5b2082fe66dc6d00da455 100644 (file)
@@ -4,16 +4,19 @@
 #include "bisect.h"
 
 static const char * const git_bisect_helper_usage[] = {
-       "git bisect--helper --next-all",
+       "git bisect--helper --next-all [--no-checkout]",
        NULL
 };
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
        int next_all = 0;
+       int no_checkout = 0;
        struct option options[] = {
                OPT_BOOLEAN(0, "next-all", &next_all,
                            "perform 'git bisect next'"),
+               OPT_BOOLEAN(0, "no-checkout", &no_checkout,
+                           "update BISECT_HEAD instead of checking out the current commit"),
                OPT_END()
        };
 
@@ -24,5 +27,5 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                usage_with_options(git_bisect_helper_usage, options);
 
        /* next-all */
-       return bisect_next_all(prefix);
+       return bisect_next_all(prefix, no_checkout);
 }
index f1fec24745a854e77555a1b4bea3bc8d7ab04367..c16d82b7de652026b70fc3d949ddc849f749eee8 100644 (file)
@@ -3,38 +3,6 @@
  *
  * Copyright (C) 2005 Linus Torvalds
  *
- * Careful: order of argument flags does matter. For example,
- *
- *     git checkout-index -a -f file.c
- *
- * Will first check out all files listed in the cache (but not
- * overwrite any old ones), and then force-checkout "file.c" a
- * second time (ie that one _will_ overwrite any old contents
- * with the same filename).
- *
- * Also, just doing "git checkout-index" does nothing. You probably
- * meant "git checkout-index -a". And if you want to force it, you
- * want "git checkout-index -f -a".
- *
- * Intuitiveness is not the goal here. Repeatability is. The
- * reason for the "no arguments means no work" thing is that
- * from scripts you are supposed to be able to do things like
- *
- *     find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
- *
- * or:
- *
- *     find . -name '*.h' -print0 | git checkout-index -f -z --stdin
- *
- * which will force all existing *.h files to be replaced with
- * their cached copies. If an empty command line implied "all",
- * then this would force-refresh everything in the cache, which
- * was not the point.
- *
- * Oh, and the "--" is just a good idea when you know the rest
- * will be filenames. Just so that you wouldn't have a filename
- * of "-a" causing problems (not possible in the above example,
- * but get used to it in scripting!).
  */
 #include "builtin.h"
 #include "cache.h"
index a15784a7b8406be3ead20839aab0f8de2cc52f3d..7663bc22c9a3cb35b803815762791fec44d4c477 100644 (file)
@@ -345,8 +345,9 @@ static void remove_junk_on_signal(int signo)
 static struct ref *wanted_peer_refs(const struct ref *refs,
                struct refspec *refspec)
 {
-       struct ref *local_refs = NULL;
-       struct ref **tail = &local_refs;
+       struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
+       struct ref *local_refs = head;
+       struct ref **tail = head ? &head->next : &local_refs;
 
        get_fetch_map(refs, refspec, &tail, 0);
        if (!option_mirror)
@@ -359,8 +360,11 @@ static void write_remote_refs(const struct ref *local_refs)
 {
        const struct ref *r;
 
-       for (r = local_refs; r; r = r->next)
+       for (r = local_refs; r; r = r->next) {
+               if (!r->peer_ref)
+                       continue;
                add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
+       }
 
        pack_refs(PACK_REFS_ALL);
        clear_extra_refs();
index e1af9b19f0be71484ae9341762dc2bf89cabb70c..cb738574f729e91ff239f12db3f3f1ef1ef9570c 100644 (file)
@@ -256,8 +256,10 @@ static int list_paths(struct string_list *list, const char *with_tree,
                ;
        m = xcalloc(1, i);
 
-       if (with_tree)
-               overlay_tree_on_cache(with_tree, prefix);
+       if (with_tree) {
+               const char *max_prefix = pathspec_prefix(prefix, pattern);
+               overlay_tree_on_cache(with_tree, max_prefix);
+       }
 
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
index daf19451ba7df541d1314c880679749232ccce35..9836e6b7ca22e254c06d8d766d510ef43a8cbe90 100644 (file)
@@ -16,6 +16,7 @@
 #include "string-list.h"
 #include "utf8.h"
 #include "parse-options.h"
+#include "quote.h"
 
 static const char *fast_export_usage[] = {
        "git fast-export [rev-list-opts]",
@@ -26,6 +27,7 @@ static int progress;
 static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
 static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
 static int fake_missing_tagger;
+static int use_done_feature;
 static int no_data;
 static int full_tree;
 
@@ -178,6 +180,15 @@ static int depth_first(const void *a_, const void *b_)
        return (a->status == 'R') - (b->status == 'R');
 }
 
+static void print_path(const char *path)
+{
+       int need_quote = quote_c_style(path, NULL, NULL, 0);
+       if (need_quote)
+               quote_c_style(path, NULL, stdout, 0);
+       else
+               printf("%s", path);
+}
+
 static void show_filemodify(struct diff_queue_struct *q,
                            struct diff_options *options, void *data)
 {
@@ -195,13 +206,18 @@ static void show_filemodify(struct diff_queue_struct *q,
 
                switch (q->queue[i]->status) {
                case DIFF_STATUS_DELETED:
-                       printf("D %s\n", spec->path);
+                       printf("D ");
+                       print_path(spec->path);
+                       putchar('\n');
                        break;
 
                case DIFF_STATUS_COPIED:
                case DIFF_STATUS_RENAMED:
-                       printf("%c \"%s\" \"%s\"\n", q->queue[i]->status,
-                              ospec->path, spec->path);
+                       printf("%c ", q->queue[i]->status);
+                       print_path(ospec->path);
+                       putchar(' ');
+                       print_path(spec->path);
+                       putchar('\n');
 
                        if (!hashcmp(ospec->sha1, spec->sha1) &&
                            ospec->mode == spec->mode)
@@ -216,13 +232,15 @@ static void show_filemodify(struct diff_queue_struct *q,
                         * output the SHA-1 verbatim.
                         */
                        if (no_data || S_ISGITLINK(spec->mode))
-                               printf("M %06o %s %s\n", spec->mode,
-                                      sha1_to_hex(spec->sha1), spec->path);
+                               printf("M %06o %s ", spec->mode,
+                                      sha1_to_hex(spec->sha1));
                        else {
                                struct object *object = lookup_object(spec->sha1);
-                               printf("M %06o :%d %s\n", spec->mode,
-                                      get_object_mark(object), spec->path);
+                               printf("M %06o :%d ", spec->mode,
+                                      get_object_mark(object));
                        }
+                       print_path(spec->path);
+                       putchar('\n');
                        break;
 
                default:
@@ -627,6 +645,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                             "Fake a tagger when tags lack one"),
                OPT_BOOLEAN(0, "full-tree", &full_tree,
                             "Output full tree for each commit"),
+               OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
+                            "Use the done feature to terminate the stream"),
                { OPTION_NEGBIT, 0, "data", &no_data, NULL,
                        "Skip output of blob data",
                        PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
@@ -648,6 +668,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (argc > 1)
                usage_with_options (fast_export_usage, options);
 
+       if (use_done_feature)
+               printf("feature done\n");
+
        if (import_filename)
                import_marks(import_filename);
 
@@ -675,5 +698,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (export_filename)
                export_marks(export_filename);
 
+       if (use_done_feature)
+               printf("done\n");
+
        return 0;
 }
index cccf8da6d2a600154536ea642250699d9356f148..1851797540c17791f03e0d2fb86f5463e4e3816e 100644 (file)
@@ -827,17 +827,19 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "heading", &opt.heading,
                        "show filename only once above matches from same file"),
                OPT_GROUP(""),
-               OPT_CALLBACK('C', NULL, &opt, "n",
+               OPT_CALLBACK('C', "context", &opt, "n",
                        "show <n> context lines before and after matches",
                        context_callback),
-               OPT_INTEGER('B', NULL, &opt.pre_context,
+               OPT_INTEGER('B', "before-context", &opt.pre_context,
                        "show <n> context lines before matches"),
-               OPT_INTEGER('A', NULL, &opt.post_context,
+               OPT_INTEGER('A', "after-context", &opt.post_context,
                        "show <n> context lines after matches"),
                OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
                        context_callback),
                OPT_BOOLEAN('p', "show-function", &opt.funcname,
                        "show a line with the function name before matches"),
+               OPT_BOOLEAN('W', "function-context", &opt.funcbody,
+                       "show the surrounding function"),
                OPT_GROUP(""),
                OPT_CALLBACK('f', NULL, &opt, "file",
                        "read patterns from file", file_callback),
@@ -980,7 +982,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                use_threads = 0;
 
        if (use_threads) {
-               if (opt.pre_context || opt.post_context || opt.file_break)
+               if (opt.pre_context || opt.post_context || opt.file_break ||
+                   opt.funcbody)
                        skip_first_line = 1;
                start_threads(&opt);
        }
index 15701233e29b240cfa1abdb56bd489306e74c677..0e98bff0c46035162fc424418cc434b52299c1cc 100644 (file)
@@ -276,41 +276,6 @@ static void prune_cache(const char *prefix)
        active_nr = last;
 }
 
-static const char *pathspec_prefix(const char *prefix)
-{
-       const char **p, *n, *prev;
-       unsigned long max;
-
-       if (!pathspec) {
-               max_prefix_len = prefix ? strlen(prefix) : 0;
-               return prefix;
-       }
-
-       prev = NULL;
-       max = PATH_MAX;
-       for (p = pathspec; (n = *p) != NULL; p++) {
-               int i, len = 0;
-               for (i = 0; i < max; i++) {
-                       char c = n[i];
-                       if (prev && prev[i] != c)
-                               break;
-                       if (!c || c == '*' || c == '?')
-                               break;
-                       if (c == '/')
-                               len = i+1;
-               }
-               prev = n;
-               if (len < max) {
-                       max = len;
-                       if (!max)
-                               break;
-               }
-       }
-
-       max_prefix_len = max;
-       return max ? xmemdupz(prev, max) : NULL;
-}
-
 static void strip_trailing_slash_from_submodules(void)
 {
        const char **p;
@@ -576,7 +541,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                strip_trailing_slash_from_submodules();
 
        /* Find common prefix for all pathspec's */
-       max_prefix = pathspec_prefix(prefix);
+       max_prefix = pathspec_prefix(prefix, pathspec);
+       max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
 
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
index f08c5b0c942eec58b85ab41e5e5fb1ec216df683..6b666e1e87017f41d2f53c50b157f280b3a5f282 100644 (file)
@@ -173,7 +173,5 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        tree = parse_tree_indirect(sha1);
        if (!tree)
                die("not a tree object");
-       read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
-
-       return 0;
+       return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
 }
index ac8bed8aa1a704e81192b43beb5fe1ca5b805e85..a9c67c18ba159c8f04fa6bfff52ed9718965190a 100644 (file)
@@ -51,6 +51,8 @@ struct object_entry {
                                       * objects against.
                                       */
        unsigned char no_try_delta;
+       unsigned char tagged; /* near the very tip of refs */
+       unsigned char filled; /* assigned write-order */
 };
 
 /*
@@ -96,6 +98,7 @@ static unsigned long window_memory_limit = 0;
  */
 static int *object_ix;
 static int object_ix_hashsz;
+static struct object_entry *locate_object_entry(const unsigned char *sha1);
 
 /*
  * stats
@@ -200,6 +203,7 @@ static void copy_pack_data(struct sha1file *f,
        }
 }
 
+/* Return 0 if we will bust the pack-size limit */
 static unsigned long write_object(struct sha1file *f,
                                  struct object_entry *entry,
                                  off_t write_offset)
@@ -434,6 +438,134 @@ static int write_one(struct sha1file *f,
        return 1;
 }
 
+static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
+                      void *cb_data)
+{
+       unsigned char peeled[20];
+       struct object_entry *entry = locate_object_entry(sha1);
+
+       if (entry)
+               entry->tagged = 1;
+       if (!peel_ref(path, peeled)) {
+               entry = locate_object_entry(peeled);
+               if (entry)
+                       entry->tagged = 1;
+       }
+       return 0;
+}
+
+static void add_to_write_order(struct object_entry **wo,
+                              int *endp,
+                              struct object_entry *e)
+{
+       if (e->filled)
+               return;
+       wo[(*endp)++] = e;
+       e->filled = 1;
+}
+
+static void add_descendants_to_write_order(struct object_entry **wo,
+                                          int *endp,
+                                          struct object_entry *e)
+{
+       struct object_entry *child;
+
+       for (child = e->delta_child; child; child = child->delta_sibling)
+               add_to_write_order(wo, endp, child);
+       for (child = e->delta_child; child; child = child->delta_sibling)
+               add_descendants_to_write_order(wo, endp, child);
+}
+
+static void add_family_to_write_order(struct object_entry **wo,
+                                     int *endp,
+                                     struct object_entry *e)
+{
+       struct object_entry *root;
+
+       for (root = e; root->delta; root = root->delta)
+               ; /* nothing */
+       add_to_write_order(wo, endp, root);
+       add_descendants_to_write_order(wo, endp, root);
+}
+
+static struct object_entry **compute_write_order(void)
+{
+       int i, wo_end;
+
+       struct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));
+
+       for (i = 0; i < nr_objects; i++) {
+               objects[i].tagged = 0;
+               objects[i].filled = 0;
+               objects[i].delta_child = NULL;
+               objects[i].delta_sibling = NULL;
+       }
+
+       /*
+        * Fully connect delta_child/delta_sibling network.
+        * Make sure delta_sibling is sorted in the original
+        * recency order.
+        */
+       for (i = nr_objects - 1; 0 <= i; i--) {
+               struct object_entry *e = &objects[i];
+               if (!e->delta)
+                       continue;
+               /* Mark me as the first child */
+               e->delta_sibling = e->delta->delta_child;
+               e->delta->delta_child = e;
+       }
+
+       /*
+        * Mark objects that are at the tip of tags.
+        */
+       for_each_tag_ref(mark_tagged, NULL);
+
+       /*
+        * Give the commits in the original recency order until
+        * we see a tagged tip.
+        */
+       for (i = wo_end = 0; i < nr_objects; i++) {
+               if (objects[i].tagged)
+                       break;
+               add_to_write_order(wo, &wo_end, &objects[i]);
+       }
+
+       /*
+        * Then fill all the tagged tips.
+        */
+       for (; i < nr_objects; i++) {
+               if (objects[i].tagged)
+                       add_to_write_order(wo, &wo_end, &objects[i]);
+       }
+
+       /*
+        * And then all remaining commits and tags.
+        */
+       for (i = 0; i < nr_objects; i++) {
+               if (objects[i].type != OBJ_COMMIT &&
+                   objects[i].type != OBJ_TAG)
+                       continue;
+               add_to_write_order(wo, &wo_end, &objects[i]);
+       }
+
+       /*
+        * And then all the trees.
+        */
+       for (i = 0; i < nr_objects; i++) {
+               if (objects[i].type != OBJ_TREE)
+                       continue;
+               add_to_write_order(wo, &wo_end, &objects[i]);
+       }
+
+       /*
+        * Finally all the rest in really tight order
+        */
+       for (i = 0; i < nr_objects; i++)
+               add_family_to_write_order(wo, &wo_end, &objects[i]);
+
+       return wo;
+}
+
 static void write_pack_file(void)
 {
        uint32_t i = 0, j;
@@ -442,10 +574,12 @@ static void write_pack_file(void)
        struct pack_header hdr;
        uint32_t nr_remaining = nr_result;
        time_t last_mtime = 0;
+       struct object_entry **write_order;
 
        if (progress > pack_to_stdout)
                progress_state = start_progress("Writing objects", nr_result);
        written_list = xmalloc(nr_objects * sizeof(*written_list));
+       write_order = compute_write_order();
 
        do {
                unsigned char sha1[20];
@@ -469,7 +603,8 @@ static void write_pack_file(void)
                offset = sizeof(hdr);
                nr_written = 0;
                for (; i < nr_objects; i++) {
-                       if (!write_one(f, objects + i, &offset))
+                       struct object_entry *e = write_order[i];
+                       if (!write_one(f, e, &offset))
                                break;
                        display_progress(progress_state, written);
                }
@@ -546,6 +681,7 @@ static void write_pack_file(void)
        } while (nr_remaining && i < nr_objects);
 
        free(written_list);
+       free(write_order);
        stop_progress(&progress_state);
        if (written != nr_result)
                die("wrote %"PRIu32" objects while expecting %"PRIu32,
index e1a687ad0761e46f6ec95659bb585b9014d4abf4..60260d0aa93a878968e5ed72a2a77b96c5c0f040 100644 (file)
@@ -120,9 +120,25 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
        return 0;
 }
 
+static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       path = strip_namespace(path);
+       /*
+        * Advertise refs outside our current namespace as ".have"
+        * refs, so that the client can use them to minimize data
+        * transfer but will otherwise ignore them. This happens to
+        * cover ".have" that are thrown in by add_one_alternate_ref()
+        * to mark histories that are complete in our alternates as
+        * well.
+        */
+       if (!path)
+               path = ".have";
+       return show_ref(path, sha1, flag, cb_data);
+}
+
 static void write_head_info(void)
 {
-       for_each_ref(show_ref, NULL);
+       for_each_ref(show_ref_cb, NULL);
        if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
 
@@ -333,6 +349,8 @@ static void refuse_unconfigured_deny_delete_current(void)
 static const char *update(struct command *cmd)
 {
        const char *name = cmd->ref_name;
+       struct strbuf namespaced_name_buf = STRBUF_INIT;
+       const char *namespaced_name;
        unsigned char *old_sha1 = cmd->old_sha1;
        unsigned char *new_sha1 = cmd->new_sha1;
        struct ref_lock *lock;
@@ -343,7 +361,10 @@ static const char *update(struct command *cmd)
                return "funny refname";
        }
 
-       if (is_ref_checked_out(name)) {
+       strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+       namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+
+       if (is_ref_checked_out(namespaced_name)) {
                switch (deny_current_branch) {
                case DENY_IGNORE:
                        break;
@@ -371,7 +392,7 @@ static const char *update(struct command *cmd)
                        return "deletion prohibited";
                }
 
-               if (!strcmp(name, head_name)) {
+               if (!strcmp(namespaced_name, head_name)) {
                        switch (deny_delete_current) {
                        case DENY_IGNORE:
                                break;
@@ -427,14 +448,14 @@ static const char *update(struct command *cmd)
                        rp_warning("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
-               if (delete_ref(name, old_sha1, 0)) {
+               if (delete_ref(namespaced_name, old_sha1, 0)) {
                        rp_error("failed to delete %s", name);
                        return "failed to delete";
                }
                return NULL; /* good */
        }
        else {
-               lock = lock_any_ref_for_update(name, old_sha1, 0);
+               lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
                if (!lock) {
                        rp_error("failed to lock %s", name);
                        return "failed to lock";
@@ -491,17 +512,29 @@ static void run_update_post_hook(struct command *commands)
 
 static void check_aliased_update(struct command *cmd, struct string_list *list)
 {
+       struct strbuf buf = STRBUF_INIT;
+       const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
        unsigned char sha1[20];
        char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
        int flag;
 
-       const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+       strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+       dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+       strbuf_release(&buf);
 
        if (!(flag & REF_ISSYMREF))
                return;
 
+       dst_name = strip_namespace(dst_name);
+       if (!dst_name) {
+               rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+               cmd->skip_update = 1;
+               cmd->error_string = "broken symref";
+               return;
+       }
+
        if ((item = string_list_lookup(list, dst_name)) == NULL)
                return;
 
@@ -636,7 +669,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
 
 static const char *pack_lockfile;
 
-static const char *unpack(void)
+static const char *unpack(int quiet)
 {
        struct pack_header hdr;
        const char *hdr_err;
@@ -651,8 +684,10 @@ static const char *unpack(void)
 
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
                int code, i = 0;
-               const char *unpacker[4];
+               const char *unpacker[5];
                unpacker[i++] = "unpack-objects";
+               if (quiet)
+                       unpacker[i++] = "-q";
                if (receive_fsck_objects)
                        unpacker[i++] = "--strict";
                unpacker[i++] = hdr_arg;
@@ -753,6 +788,7 @@ static void add_alternate_refs(void)
 
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
+       int quiet = 0;
        int advertise_refs = 0;
        int stateless_rpc = 0;
        int i;
@@ -766,6 +802,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                const char *arg = *argv++;
 
                if (*arg == '-') {
+                       if (!strcmp(arg, "--quiet")) {
+                               quiet = 1;
+                               continue;
+                       }
+
                        if (!strcmp(arg, "--advertise-refs")) {
                                advertise_refs = 1;
                                continue;
@@ -814,7 +855,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                const char *unpack_status = NULL;
 
                if (!delete_only(commands))
-                       unpack_status = unpack();
+                       unpack_status = unpack(quiet);
                execute_commands(commands, unpack_status);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
index ebf610e64a267c5ca769d70203b282fb8f96a434..3a9c80f3dbfe26d5623c118fc0fcaa257e01b973 100644 (file)
@@ -777,6 +777,5 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
        if (!strcmp(argv[1], "delete"))
                return cmd_reflog_delete(argc - 1, argv + 1, prefix);
 
-       /* Not a recognized reflog command..*/
-       usage(reflog_usage);
+       return cmd_log_reflog(argc, argv, prefix);
 }
index 98bca044c1267fa63cb084a45aaf621e85f7d9b6..811e8e252c1c6a54e65179557203daf2bc42bdb9 100644 (file)
@@ -33,25 +33,6 @@ static const char *reset_type_names[] = {
        N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL
 };
 
-static char *args_to_str(const char **argv)
-{
-       char *buf = NULL;
-       unsigned long len, space = 0, nr = 0;
-
-       for (; *argv; argv++) {
-               len = strlen(*argv);
-               ALLOC_GROW(buf, nr + 1 + len, space);
-               if (nr)
-                       buf[nr++] = ' ';
-               memcpy(buf + nr, *argv, len);
-               nr += len;
-       }
-       ALLOC_GROW(buf, nr + 1, space);
-       buf[nr] = '\0';
-
-       return buf;
-}
-
 static inline int is_merge(void)
 {
        return !access(git_path("MERGE_HEAD"), F_OK);
@@ -162,7 +143,7 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *one = q->queue[i]->one;
-               if (one->mode) {
+               if (one->mode && !is_null_sha1(one->sha1)) {
                        struct cache_entry *ce;
                        ce = make_cache_entry(one->mode, one->sha1, one->path,
                                0, 0);
@@ -215,14 +196,18 @@ static int read_from_tree(const char *prefix, const char **argv,
        return update_index_refresh(index_fd, lock, refresh_flags);
 }
 
-static void prepend_reflog_action(const char *action, char *buf, size_t size)
+static void set_reflog_message(struct strbuf *sb, const char *action,
+                              const char *rev)
 {
-       const char *sep = ": ";
        const char *rla = getenv("GIT_REFLOG_ACTION");
-       if (!rla)
-               rla = sep = "";
-       if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
-               warning(_("Reflog action message too long: %.*s..."), 50, buf);
+
+       strbuf_reset(sb);
+       if (rla)
+               strbuf_addf(sb, "%s: %s", rla, action);
+       else if (rev)
+               strbuf_addf(sb, "reset: moving to %s", rev);
+       else
+               strbuf_addf(sb, "reset: %s", action);
 }
 
 static void die_if_unmerged_cache(int reset_type)
@@ -241,7 +226,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        unsigned char sha1[20], *orig = NULL, sha1_orig[20],
                                *old_orig = NULL, sha1_old_orig[20];
        struct commit *commit;
-       char *reflog_action, msg[1024];
+       struct strbuf msg = STRBUF_INIT;
        const struct option options[] = {
                OPT__QUIET(&quiet, "be quiet, only report errors"),
                OPT_SET_INT(0, "mixed", &reset_type,
@@ -261,8 +246,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, git_reset_usage,
                                                PARSE_OPT_KEEP_DASHDASH);
-       reflog_action = args_to_str(argv);
-       setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
        /*
         * Possible arguments are:
@@ -357,13 +340,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                old_orig = sha1_old_orig;
        if (!get_sha1("HEAD", sha1_orig)) {
                orig = sha1_orig;
-               prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
-               update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+               set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+               update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
        }
        else if (old_orig)
                delete_ref("ORIG_HEAD", old_orig, 0);
-       prepend_reflog_action("updating HEAD", msg, sizeof(msg));
-       update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+       set_reflog_message(&msg, "updating HEAD", rev);
+       update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
 
        switch (reset_type) {
        case HARD:
@@ -380,7 +363,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        remove_branch_state();
 
-       free(reflog_action);
+       strbuf_release(&msg);
 
        return update_ref_status;
 }
index c1f6ddd927d61fbc2558ee3624224af405d918c3..40a1675997cee19afb77b94bdd514fb5bc9a27bc 100644 (file)
@@ -439,6 +439,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                args.force_update = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--quiet")) {
+                               args.quiet = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--verbose")) {
                                args.verbose = 1;
                                continue;
@@ -488,8 +492,13 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
-               conn = git_connect(fd, dest, receivepack,
+               struct strbuf sb = STRBUF_INIT;
+               strbuf_addstr(&sb, receivepack);
+               if (args.quiet)
+                       strbuf_addstr(&sb, " --quiet");
+               conn = git_connect(fd, dest, sb.buf,
                        args.verbose ? CONNECT_VERBOSE : 0);
+               strbuf_release(&sb);
        }
 
        memset(&extra_have, 0, sizeof(extra_have));
diff --git a/cache.h b/cache.h
index 9e12d55470435102215f2dd99eb3432856612dbd..fcf4501a60d14105134a2d5be984afde5dda3e3d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -394,6 +394,7 @@ static inline enum object_type object_type(unsigned int mode)
 }
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@ -434,6 +435,8 @@ extern char *get_object_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
+extern const char *get_git_namespace(void);
+extern const char *strip_namespace(const char *namespaced_ref);
 extern const char *get_git_work_tree(void);
 extern const char *read_gitfile_gently(const char *path);
 extern void set_git_work_tree(const char *tree);
@@ -441,6 +444,7 @@ extern void set_git_work_tree(const char *tree);
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
+extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
 extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
@@ -773,6 +777,9 @@ extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
+/* for development: log offset of pack access */
+extern const char *log_pack_access;
+
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
index be67cfcd45469497f81e0c36ed87a11fd409daee..b11eb7102c53d3418aa875ee899e61e01de87bca 100644 (file)
@@ -768,7 +768,8 @@ static void show_combined_header(struct combine_diff_path *elem,
 }
 
 static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
-                           int dense, struct rev_info *rev)
+                           int dense, int working_tree_file,
+                           struct rev_info *rev)
 {
        struct diff_options *opt = &rev->diffopt;
        unsigned long result_size, cnt, lno;
@@ -777,7 +778,6 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        struct sline *sline; /* survived lines */
        int mode_differs = 0;
        int i, show_hunks;
-       int working_tree_file = is_null_sha1(elem->sha1);
        mmfile_t result_file;
        struct userdiff_driver *userdiff;
        struct userdiff_driver *textconv = NULL;
@@ -1028,6 +1028,12 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
        write_name_quoted(p->path, stdout, line_termination);
 }
 
+/*
+ * The result (p->elem) is from the working tree and their
+ * parents are typically from multiple stages during a merge
+ * (i.e. diff-files) or the state in HEAD and in the index
+ * (i.e. diff-index).
+ */
 void show_combined_diff(struct combine_diff_path *p,
                       int num_parent,
                       int dense,
@@ -1041,7 +1047,7 @@ void show_combined_diff(struct combine_diff_path *p,
                                  DIFF_FORMAT_NAME_STATUS))
                show_raw_diff(p, num_parent, rev);
        else if (opt->output_format & DIFF_FORMAT_PATCH)
-               show_patch_diff(p, num_parent, dense, rev);
+               show_patch_diff(p, num_parent, dense, 1, rev);
 }
 
 void diff_tree_combined(const unsigned char *sha1,
@@ -1109,7 +1115,7 @@ void diff_tree_combined(const unsigned char *sha1,
                        for (p = paths; p; p = p->next) {
                                if (p->len)
                                        show_patch_diff(p, num_parent, dense,
-                                                       rev);
+                                                       0, rev);
                        }
                }
        }
index e42c59b190986d28ec535861e08b843739356b9a..4183f80262ea9f24e286295a2295f93459548b78 100644 (file)
--- a/config.c
+++ b/config.c
@@ -576,6 +576,9 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.logpackaccess"))
+               return git_config_string(&log_pack_access, var, value);
+
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        if (core_eol == EOL_CRLF)
index 844107e2954f6cb8354990d69c6568b9d3091470..ee1d4b4b46f789168c155eb170b87dbed85fb590 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -217,7 +217,7 @@ static int git_tcp_connect_sock(char *host, int flags)
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
 
-       for (ai0 = ai; ai; ai = ai->ai_next) {
+       for (ai0 = ai; ai; ai = ai->ai_next, cnt++) {
                sockfd = socket(ai->ai_family,
                                ai->ai_socktype, ai->ai_protocol);
                if ((sockfd < 0) ||
@@ -254,7 +254,8 @@ static int git_tcp_connect_sock(char *host, int flags)
  */
 static int git_tcp_connect_sock(char *host, int flags)
 {
-       int sockfd = -1, saved_errno = 0;
+       struct strbuf error_message = STRBUF_INIT;
+       int sockfd = -1;
        const char *port = STR(DEFAULT_GIT_PORT);
        char *ep;
        struct hostent *he;
@@ -284,25 +285,21 @@ static int git_tcp_connect_sock(char *host, int flags)
                fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
 
        for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
-               sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
-               if (sockfd < 0) {
-                       saved_errno = errno;
-                       continue;
-               }
-
                memset(&sa, 0, sizeof sa);
                sa.sin_family = he->h_addrtype;
                sa.sin_port = htons(nport);
                memcpy(&sa.sin_addr, *ap, he->h_length);
 
-               if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
-                       saved_errno = errno;
-                       fprintf(stderr, "%s[%d: %s]: errno=%s\n",
+               sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
+               if ((sockfd < 0) ||
+                   connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+                       strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n",
                                host,
                                cnt,
                                inet_ntoa(*(struct in_addr *)&sa.sin_addr),
-                               strerror(saved_errno));
-                       close(sockfd);
+                               strerror(errno));
+                       if (0 <= sockfd)
+                               close(sockfd);
                        sockfd = -1;
                        continue;
                }
@@ -313,7 +310,7 @@ static int git_tcp_connect_sock(char *host, int flags)
        }
 
        if (sockfd < 0)
-               die("unable to connect a socket (%s)", strerror(saved_errno));
+               die("unable to connect to %s:\n%s", host, error_message.buf);
 
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "done.\n");
index 5a8309076dc633f431797c4bd59876490b0d7408..8648a36e7b8239e969dc8971f451431c298e405e 100755 (executable)
@@ -1469,7 +1469,7 @@ _git_help ()
        __gitcomp "$__git_all_commands $(__git_aliases)
                attributes cli core-tutorial cvs-migration
                diffcore gitk glossary hooks ignore modules
-               repository-layout tutorial tutorial-2
+               namespaces repository-layout tutorial tutorial-2
                workflows
                "
 }
@@ -2640,6 +2640,7 @@ _git ()
                        --exec-path
                        --html-path
                        --work-tree=
+                       --namespace=
                        --help
                        "
                        ;;
index 98d2aee67fc1f6bfdfc960527b6f0bf6acf5122a..6b9de9e7e0e3304d42048ff3680eac9270330ee4 100755 (executable)
@@ -1649,7 +1649,8 @@ class P4Sync(Command, P4UserMap):
     def importHeadRevision(self, revision):
         print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
 
-        details = { "user" : "git perforce import user", "time" : int(time.time()) }
+        details = {}
+        details["user"] = "git perforce import user"
         details["desc"] = ("Initial import of %s from the state at revision %s\n"
                            % (' '.join(self.depotPaths), revision))
         details["change"] = revision
@@ -1689,6 +1690,18 @@ class P4Sync(Command, P4UserMap):
             fileCnt = fileCnt + 1
 
         details["change"] = newestRevision
+
+        # Use time from top-most change so that all git-p4 clones of
+        # the same p4 repo have the same commit SHA1s.
+        res = p4CmdList("describe -s %d" % newestRevision)
+        newestTime = None
+        for r in res:
+            if r.has_key('time'):
+                newestTime = int(r['time'])
+        if newestTime is None:
+            die("\"describe -s\" on newest change %d did not give a time")
+        details["time"] = newestTime
+
         self.updateOptionDict(details)
         try:
             self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
index 21989fc6ab84c0503cdc9418673d9d0bf06f9d49..fa6d41a1ab8c5f3de777c1ba93d241eb5140a3c7 100755 (executable)
 #   email body. If not specified, there is no limit.
 #   Lines beyond the limit are suppressed and counted, and a final
 #   line is added indicating the number of suppressed lines.
+# hooks.diffopts
+#   Alternate options for the git diff-tree invocation that shows changes.
+#   Default is "--stat --summary --find-copies-harder". Add -p to those
+#   options to include a unified diff of changes in addition to the usual
+#   summary output.
 #
 # Notes
 # -----
@@ -446,7 +451,7 @@ generate_update_branch_email()
        # non-fast-forward updates.
        echo ""
        echo "Summary of changes:"
-       git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
+       git diff-tree $diffopts $oldrev..$newrev
 }
 
 #
@@ -723,6 +728,8 @@ envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
 custom_showrev=$(git config hooks.showrev)
 maxlines=$(git config hooks.emailmaxlines)
+diffopts=$(git config hooks.diffopts)
+: ${diffopts:="--stat --summary --find-copies-harder"}
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
index 2e09500c8210e22d6c81484aa198f1d811a9933d..f8454dd2918dc74bee43d9f2faa1837cc5665abd 100644 (file)
@@ -379,7 +379,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        if (cached && idx && ce_stage(idx)) {
                struct diff_filepair *pair;
                pair = diff_unmerge(&revs->diffopt, idx->name);
-               fill_filespec(pair->one, idx->sha1, idx->ce_mode);
+               if (tree)
+                       fill_filespec(pair->one, tree->sha1, tree->ce_mode);
                return;
        }
 
@@ -444,20 +445,19 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
        return 0;
 }
 
-int run_diff_index(struct rev_info *revs, int cached)
+static int diff_cache(struct rev_info *revs,
+                     const unsigned char *tree_sha1,
+                     const char *tree_name,
+                     int cached)
 {
-       struct object *ent;
        struct tree *tree;
-       const char *tree_name;
-       struct unpack_trees_options opts;
        struct tree_desc t;
+       struct unpack_trees_options opts;
 
-       ent = revs->pending.objects[0].item;
-       tree_name = revs->pending.objects[0].name;
-       tree = parse_tree_indirect(ent->sha1);
+       tree = parse_tree_indirect(tree_sha1);
        if (!tree)
-               return error("bad tree object %s", tree_name);
-
+               return error("bad tree object %s",
+                            tree_name ? tree_name : sha1_to_hex(tree_sha1));
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
        opts.index_only = cached;
@@ -470,7 +470,15 @@ int run_diff_index(struct rev_info *revs, int cached)
        opts.dst_index = NULL;
 
        init_tree_desc(&t, tree->buffer, tree->size);
-       if (unpack_trees(1, &t, &opts))
+       return unpack_trees(1, &t, &opts);
+}
+
+int run_diff_index(struct rev_info *revs, int cached)
+{
+       struct object_array_entry *ent;
+
+       ent = revs->pending.objects;
+       if (diff_cache(revs, ent->item->sha1, ent->name, cached))
                exit(128);
 
        diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
@@ -482,53 +490,13 @@ int run_diff_index(struct rev_info *revs, int cached)
 
 int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
 {
-       struct tree *tree;
        struct rev_info revs;
-       int i;
-       struct cache_entry **dst;
-       struct cache_entry *last = NULL;
-       struct unpack_trees_options opts;
-       struct tree_desc t;
-
-       /*
-        * This is used by git-blame to run diff-cache internally;
-        * it potentially needs to repeatedly run this, so we will
-        * start by removing the higher order entries the last round
-        * left behind.
-        */
-       dst = active_cache;
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
-               if (ce_stage(ce)) {
-                       if (last && !strcmp(ce->name, last->name))
-                               continue;
-                       cache_tree_invalidate_path(active_cache_tree,
-                                                  ce->name);
-                       last = ce;
-                       ce->ce_flags |= CE_REMOVE;
-               }
-               *dst++ = ce;
-       }
-       active_nr = dst - active_cache;
 
        init_revisions(&revs, NULL);
        init_pathspec(&revs.prune_data, opt->pathspec.raw);
-       tree = parse_tree_indirect(tree_sha1);
-       if (!tree)
-               die("bad tree object %s", sha1_to_hex(tree_sha1));
+       revs.diffopt = *opt;
 
-       memset(&opts, 0, sizeof(opts));
-       opts.head_idx = 1;
-       opts.index_only = 1;
-       opts.diff_index_cached = !DIFF_OPT_TST(opt, FIND_COPIES_HARDER);
-       opts.merge = 1;
-       opts.fn = oneway_diff;
-       opts.unpack_data = &revs;
-       opts.src_index = &the_index;
-       opts.dst_index = &the_index;
-
-       init_tree_desc(&t, tree->buffer, tree->size);
-       if (unpack_trees(1, &t, &opts))
+       if (diff_cache(&revs, tree_sha1, NULL, 1))
                exit(128);
        return 0;
 }
diff --git a/diff.c b/diff.c
index 93ef9a265ca6b52b644468979dba732c70b5097a..d3d8daec77142bc6a67aacdc2e62ee522ddf12db 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3393,6 +3393,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--patience"))
                DIFF_XDL_SET(options, PATIENCE_DIFF);
+       else if (!strcmp(arg, "--histogram"))
+               DIFF_XDL_SET(options, HISTOGRAM_DIFF);
 
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
index 94d58fd24413ec1d1a5769029bd5149b609f0d4e..03d29e8d48f46428c4efdacac84e6a0e1ddf4a0b 100644 (file)
@@ -8,6 +8,7 @@
  * are.
  */
 #include "cache.h"
+#include "refs.h"
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
@@ -36,6 +37,7 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
+const char *log_pack_access;
 const char *pager_program;
 int pager_use_color = 1;
 const char *editor_program;
@@ -65,6 +67,9 @@ int core_preload_index = 0;
 char *git_work_tree_cfg;
 static char *work_tree;
 
+static const char *namespace;
+static size_t namespace_len;
+
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_graft_file;
 
@@ -86,6 +91,27 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
        NULL
 };
 
+static char *expand_namespace(const char *raw_namespace)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf **components, **c;
+
+       if (!raw_namespace || !*raw_namespace)
+               return xstrdup("");
+
+       strbuf_addstr(&buf, raw_namespace);
+       components = strbuf_split(&buf, '/');
+       strbuf_reset(&buf);
+       for (c = components; *c; c++)
+               if (strcmp((*c)->buf, "/") != 0)
+                       strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
+       strbuf_list_free(components);
+       if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)
+               die("bad git namespace path \"%s\"", raw_namespace);
+       strbuf_addch(&buf, '/');
+       return strbuf_detach(&buf, NULL);
+}
+
 static void setup_git_env(void)
 {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
@@ -111,6 +137,8 @@ static void setup_git_env(void)
                git_graft_file = git_pathdup("info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                read_replace_refs = 0;
+       namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
+       namespace_len = strlen(namespace);
 }
 
 int is_bare_repository(void)
@@ -131,6 +159,20 @@ const char *get_git_dir(void)
        return git_dir;
 }
 
+const char *get_git_namespace(void)
+{
+       if (!namespace)
+               setup_git_env();
+       return namespace;
+}
+
+const char *strip_namespace(const char *namespaced_ref)
+{
+       if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+               return NULL;
+       return namespaced_ref + namespace_len;
+}
+
 static int git_work_tree_initialized;
 
 /*
index 9e8d1868aa3b178f4b61ef57430883854898b044..7cc22625e5b73d2778566a154bce94316311b0c8 100644 (file)
@@ -355,6 +355,7 @@ static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
 static int seen_data_command;
+static int require_explicit_termination;
 
 /* Signal handling */
 static volatile sig_atomic_t checkpoint_requested;
@@ -3140,6 +3141,8 @@ static int parse_one_feature(const char *feature, int from_stream)
                relative_marks_paths = 1;
        } else if (!strcmp(feature, "no-relative-marks")) {
                relative_marks_paths = 0;
+       } else if (!strcmp(feature, "done")) {
+               require_explicit_termination = 1;
        } else if (!strcmp(feature, "force")) {
                force_update = 1;
        } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
@@ -3290,6 +3293,8 @@ int main(int argc, const char **argv)
                        parse_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
+               else if (!strcmp("done", command_buf.buf))
+                       break;
                else if (!prefixcmp(command_buf.buf, "progress "))
                        parse_progress();
                else if (!prefixcmp(command_buf.buf, "feature "))
@@ -3309,6 +3314,9 @@ int main(int argc, const char **argv)
        if (!seen_data_command)
                parse_argv();
 
+       if (require_explicit_termination && feof(stdin))
+               die("stream ends early");
+
        end_packfile();
 
        dump_branches();
index 3ef4861d04ed5fed14d31b92e047a03cf5362970..1093ef4ad6b9793fb829403c41391a09a07e57a2 100755 (executable)
@@ -15,8 +15,8 @@ do
      sed -n '
      /^NAME/,/git-'"$cmd"'/H
      ${
-            x
-            s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", "\1"},/
+           x
+           s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", "\1"},/
            p
      }' "Documentation/git-$cmd.txt"
 done
index 463c741dfc259927b4429f19972ced21d87c215e..65924246d0fae0fa514c815021e2dc3369a7ceb9 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -511,6 +511,8 @@ else
        fi
 fi
 
+git update-index -q --refresh
+
 case "$resolved" in
 '')
        case "$HAS_HEAD" in
index b2186a86279e8919214bb205400a05f32a316c3b..e0ca3fb853083d0ebdfe11e5eb3fead4723248ad 100755 (executable)
@@ -2,38 +2,47 @@
 
 USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
 LONG_USAGE='git bisect help
-        print this long help message.
-git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
-        reset bisect state and start bisection.
+       print this long help message.
+git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+       reset bisect state and start bisection.
 git bisect bad [<rev>]
-        mark <rev> a known-bad revision.
+       mark <rev> a known-bad revision.
 git bisect good [<rev>...]
-        mark <rev>... known-good revisions.
+       mark <rev>... known-good revisions.
 git bisect skip [(<rev>|<range>)...]
-        mark <rev>... untestable revisions.
+       mark <rev>... untestable revisions.
 git bisect next
-        find next bisection to test and check it out.
+       find next bisection to test and check it out.
 git bisect reset [<commit>]
-        finish bisection search and go back to commit.
+       finish bisection search and go back to commit.
 git bisect visualize
-        show bisect status in gitk.
+       show bisect status in gitk.
 git bisect replay <logfile>
-        replay bisection log.
+       replay bisection log.
 git bisect log
-        show bisect log.
+       show bisect log.
 git bisect run <cmd>...
-        use <cmd>... to automatically bisect.
+       use <cmd>... to automatically bisect.
 
 Please use "git help bisect" to get the full man page.'
 
 OPTIONS_SPEC=
 . git-sh-setup
 . git-sh-i18n
-require_work_tree
 
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 
+bisect_head()
+{
+       if test -f "$GIT_DIR/BISECT_HEAD"
+       then
+               echo BISECT_HEAD
+       else
+               echo HEAD
+       fi
+}
+
 bisect_autostart() {
        test -s "$GIT_DIR/BISECT_START" || {
                (
@@ -45,7 +54,7 @@ bisect_autostart() {
                        # TRANSLATORS: Make sure to include [Y] and [n] in your
                        # translation. The program will only accept English input
                        # at this point.
-           gettext "Do you want me to do it for you [Y/n]? " >&2
+                       gettext "Do you want me to do it for you [Y/n]? " >&2
                        read yesno
                        case "$yesno" in
                        [Nn]*)
@@ -59,6 +68,50 @@ bisect_autostart() {
 }
 
 bisect_start() {
+       #
+       # Check for one bad and then some good revisions.
+       #
+       has_double_dash=0
+       for arg; do
+               case "$arg" in --) has_double_dash=1; break ;; esac
+       done
+       orig_args=$(git rev-parse --sq-quote "$@")
+       bad_seen=0
+       eval=''
+       if test "z$(git rev-parse --is-bare-repository)" != zfalse
+       then
+               mode=--no-checkout
+       else
+               mode=''
+       fi
+       while [ $# -gt 0 ]; do
+               arg="$1"
+               case "$arg" in
+               --)
+                       shift
+                       break
+               ;;
+               --no-checkout)
+                       mode=--no-checkout
+                       shift ;;
+               --*)
+                       die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
+               *)
+                       rev=$(git rev-parse -q --verify "$arg^{commit}") || {
+                               test $has_double_dash -eq 1 &&
+                               die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
+                               break
+                       }
+                       case $bad_seen in
+                       0) state='bad' ; bad_seen=1 ;;
+                       *) state='good' ;;
+                       esac
+                       eval="$eval bisect_write '$state' '$rev' 'nolog' &&"
+                       shift
+                       ;;
+               esac
+       done
+
        #
        # Verify HEAD.
        #
@@ -74,7 +127,10 @@ bisect_start() {
        then
                # Reset to the rev from where we started.
                start_head=$(cat "$GIT_DIR/BISECT_START")
-               git checkout "$start_head" -- || exit
+               if test "z$mode" != "z--no-checkout"
+               then
+                       git checkout "$start_head" --
+               fi
        else
                # Get rev from where we start.
                case "$head" in
@@ -97,39 +153,6 @@ bisect_start() {
        #
        bisect_clean_state || exit
 
-       #
-       # Check for one bad and then some good revisions.
-       #
-       has_double_dash=0
-       for arg; do
-           case "$arg" in --) has_double_dash=1; break ;; esac
-       done
-       orig_args=$(git rev-parse --sq-quote "$@")
-       bad_seen=0
-       eval=''
-       while [ $# -gt 0 ]; do
-           arg="$1"
-           case "$arg" in
-           --)
-               shift
-               break
-               ;;
-           *)
-               rev=$(git rev-parse -q --verify "$arg^{commit}") || {
-                   test $has_double_dash -eq 1 &&
-                       die "$(eval_gettext "'\$arg' does not appear to be a valid revision")"
-                   break
-               }
-               case $bad_seen in
-               0) state='bad' ; bad_seen=1 ;;
-               *) state='good' ;;
-               esac
-               eval="$eval bisect_write '$state' '$rev' 'nolog'; "
-               shift
-               ;;
-           esac
-       done
-
        #
        # Change state.
        # In case of mistaken revs or checkout error, or signals received,
@@ -143,9 +166,12 @@ bisect_start() {
        #
        # Write new start state.
        #
-       echo "$start_head" >"$GIT_DIR/BISECT_START" &&
+       echo "$start_head" >"$GIT_DIR/BISECT_START" && {
+               test "z$mode" != "z--no-checkout" ||
+               git update-ref --no-deref BISECT_HEAD "$start_head"
+       } &&
        git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" &&
-       eval "$eval" &&
+       eval "$eval true" &&
        echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
        #
        # Check if we can proceed to the next bisect state.
@@ -176,7 +202,8 @@ is_expected_rev() {
 
 check_expected_revs() {
        for _rev in "$@"; do
-               if ! is_expected_rev "$_rev"; then
+               if ! is_expected_rev "$_rev"
+               then
                        rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
                        rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
                        return
@@ -185,18 +212,18 @@ check_expected_revs() {
 }
 
 bisect_skip() {
-        all=''
+       all=''
        for arg in "$@"
        do
-           case "$arg" in
-            *..*)
-               revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
-            *)
-                revs=$(git rev-parse --sq-quote "$arg") ;;
-           esac
-            all="$all $revs"
-        done
-        eval bisect_state 'skip' $all
+               case "$arg" in
+               *..*)
+                       revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
+               *)
+                       revs=$(git rev-parse --sq-quote "$arg") ;;
+               esac
+               all="$all $revs"
+       done
+       eval bisect_state 'skip' $all
 }
 
 bisect_state() {
@@ -206,8 +233,8 @@ bisect_state() {
        0,*)
                die "$(gettext "Please call 'bisect_state' with at least one argument.")" ;;
        1,bad|1,good|1,skip)
-               rev=$(git rev-parse --verify HEAD) ||
-                       die "$(gettext "Bad rev input: HEAD")"
+               rev=$(git rev-parse --verify $(bisect_head)) ||
+                       die "$(gettext "Bad rev input: $(bisect_head)")"
                bisect_write "$state" "$rev"
                check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
@@ -291,10 +318,10 @@ bisect_next() {
        bisect_next_check good
 
        # Perform all bisection computation, display and checkout
-       git bisect--helper --next-all
+       git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
        res=$?
 
-        # Check if we should exit because bisection is finished
+       # Check if we should exit because bisection is finished
        test $res -eq 10 && exit 0
 
        # Check for an error in the bisection process
@@ -309,7 +336,8 @@ bisect_visualize() {
        if test $# = 0
        then
                if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
-                  type gitk >/dev/null 2>&1; then
+                       type gitk >/dev/null 2>&1
+               then
                        set gitk
                else
                        set git log
@@ -333,19 +361,20 @@ bisect_reset() {
        case "$#" in
        0) branch=$(cat "$GIT_DIR/BISECT_START") ;;
        1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || {
-              invalid="$1"
-              die "$(eval_gettext "'\$invalid' is not a valid commit")"
-          }
-          branch="$1" ;;
+                       invalid="$1"
+                       die "$(eval_gettext "'\$invalid' is not a valid commit")"
+               }
+               branch="$1" ;;
        *)
-           usage ;;
+               usage ;;
        esac
-       if git checkout "$branch" -- ; then
-               bisect_clean_state
-       else
+
+       if ! test -f "$GIT_DIR/BISECT_HEAD" && ! git checkout "$branch" --
+       then
                die "$(eval_gettext "Could not check out original HEAD '\$branch'.
 Try 'git bisect reset <commit>'.")"
        fi
+       bisect_clean_state
 }
 
 bisect_clean_state() {
@@ -362,7 +391,8 @@ bisect_clean_state() {
        rm -f "$GIT_DIR/BISECT_RUN" &&
        # Cleanup head-name if it got left by an old version of git-bisect
        rm -f "$GIT_DIR/head-name" &&
-
+       git update-ref -d --no-deref BISECT_HEAD &&
+       # clean up BISECT_START last
        rm -f "$GIT_DIR/BISECT_START"
 }
 
@@ -374,7 +404,8 @@ bisect_replay () {
        while read git bisect command rev
        do
                test "$git $bisect" = "git bisect" -o "$git" = "git-bisect" || continue
-               if test "$git" = "git-bisect"; then
+               if test "$git" = "git-bisect"
+               then
                        rev="$command"
                        command="$bisect"
                fi
@@ -392,65 +423,71 @@ bisect_replay () {
 }
 
 bisect_run () {
-    bisect_next_check fail
-
-    while true
-    do
-      command="$@"
-      eval_gettext "running \$command"; echo
-      "$@"
-      res=$?
-
-      # Check for really bad run error.
-      if [ $res -lt 0 -o $res -ge 128 ]; then
-         (
-           eval_gettext "bisect run failed:
+       bisect_next_check fail
+
+       while true
+       do
+               command="$@"
+               eval_gettext "running \$command"; echo
+               "$@"
+               res=$?
+
+               # Check for really bad run error.
+               if [ $res -lt 0 -o $res -ge 128 ]
+               then
+                       (
+                               eval_gettext "bisect run failed:
 exit code \$res from '\$command' is < 0 or >= 128" &&
-           echo
-         ) >&2
-         exit $res
-      fi
-
-      # Find current state depending on run success or failure.
-      # A special exit code of 125 means cannot test.
-      if [ $res -eq 125 ]; then
-         state='skip'
-      elif [ $res -gt 0 ]; then
-         state='bad'
-      else
-         state='good'
-      fi
-
-      # We have to use a subshell because "bisect_state" can exit.
-      ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
-      res=$?
-
-      cat "$GIT_DIR/BISECT_RUN"
-
-      if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
-               > /dev/null; then
-         (
-             gettext "bisect run cannot continue any more" &&
-             echo
-         ) >&2
-         exit $res
-      fi
-
-      if [ $res -ne 0 ]; then
-         (
-             eval_gettext "bisect run failed:
+                               echo
+                       ) >&2
+                       exit $res
+               fi
+
+               # Find current state depending on run success or failure.
+               # A special exit code of 125 means cannot test.
+               if [ $res -eq 125 ]
+               then
+                       state='skip'
+               elif [ $res -gt 0 ]
+               then
+                       state='bad'
+               else
+                       state='good'
+               fi
+
+               # We have to use a subshell because "bisect_state" can exit.
+               ( bisect_state $state > "$GIT_DIR/BISECT_RUN" )
+               res=$?
+
+               cat "$GIT_DIR/BISECT_RUN"
+
+               if sane_grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \
+                       > /dev/null
+               then
+                       (
+                               gettext "bisect run cannot continue any more" &&
+                               echo
+                       ) >&2
+                       exit $res
+               fi
+
+               if [ $res -ne 0 ]
+               then
+                       (
+                               eval_gettext "bisect run failed:
 'bisect_state \$state' exited with error code \$res" &&
-             echo
-         ) >&2
-         exit $res
-      fi
+                               echo
+                       ) >&2
+                       exit $res
+               fi
 
-      if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
-         gettext "bisect run success"; echo
-         exit 0;
-      fi
+               if sane_grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null
+               then
+                       gettext "bisect run success"; echo
+                       exit 0;
+               fi
 
-    done
+       done
 }
 
 bisect_log () {
@@ -460,33 +497,33 @@ bisect_log () {
 
 case "$#" in
 0)
-    usage ;;
+       usage ;;
 *)
-    cmd="$1"
-    shift
-    case "$cmd" in
-    help)
-        git bisect -h ;;
-    start)
-        bisect_start "$@" ;;
-    bad|good)
-        bisect_state "$cmd" "$@" ;;
-    skip)
-        bisect_skip "$@" ;;
-    next)
-        # Not sure we want "next" at the UI level anymore.
-        bisect_next "$@" ;;
-    visualize|view)
-       bisect_visualize "$@" ;;
-    reset)
-        bisect_reset "$@" ;;
-    replay)
-       bisect_replay "$@" ;;
-    log)
-       bisect_log ;;
-    run)
-        bisect_run "$@" ;;
-    *)
-        usage ;;
-    esac
+       cmd="$1"
+       shift
+       case "$cmd" in
+       help)
+               git bisect -h ;;
+       start)
+               bisect_start "$@" ;;
+       bad|good)
+               bisect_state "$cmd" "$@" ;;
+       skip)
+               bisect_skip "$@" ;;
+       next)
+               # Not sure we want "next" at the UI level anymore.
+               bisect_next "$@" ;;
+       visualize|view)
+               bisect_visualize "$@" ;;
+       reset)
+               bisect_reset "$@" ;;
+       replay)
+               bisect_replay "$@" ;;
+       log)
+               bisect_log ;;
+       run)
+               bisect_run "$@" ;;
+       *)
+               usage ;;
+       esac
 esac
index ddfbf771492c599e7a6ca04bb39962cf4c7a784c..5ef8ff76f6fd262c3109d25d706ebd3db35c73fa 100644 (file)
@@ -240,6 +240,7 @@ extern char *gitbasename(char *);
 
 /* General helper functions */
 extern void vreportf(const char *prefix, const char *err, va_list params);
+extern void vwritef(int fd, const char *prefix, const char *err, va_list params);
 extern NORETURN void usage(const char *err);
 extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -248,6 +249,7 @@ extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
+extern void set_error_routine(void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
 extern int suffixcmp(const char *str, const char *suffix);
index 962a93b586571eb6fc60aae53c77f6e6b9fb281f..804a7f4bc912ab0c9c51038456d9abe4216947a5 100755 (executable)
@@ -12,7 +12,7 @@
 
 functions=$(cat << \EOF
 warn () {
-        echo "$*" >&2
+       echo "$*" >&2
 }
 
 map()
@@ -98,11 +98,11 @@ set_ident () {
 }
 
 USAGE="[--env-filter <command>] [--tree-filter <command>]
-            [--index-filter <command>] [--parent-filter <command>]
-            [--msg-filter <command>] [--commit-filter <command>]
-            [--tag-name-filter <command>] [--subdirectory-filter <directory>]
-            [--original <namespace>] [-d <directory>] [-f | --force]
-            [<rev-list options>...]"
+       [--index-filter <command>] [--parent-filter <command>]
+       [--msg-filter <command>] [--commit-filter <command>]
+       [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+       [--original <namespace>] [-d <directory>] [-f | --force]
+       [<rev-list options>...]"
 
 OPTIONS_SPEC=
 . git-sh-setup
@@ -363,7 +363,7 @@ while read commit parents; do
        sed -e '1,/^$/d' <../commit | \
                eval "$filter_msg" > ../message ||
                        die "msg filter failed: $filter_msg"
-       @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
+       workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
                $(git write-tree) $parentstr < ../message > ../map/$commit ||
                        die "could not write rewritten commit"
 done <../revs
index 91f90acfba2a710644f4a076d27d50e0fbb33c2d..9a89e8f31981a0c82cf25ab6c75a6f841c7ba79b 100644 (file)
@@ -79,7 +79,7 @@ get_merge_tool_cmd () {
        fi
        if diff_mode; then
                echo "$(git config difftool.$merge_tool.cmd ||
-                       git config mergetool.$merge_tool.cmd)"
+                       git config mergetool.$merge_tool.cmd)"
        else
                echo "$(git config mergetool.$merge_tool.cmd)"
        fi
@@ -419,7 +419,7 @@ get_merge_tool_path () {
        fi
        if diff_mode; then
                merge_tool_path=$(git config difftool."$merge_tool".path ||
-                                 git config mergetool."$merge_tool".path)
+                                 git config mergetool."$merge_tool".path)
        else
                merge_tool_path=$(git config mergetool."$merge_tool".path)
        fi
@@ -429,7 +429,7 @@ get_merge_tool_path () {
        if test -z "$(get_merge_tool_cmd "$merge_tool")" &&
        ! type "$merge_tool_path" > /dev/null 2>&1; then
                echo >&2 "The $TOOL_MODE tool $merge_tool is not available as"\
-                        "'$merge_tool_path'"
+                        "'$merge_tool_path'"
                exit 1
        fi
        echo "$merge_tool_path"
index a10b1290bc9218942fcec54b9e930f072ca11bce..eec3a07f0fa45013e8d9fa9a3dd1a2fab98a2970 100755 (executable)
@@ -10,7 +10,7 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
 . git-sh-i18n
-set_reflog_action "pull $*"
+set_reflog_action "pull${1+ $*}"
 require_work_tree
 cd_to_toplevel
 
index 266a4c13bb3607ad635df19bf6dd80f205effae2..6759702c573f51ac00bf1b5c8abccf252cb4ff1d 100755 (executable)
@@ -22,7 +22,7 @@ currently checked out branch is used.
 
 Example:       git-rebase master~1 topic
 
-        A---B---C topic                   A'\''--B'\''--C'\'' topic
+       A---B---C topic                   A'\''--B'\''--C'\'' topic
        /                   -->           /
   D---E---F---G master          D---E---F---G master
 '
index df9d512f1a966635828cb7a8dadde3b0c2b7b9d8..e9c832bfd3da7db771cc2113027d3e590dc51d59 100644 (file)
@@ -35,7 +35,7 @@ def get_repo(alias, url):
     prefix = 'refs/testgit/%s/' % alias
     debug("prefix: '%s'", prefix)
 
-    repo.gitdir = ""
+    repo.gitdir = os.environ["GIT_DIR"]
     repo.alias = alias
     repo.prefix = prefix
 
@@ -70,9 +70,19 @@ def do_capabilities(repo, args):
 
     print "import"
     print "export"
-    print "gitdir"
     print "refspec refs/heads/*:%s*" % repo.prefix
 
+    dirname = repo.get_base_path(repo.gitdir)
+
+    if not os.path.exists(dirname):
+        os.makedirs(dirname)
+
+    path = os.path.join(dirname, 'testgit.marks')
+
+    print "*export-marks %s" % path
+    if os.path.exists(path):
+        print "*import-marks %s" % path
+
     print # end capabilities
 
 
@@ -121,8 +131,24 @@ def do_import(repo, args):
     if not repo.gitdir:
         die("Need gitdir to import")
 
+    ref = args[0]
+    refs = [ref]
+
+    while True:
+        line = sys.stdin.readline()
+        if line == '\n':
+            break
+        if not line.startswith('import '):
+            die("Expected import line.")
+
+        # strip of leading 'import '
+        ref = line[7:].strip()
+        refs.append(ref)
+
     repo = update_local_repo(repo)
-    repo.exporter.export_repo(repo.gitdir)
+    repo.exporter.export_repo(repo.gitdir, refs)
+
+    print "done"
 
 
 def do_export(repo, args):
@@ -132,32 +158,15 @@ def do_export(repo, args):
     if not repo.gitdir:
         die("Need gitdir to export")
 
-    dirname = repo.get_base_path(repo.gitdir)
-
-    if not os.path.exists(dirname):
-        os.makedirs(dirname)
-
-    path = os.path.join(dirname, 'testgit.marks')
-    print path
-    if os.path.exists(path):
-        print path
-    else:
-        print ""
-    sys.stdout.flush()
-
     update_local_repo(repo)
-    repo.importer.do_import(repo.gitdir)
-    repo.non_local.push(repo.gitdir)
-
-
-def do_gitdir(repo, args):
-    """Stores the location of the gitdir.
-    """
+    changed = repo.importer.do_import(repo.gitdir)
 
-    if not args:
-        die("gitdir needs an argument")
+    if not repo.local:
+        repo.non_local.push(repo.gitdir)
 
-    repo.gitdir = ' '.join(args)
+    for ref in changed:
+        print "ok %s" % ref
+    print
 
 
 COMMANDS = {
@@ -165,7 +174,6 @@ COMMANDS = {
     'list': do_list,
     'import': do_import,
     'export': do_export,
-    'gitdir': do_gitdir,
 }
 
 
index bc1d3fa663a5eb39dd96f6ad2ca17437073ceb0a..f46862f61b48a970f6ec24a9ae7624905ba3013a 100755 (executable)
@@ -122,12 +122,17 @@ module_clone()
        path=$1
        url=$2
        reference="$3"
+       quiet=
+       if test -n "$GIT_QUIET"
+       then
+               quiet=-q
+       fi
 
        if test -n "$reference"
        then
-               git-clone "$reference" -n "$url" "$path"
+               git-clone $quiet "$reference" -n "$url" "$path"
        else
-               git-clone -n "$url" "$path"
+               git-clone $quiet -n "$url" "$path"
        fi ||
        die "$(eval_gettext "Clone of '\$url' into submodule path '\$path' failed")"
 }
diff --git a/git.c b/git.c
index 8828c18d6cce99b8becfbb510fcc9b2b752598ca..b660e36660a1fa836a7d0f71a6443bb0b530d644 100644 (file)
--- a/git.c
+++ b/git.c
@@ -7,8 +7,8 @@
 
 const char git_usage_string[] =
        "git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-       "           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
-       "           [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
+       "           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
+       "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
        "           [-c name=value] [--help]\n"
        "           <command> [<args>]";
 
@@ -126,6 +126,20 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--namespace")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No namespace given for --namespace.\n" );
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!prefixcmp(cmd, "--namespace=")) {
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
                                fprintf(stderr, "No directory given for --work-tree.\n" );
@@ -320,7 +334,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "annotate", cmd_annotate, RUN_SETUP },
                { "apply", cmd_apply, RUN_SETUP_GENTLY },
                { "archive", cmd_archive },
-               { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
+               { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
                { "blame", cmd_blame, RUN_SETUP },
                { "branch", cmd_branch, RUN_SETUP },
                { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
index f40f9d6a29cda39ee7f989e11ef66cdf3d54d625..9ee5f96d4ce313f4f94505ff65b560943bfd21cb 100644 (file)
@@ -2,6 +2,8 @@ import os
 import subprocess
 import sys
 
+from git_remote_helpers.util import check_call
+
 
 class GitExporter(object):
     """An exporter for testgit repositories.
@@ -15,7 +17,7 @@ class GitExporter(object):
 
         self.repo = repo
 
-    def export_repo(self, base):
+    def export_repo(self, base, refs=None):
         """Exports a fast-export stream for the given directory.
 
         Simply delegates to git fast-epxort and pipes it through sed
@@ -23,8 +25,13 @@ class GitExporter(object):
         default refs/heads. This is to demonstrate how the export
         data can be stored under it's own ref (using the refspec
         capability).
+
+        If None, refs defaults to ["HEAD"].
         """
 
+        if not refs:
+            refs = ["HEAD"]
+
         dirname = self.repo.get_base_path(base)
         path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
 
@@ -42,12 +49,10 @@ class GitExporter(object):
         if os.path.exists(path):
             args.append("--import-marks=" + path)
 
-        args.append("HEAD")
+        args.extend(refs)
 
         p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
 
         args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
 
-        child = subprocess.Popen(args, stdin=p1.stdout)
-        if child.wait() != 0:
-            raise CalledProcessError
+        check_call(args, stdin=p1.stdout)
index 70a712729b63047b9760b72d3e2b0a45ff176646..5c6b595e16665bc508625ab0e96c95776bacba1a 100644 (file)
@@ -1,6 +1,8 @@
 import os
 import subprocess
 
+from git_remote_helpers.util import check_call, check_output
+
 
 class GitImporter(object):
     """An importer for testgit repositories.
@@ -14,6 +16,18 @@ class GitImporter(object):
 
         self.repo = repo
 
+    def get_refs(self, gitdir):
+        """Returns a dictionary with refs.
+        """
+        args = ["git", "--git-dir=" + gitdir, "for-each-ref", "refs/heads"]
+        lines = check_output(args).strip().split('\n')
+        refs = {}
+        for line in lines:
+            value, name = line.split(' ')
+            name = name.strip('commit\t')
+            refs[name] = value
+        return refs
+
     def do_import(self, base):
         """Imports a fast-import stream to the given directory.
 
@@ -30,11 +44,23 @@ class GitImporter(object):
         if not os.path.exists(dirname):
             os.makedirs(dirname)
 
+        refs_before = self.get_refs(gitdir)
+
         args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
 
         if os.path.exists(path):
             args.append("--import-marks=" + path)
 
-        child = subprocess.Popen(args)
-        if child.wait() != 0:
-            raise CalledProcessError
+        check_call(args)
+
+        refs_after = self.get_refs(gitdir)
+
+        changed = {}
+
+        for name, value in refs_after.iteritems():
+            if refs_before.get(name) == value:
+                continue
+
+            changed[name] = value
+
+        return changed
index f27389bb945ef423dc412c2368762439086f593e..e70025095dcfb31d3944e72ac1f83dd7d4109103 100644 (file)
@@ -1,7 +1,7 @@
 import os
 import subprocess
 
-from git_remote_helpers.util import die, warn
+from git_remote_helpers.util import check_call, die, warn
 
 
 class NonLocalGit(object):
@@ -29,9 +29,7 @@ class NonLocalGit(object):
         os.makedirs(path)
         args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
 
-        child = subprocess.Popen(args)
-        if child.wait() != 0:
-            raise CalledProcessError
+        check_call(args)
 
         return path
 
@@ -45,14 +43,10 @@ class NonLocalGit(object):
             die("could not find repo at %s", path)
 
         args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
-        child = subprocess.Popen(args)
-        if child.wait() != 0:
-            raise CalledProcessError
+        check_call(args)
 
         args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
-        child = subprocess.Popen(args)
-        if child.wait() != 0:
-            raise CalledProcessError
+        child = check_call(args)
 
     def push(self, base):
         """Pushes from the non-local repo to base.
@@ -63,7 +57,5 @@ class NonLocalGit(object):
         if not os.path.exists(path):
             die("could not find repo at %s", path)
 
-        args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath]
-        child = subprocess.Popen(args)
-        if child.wait() != 0:
-            raise CalledProcessError
+        args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath, "--all"]
+        child = check_call(args)
index 58e1cdb560fa0fe1a4745f971064e5e967408502..acbf8d7785e2253777456f8910e2352992dda474 100644 (file)
@@ -1,6 +1,9 @@
 import os
 import subprocess
 
+from git_remote_helpers.util import check_call
+
+
 def sanitize(rev, sep='\t'):
     """Converts a for-each-ref line to a name/value pair.
     """
@@ -53,9 +56,7 @@ class GitRepo(object):
         path = ".cached_revs"
         ofile = open(path, "w")
 
-        child = subprocess.Popen(args, stdout=ofile)
-        if child.wait() != 0:
-            raise CalledProcessError
+        check_call(args, stdout=ofile)
         output = open(path).readlines()
         self.revmap = dict(sanitize(i) for i in output)
         if "HEAD" in self.revmap:
index dce83e60660825ba115c503ce0776955d90c1a44..fbbb01b14619c1d2ed6bcc8f304f019fbe98697f 100644 (file)
@@ -11,6 +11,21 @@ import sys
 import os
 import subprocess
 
+try:
+    from subprocess import CalledProcessError
+except ImportError:
+    # from python2.7:subprocess.py
+    # Exception classes used by this module.
+    class CalledProcessError(Exception):
+        """This exception is raised when a process run by check_call() returns
+        a non-zero exit status.  The exit status will be stored in the
+        returncode attribute."""
+        def __init__(self, returncode, cmd):
+            self.returncode = returncode
+            self.cmd = cmd
+        def __str__(self):
+            return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
 
 # Whether or not to show debug messages
 DEBUG = False
@@ -128,6 +143,72 @@ def run_command (args, cwd = None, shell = False, add_env = None,
     return (exit_code, output, errors)
 
 
+# from python2.7:subprocess.py
+def call(*popenargs, **kwargs):
+    """Run command with arguments.  Wait for command to complete, then
+    return the returncode attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    retcode = call(["ls", "-l"])
+    """
+    return subprocess.Popen(*popenargs, **kwargs).wait()
+
+
+# from python2.7:subprocess.py
+def check_call(*popenargs, **kwargs):
+    """Run command with arguments.  Wait for command to complete.  If
+    the exit code was zero then return, otherwise raise
+    CalledProcessError.  The CalledProcessError object will have the
+    return code in the returncode attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    check_call(["ls", "-l"])
+    """
+    retcode = call(*popenargs, **kwargs)
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise CalledProcessError(retcode, cmd)
+    return 0
+
+
+# from python2.7:subprocess.py
+def check_output(*popenargs, **kwargs):
+    r"""Run command with arguments and return its output as a byte string.
+
+    If the exit code was non-zero it raises a CalledProcessError.  The
+    CalledProcessError object will have the return code in the returncode
+    attribute and output in the output attribute.
+
+    The arguments are the same as for the Popen constructor.  Example:
+
+    >>> check_output(["ls", "-l", "/dev/null"])
+    'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
+
+    The stdout argument is not allowed as it is used internally.
+    To capture standard error in the result, use stderr=STDOUT.
+
+    >>> check_output(["/bin/sh", "-c",
+    ...               "ls -l non_existent_file ; exit 0"],
+    ...              stderr=STDOUT)
+    'ls: non_existent_file: No such file or directory\n'
+    """
+    if 'stdout' in kwargs:
+        raise ValueError('stdout argument not allowed, it will be overridden.')
+    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        cmd = kwargs.get("args")
+        if cmd is None:
+            cmd = popenargs[0]
+        raise subprocess.CalledProcessError(retcode, cmd)
+    return output
+
+
 def file_reader_method (missing_ok = False):
     """Decorator for simplifying reading of files.
 
index c5236fee9dced35ab35d02eb7d42109f0107a165..f5efe7454ca42decbe7b113388bc6233e2311c1e 100644 (file)
@@ -231,7 +231,7 @@ Gitweb config file
 See also "Runtime gitweb configuration" section in README file
 for gitweb (in gitweb/README).
 
-- You can configure gitweb further using the gitweb configuration file;
+- You can configure gitweb further using the per-instance gitweb configuration file;
   by default this is a file named gitweb_config.perl in the same place as
   gitweb.cgi script. You can control the default place for the config file
   using the GITWEB_CONFIG build configuration variable, and you can set it
@@ -241,6 +241,17 @@ for gitweb (in gitweb/README).
   GITWEB_CONFIG_SYSTEM build configuration variable, and override it
   through the GITWEB_CONFIG_SYSTEM environment variable.
 
+  Note that if per-instance configuration file exists, then system-wide
+  configuration is _not used at all_.  This is quite untypical and suprising
+  behavior.  On the other hand changing current behavior would break backwards
+  compatibility and can lead to unexpected changes in gitweb behavior.
+  Therefore gitweb also looks for common system-wide configuration file,
+  normally /etc/gitweb-common.conf (set during build time using build time
+  configuration variable GITWEB_CONFIG_COMMON, set it at runtime using
+  environment variable with the same name).  Settings from per-instance or
+  system-wide configuration file override those from common system-wide
+  configuration file.
+
 - The gitweb config file is a fragment of perl code. You can set variables
   using "our $variable = value"; text from "#" character until the end
   of a line is ignored. See perlsyn(1) for details.
index 5d20515fba9251123e29be623178047d4131fc01..1c85b5fda8bc994e0ecd249e11e8f2331098bea9 100644 (file)
@@ -20,6 +20,7 @@ INSTALL ?= install
 # default configuration for gitweb
 GITWEB_CONFIG = gitweb_config.perl
 GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
+GITWEB_CONFIG_COMMON = /etc/gitweb-common.conf
 GITWEB_HOME_LINK_STR = projects
 GITWEB_SITENAME =
 GITWEB_PROJECTROOT = /pub/git
@@ -129,6 +130,7 @@ GITWEB_REPLACE = \
        -e 's|++GIT_BINDIR++|$(bindir)|g' \
        -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
        -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
+       -e 's|++GITWEB_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|g' \
        -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
        -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
        -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
index a4cfcb42cd989c063c49b6511393ba936e93978a..a9988200d6b67066b837e7298e0e1529057a4bca 100644 (file)
@@ -10,9 +10,30 @@ From the git version 1.4.0 gitweb is bundled with git.
 Runtime gitweb configuration
 ----------------------------
 
-You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
-(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
-as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
+Gitweb obtains configuration data from the following sources in the
+following order:
+
+1. built-in values (some set during build stage),
+2. common system-wide configuration file (`GITWEB_CONFIG_COMMON`,
+   defaults to '/etc/gitweb-common.conf'),
+3. either per-instance configuration file (`GITWEB_CONFIG`, defaults to
+   'gitweb_config.perl' in the same directory as the installed gitweb),
+   or if it does not exists then system-wide configuration file
+   (`GITWEB_CONFIG_SYSTEM`, defaults to '/etc/gitweb.conf').
+
+Values obtained in later configuration files override values obtained earlier
+in above sequence.
+
+You can read defaults in system-wide GITWEB_CONFIG_SYSTEM from GITWEB_CONFIG
+by adding
+
+  read_config_file($GITWEB_CONFIG_SYSTEM);
+
+at very beginning of per-instance GITWEB_CONFIG file.  In this case
+settings in said per-instance file will override settings from
+system-wide configuration file.  Note that read_config_file checks
+itself that the $GITWEB_CONFIG_SYSTEM file exists.
+
 The most notable thing that is not configurable at compile time are the
 optional features, stored in the '%features' variable.
 
index dab89f2eb013c78fa7c3b4fadba3061ef6d7f1ed..70a576a626ac67516af7b980c64ec33da6b7c19b 100755 (executable)
@@ -665,13 +665,25 @@ sub read_config_file {
        return;
 }
 
-our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM);
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
 sub evaluate_gitweb_config {
        our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
        our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+       our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
 
-       # use first config file that exists
-       read_config_file($GITWEB_CONFIG) or
+       # Protect agains duplications of file names, to not read config twice.
+       # Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
+       # there possibility of duplication of filename there doesn't matter.
+       $GITWEB_CONFIG = ""        if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
+       $GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
+
+       # Common system-wide settings for convenience.
+       # Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+       read_config_file($GITWEB_CONFIG_COMMON);
+
+       # Use first config file that exists.  This means use the per-instance
+       # GITWEB_CONFIG if exists, otherwise use GITWEB_SYSTEM_CONFIG.
+       read_config_file($GITWEB_CONFIG) and return;
        read_config_file($GITWEB_CONFIG_SYSTEM);
 }
 
@@ -2514,6 +2526,13 @@ sub git_get_project_config {
 
        # key sanity check
        return unless ($key);
+       # only subsection, if exists, is case sensitive,
+       # and not lowercased by 'git config -z -l'
+       if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+               $key = join(".", lc($hi), $mi, lc($lo));
+       } else {
+               $key = lc($key);
+       }
        $key =~ s/^gitweb\.//;
        return if ($key =~ m/\W/);
 
@@ -6465,7 +6484,7 @@ sub git_blob {
                        $nr++;
                        $line = untabify($line);
                        printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
-                              $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
+                              $nr, esc_attr(href(-replay => 1)), $nr, $nr, $syntax ? to_utf8($line) : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
diff --git a/grep.c b/grep.c
index 04e9ba4ec46b9f2002135293ede1bc5570fa73dc..26e8d8ec4cbec60cd5f4e2bef0d99607c7118fb7 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -724,7 +724,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        if (opt->file_break && opt->last_shown == 0) {
                if (opt->show_hunk_mark)
                        opt->output(opt, "\n", 1);
-       } else if (opt->pre_context || opt->post_context) {
+       } else if (opt->pre_context || opt->post_context || opt->funcbody) {
                if (opt->last_shown == 0) {
                        if (opt->show_hunk_mark) {
                                output_color(opt, "--", 2, opt->color_sep);
@@ -819,10 +819,13 @@ static void show_funcname_line(struct grep_opt *opt, const char *name,
 }
 
 static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
-                            char *bol, unsigned lno)
+                            char *bol, char *end, unsigned lno)
 {
        unsigned cur = lno, from = 1, funcname_lno = 0;
-       int funcname_needed = opt->funcname;
+       int funcname_needed = !!opt->funcname;
+
+       if (opt->funcbody && !match_funcname(opt, bol, end))
+               funcname_needed = 2;
 
        if (opt->pre_context < lno)
                from = lno - opt->pre_context;
@@ -830,7 +833,8 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
                from = opt->last_shown + 1;
 
        /* Rewind. */
-       while (bol > buf && cur > from) {
+       while (bol > buf &&
+              cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
                char *eol = --bol;
 
                while (bol > buf && bol[-1] != '\n')
@@ -942,13 +946,15 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
        int binary_match_only = 0;
        unsigned count = 0;
        int try_lookahead = 0;
+       int show_function = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
        xdemitconf_t xecfg;
 
        if (!opt->output)
                opt->output = std_output;
 
-       if (opt->pre_context || opt->post_context || opt->file_break) {
+       if (opt->pre_context || opt->post_context || opt->file_break ||
+           opt->funcbody) {
                /* Show hunk marks, except for the first file. */
                if (opt->last_shown)
                        opt->show_hunk_mark = 1;
@@ -1004,7 +1010,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                 */
                if (try_lookahead
                    && !(last_hit
-                        && lno <= last_hit + opt->post_context)
+                        && (show_function ||
+                            lno <= last_hit + opt->post_context))
                    && look_ahead(opt, &left, &lno, &bol))
                        break;
                eol = end_of_line(bol, &left);
@@ -1051,15 +1058,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                        /* Hit at this line.  If we haven't shown the
                         * pre-context lines, we would need to show them.
                         */
-                       if (opt->pre_context)
-                               show_pre_context(opt, name, buf, bol, lno);
+                       if (opt->pre_context || opt->funcbody)
+                               show_pre_context(opt, name, buf, bol, eol, lno);
                        else if (opt->funcname)
                                show_funcname_line(opt, name, buf, bol, lno);
                        show_line(opt, bol, eol, name, lno, ':');
                        last_hit = lno;
+                       if (opt->funcbody)
+                               show_function = 1;
+                       goto next_line;
                }
-               else if (last_hit &&
-                        lno <= last_hit + opt->post_context) {
+               if (show_function && match_funcname(opt, bol, eol))
+                       show_function = 0;
+               if (show_function ||
+                   (last_hit && lno <= last_hit + opt->post_context)) {
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
diff --git a/grep.h b/grep.h
index c5682973eaf696099b0d0367e21df6c0a4624836..ae50c45a4d408c1931f39b3d393ea0188bdde950 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -98,6 +98,7 @@ struct grep_opt {
        int color;
        int max_depth;
        int funcname;
+       int funcbody;
        char color_context[COLOR_MAXLEN];
        char color_filename[COLOR_MAXLEN];
        char color_function[COLOR_MAXLEN];
diff --git a/http.c b/http.c
index a1ea3db499eebc2deabf1a64e43b063cbe63ad2a..a59cac45d7a6c159e740cab9b20bb63ab91f7c48 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1121,9 +1121,8 @@ struct http_pack_request *new_http_pack_request(
        struct strbuf buf = STRBUF_INIT;
        struct http_pack_request *preq;
 
-       preq = xmalloc(sizeof(*preq));
+       preq = xcalloc(1, sizeof(*preq));
        preq->target = target;
-       preq->range_header = NULL;
 
        end_url_with_slash(&buf, base_url);
        strbuf_addf(&buf, "objects/pack/pack-%s.pack",
@@ -1215,7 +1214,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        struct curl_slist *range_header = NULL;
        struct http_object_request *freq;
 
-       freq = xmalloc(sizeof(*freq));
+       freq = xcalloc(1, sizeof(*freq));
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
 
@@ -1253,8 +1252,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
                goto abort;
        }
 
-       memset(&freq->stream, 0, sizeof(freq->stream));
-
        git_inflate_init(&freq->stream);
 
        git_SHA1_Init(&freq->c);
@@ -1329,7 +1326,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
        return freq;
 
 abort:
-       free(filename);
        free(freq->url);
        free(freq);
        return NULL;
index db9ba19ddf94fec3a5cfff450b35f5bb7b46c35e..0cc1e6fc1498e9fc51c9e382aee6a20e439a4ef0 100644 (file)
@@ -1759,6 +1759,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
                o->xdl_opts |= XDF_PATIENCE_DIFF;
+       else if (!strcmp(s, "histogram"))
+               o->xdl_opts |= XDF_HISTOGRAM_DIFF;
        else if (!strcmp(s, "ignore-space-change"))
                o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
        else if (!strcmp(s, "ignore-all-space"))
diff --git a/refs.c b/refs.c
index 3a8789d3857d17a3a0a94ba2750e9f22857b8667..6f313a9e0cdec2400a993cf7c3dfa87529a493ae 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -584,7 +584,7 @@ int read_ref(const char *ref, unsigned char *sha1)
 static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                      int flags, void *cb_data, struct ref_list *entry)
 {
-       if (strncmp(base, entry->name, trim))
+       if (prefixcmp(entry->name, base))
                return 0;
 
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
@@ -728,12 +728,12 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@ -782,6 +782,31 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
        return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
 }
 
+int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret = 0;
+       unsigned char sha1[20];
+       int flag;
+
+       strbuf_addf(&buf, "%sHEAD", get_git_namespace());
+       if (resolve_ref(buf.buf, sha1, 1, &flag))
+               ret = fn(buf.buf, sha1, flag, cb_data);
+       strbuf_release(&buf);
+
+       return ret;
+}
+
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+       strbuf_addf(&buf, "%srefs/", get_git_namespace());
+       ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+       strbuf_release(&buf);
+       return ret;
+}
+
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
 {
@@ -819,7 +844,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "refs/", fn, 0,
+       return do_for_each_ref(NULL, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
diff --git a/refs.h b/refs.h
index 5de06e57e7a9644a7dd51832552e9d1afa53c8cd..dfb086e93346aa3fb6f600b2ed07b5736716bf9c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -36,6 +36,9 @@ extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, voi
 extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
 extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
 
+extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
        return strpbrk(pattern, "?*[");
index 69831e931af1fe2a3e668239e8c4bb73a7bfb936..5798aa57b6bf7f47f4fd9c8b943602faaeee4b95 100644 (file)
@@ -762,7 +762,9 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
                argv[argc++] = "--thin";
        if (options.dry_run)
                argv[argc++] = "--dry-run";
-       if (options.verbosity > 1)
+       if (options.verbosity < 0)
+               argv[argc++] = "--quiet";
+       else if (options.verbosity > 1)
                argv[argc++] = "--verbose";
        argv[argc++] = url;
        for (i = 0; i < nr_spec; i++)
@@ -855,7 +857,14 @@ int main(int argc, const char **argv)
        http_init(remote);
 
        do {
-               if (strbuf_getline(&buf, stdin, '\n') == EOF)
+               if (strbuf_getline(&buf, stdin, '\n') == EOF) {
+                       if (ferror(stdin))
+                               fprintf(stderr, "Error reading command stream\n");
+                       else
+                               fprintf(stderr, "Unexpected end of command stream\n");
+                       return 1;
+               }
+               if (buf.len == 0)
                        break;
                if (!prefixcmp(buf.buf, "fetch ")) {
                        if (nongit)
@@ -895,6 +904,7 @@ int main(int argc, const char **argv)
                        printf("\n");
                        fflush(stdout);
                } else {
+                       fprintf(stderr, "Unknown command '%s'\n", buf.buf);
                        return 1;
                }
                strbuf_reset(&buf);
index ca42a126ad04514f0ed8378768ebce98cfc5a659..b8ecfa5d9558e3824872ad6778fc344681cc8d5c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -896,7 +896,7 @@ struct ref *alloc_ref(const char *name)
        return alloc_ref_with_prefix("", 0, name);
 }
 
-static struct ref *copy_ref(const struct ref *ref)
+struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *cpy;
        size_t len;
@@ -1667,7 +1667,9 @@ struct ref *guess_remote_head(const struct ref *head,
 
        /* Look for another ref that points there */
        for (r = refs; r; r = r->next) {
-               if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) {
+               if (r != head &&
+                   !prefixcmp(r->name, "refs/heads/") &&
+                   !hashcmp(r->old_sha1, head->old_sha1)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
                        if (!all)
index 888d7c15de2eacc56d869a96d4463aefca7a7a06..9a30a9dba64825950f7a0448ad8b64581c0b8fae 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -70,7 +70,7 @@ struct refspec {
 extern const struct refspec *tag_refspec;
 
 struct ref *alloc_ref(const char *name);
-
+struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 
 int check_ref_type(const struct ref *ref, int flags);
index 70e8a249d0fe07b6a32b4da4ac8224d1c2f06b1b..a2796c4caecc0fae6ea1a95b2c965b92d98f8a31 100644 (file)
@@ -77,16 +77,14 @@ static void notify_parent(void)
 
 static NORETURN void die_child(const char *err, va_list params)
 {
-       char msg[4096];
-       int len = vsnprintf(msg, sizeof(msg), err, params);
-       if (len > sizeof(msg))
-               len = sizeof(msg);
-
-       write_in_full(child_err, "fatal: ", 7);
-       write_in_full(child_err, msg, len);
-       write_in_full(child_err, "\n", 1);
+       vwritef(child_err, "fatal: ", err, params);
        exit(128);
 }
+
+static void error_child(const char *err, va_list params)
+{
+       vwritef(child_err, "error: ", err, params);
+}
 #endif
 
 static inline void set_cloexec(int fd)
@@ -127,9 +125,6 @@ static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
                if (code == 127) {
                        code = -1;
                        failed_errno = ENOENT;
-                       if (!silent_exec_failure)
-                               error("cannot run %s: %s", argv0,
-                                       strerror(ENOENT));
                }
        } else {
                error("waitpid is confused (%s)", argv0);
@@ -217,6 +212,7 @@ fail_pipe:
                        set_cloexec(child_err);
                }
                set_die_routine(die_child);
+               set_error_routine(error_child);
 
                close(notify_pipe[0]);
                set_cloexec(notify_pipe[1]);
@@ -283,14 +279,14 @@ fail_pipe:
                } else {
                        execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
-               /*
-                * Do not check for cmd->silent_exec_failure; the parent
-                * process will check it when it sees this exit code.
-                */
-               if (errno == ENOENT)
+               if (errno == ENOENT) {
+                       if (!cmd->silent_exec_failure)
+                               error("cannot run %s: %s", cmd->argv[0],
+                                       strerror(ENOENT));
                        exit(127);
-               else
+               } else {
                        die_errno("cannot exec '%s'", cmd->argv[0]);
+               }
        }
        if (cmd->pid < 0)
                error("cannot fork() for %s: %s", cmd->argv[0],
diff --git a/setup.c b/setup.c
index 5ea5502e4881a806ebb3aa9fd38f146cf9f65cf9..2c51a9a4c7665ce786e58e312c55f9f8b4bca6ae 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -264,6 +264,38 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
        return pathspec;
 }
 
+const char *pathspec_prefix(const char *prefix, const char **pathspec)
+{
+       const char **p, *n, *prev;
+       unsigned long max;
+
+       if (!pathspec)
+               return prefix ? xmemdupz(prefix, strlen(prefix)) : NULL;
+
+       prev = NULL;
+       max = PATH_MAX;
+       for (p = pathspec; (n = *p) != NULL; p++) {
+               int i, len = 0;
+               for (i = 0; i < max; i++) {
+                       char c = n[i];
+                       if (prev && prev[i] != c)
+                               break;
+                       if (!c || c == '*' || c == '?')
+                               break;
+                       if (c == '/')
+                               len = i+1;
+               }
+               prev = n;
+               if (len < max) {
+                       max = len;
+                       if (!max)
+                               break;
+               }
+       }
+
+       return max ? xmemdupz(prev, max) : NULL;
+}
+
 /*
  * Test if it looks like we're at a git directory.
  * We want to see:
index 89d7e5eb57ea80a7bca3f361530903032fbe2b03..d5616dca0809bdb9fd6a7a1980b92ded9ae6e230 100644 (file)
@@ -1797,6 +1797,24 @@ static void *unpack_delta_entry(struct packed_git *p,
        return result;
 }
 
+static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
+{
+       static FILE *log_file;
+
+       if (!log_file) {
+               log_file = fopen(log_pack_access, "w");
+               if (!log_file) {
+                       error("cannot open pack access log '%s' for writing: %s",
+                             log_pack_access, strerror(errno));
+                       log_pack_access = NULL;
+                       return;
+               }
+       }
+       fprintf(log_file, "%s %"PRIuMAX"\n",
+               p->pack_name, (uintmax_t)obj_offset);
+       fflush(log_file);
+}
+
 int do_check_packed_object_crc;
 
 void *unpack_entry(struct packed_git *p, off_t obj_offset,
@@ -1806,6 +1824,9 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        off_t curpos = obj_offset;
        void *data;
 
+       if (log_pack_access)
+               write_pack_access_log(p, obj_offset);
+
        if (do_check_packed_object_crc && p->index_version > 1) {
                struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
                unsigned long len = revidx[1].offset - obj_offset;
index 25c9a209bb1ba9cec8bb49623ae45fc19fdca2c7..71072e1b1da670cdb4b048a3a6e83a4ae806bf5f 100644 (file)
@@ -94,7 +94,9 @@ struct git_istream {
 
 int close_istream(struct git_istream *st)
 {
-       return st->vtbl->close(st);
+       int r = st->vtbl->close(st);
+       free(st);
+       return r;
 }
 
 ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
new file mode 100644 (file)
index 0000000..75ffd91
--- /dev/null
@@ -0,0 +1,165 @@
+#!/bin/sh
+
+test_diff_frobnitz() {
+       cat >file1 <<\EOF
+#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("Your answer is: ");
+        printf("%d\n", foo);
+    }
+}
+
+int fact(int n)
+{
+    if(n > 1)
+    {
+        return fact(n-1) * n;
+    }
+    return 1;
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fact(10));
+}
+EOF
+
+       cat >file2 <<\EOF
+#include <stdio.h>
+
+int fib(int n)
+{
+    if(n > 2)
+    {
+        return fib(n-1) + fib(n-2);
+    }
+    return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+    int i;
+    for(i = 0; i < 10; i++)
+    {
+        printf("%d\n", foo);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    frobnitz(fib(10));
+}
+EOF
+
+       cat >expect <<\EOF
+diff --git a/file1 b/file2
+index 6faa5a3..e3af329 100644
+--- a/file1
++++ b/file2
+@@ -1,26 +1,25 @@
+ #include <stdio.h>
++int fib(int n)
++{
++    if(n > 2)
++    {
++        return fib(n-1) + fib(n-2);
++    }
++    return 1;
++}
++
+ // Frobs foo heartily
+ int frobnitz(int foo)
+ {
+     int i;
+     for(i = 0; i < 10; i++)
+     {
+-        printf("Your answer is: ");
+         printf("%d\n", foo);
+     }
+ }
+-int fact(int n)
+-{
+-    if(n > 1)
+-    {
+-        return fact(n-1) * n;
+-    }
+-    return 1;
+-}
+-
+ int main(int argc, char **argv)
+ {
+-    frobnitz(fact(10));
++    frobnitz(fib(10));
+ }
+EOF
+
+       STRATEGY=$1
+
+       test_expect_success "$STRATEGY diff" '
+               test_must_fail git diff --no-index "--$STRATEGY" file1 file2 > output &&
+               test_cmp expect output
+       '
+
+       test_expect_success "$STRATEGY diff output is valid" '
+               mv file2 expect &&
+               git apply < output &&
+               test_cmp expect file2
+       '
+}
+
+test_diff_unique() {
+       cat >uniq1 <<\EOF
+1
+2
+3
+4
+5
+6
+EOF
+
+       cat >uniq2 <<\EOF
+a
+b
+c
+d
+e
+f
+EOF
+
+       cat >expect <<\EOF
+diff --git a/uniq1 b/uniq2
+index b414108..0fdf397 100644
+--- a/uniq1
++++ b/uniq2
+@@ -1,6 +1,6 @@
+-1
+-2
+-3
+-4
+-5
+-6
++a
++b
++c
++d
++e
++f
+EOF
+
+       STRATEGY=$1
+
+       test_expect_success 'completely different files' '
+               test_must_fail git diff --no-index "--$STRATEGY" uniq1 uniq2 > output &&
+               test_cmp expect output
+       '
+}
+
index 7f519e5ebeac0780aee1c6d9c4458e00b62a273d..647d888507a4b74b82ae4016c2f30f7d171e98ca 100755 (executable)
@@ -21,10 +21,10 @@ test_expect_success 'setup reflog with alternating commits' '
 
 test_expect_success 'reflog shows all entries' '
        cat >expect <<-\EOF
-               topic@{0} two: updating HEAD
-               topic@{1} one: updating HEAD
-               topic@{2} two: updating HEAD
-               topic@{3} one: updating HEAD
+               topic@{0} reset: moving to two
+               topic@{1} reset: moving to one
+               topic@{2} reset: moving to two
+               topic@{3} reset: moving to one
                topic@{4} branch: Created from HEAD
        EOF
        git log -g --format="%gd %gs" topic >actual &&
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
new file mode 100755 (executable)
index 0000000..09dcf04
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='
+Miscellaneous tests for git ls-tree.
+
+             1. git ls-tree fails in presence of tree damage.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir a &&
+       touch a/one &&
+       git add a/one &&
+       git commit -m test
+'
+
+test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
+       rm -f .git/objects/5f/cffbd6e4c5c5b8d81f5e9314b20e338e3ffff5 &&
+       test_must_fail git ls-tree -r HEAD
+'
+
+test_done
index 2ea3be6546f43fd5c224aa8df6add96fa9df6ac2..1aa366a410e9a3e2ad4c2fa84431198fbb553a5f 100755 (executable)
@@ -26,13 +26,13 @@ test_expect_success 'example 1: notes to add an Acked-by line' '
 '
 
 test_expect_success 'example 2: binary notes' '
-       cp "$TEST_DIRECTORY"/test4012.png . &&
+       cp "$TEST_DIRECTORY"/test-binary-1.png . &&
        git checkout B &&
-       blob=$(git hash-object -w test4012.png) &&
+       blob=$(git hash-object -w test-binary-1.png) &&
        git notes --ref=logo add -C "$blob" &&
        git notes --ref=logo copy B C &&
        git notes --ref=logo show C >actual &&
-       test_cmp test4012.png actual
+       test_cmp test-binary-1.png actual
 '
 
 test_done
index 05ec0628322ccc103343515feb70d27a9a0e6c76..2d9f9a0cf1555cab24af19f264d495e134c27352 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'prepare repository' \
        'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
         git update-index --add a b c d &&
         echo git >a &&
-        cat "$TEST_DIRECTORY"/test4012.png >b &&
+        cat "$TEST_DIRECTORY"/test-binary-1.png >b &&
         echo git >c &&
         cat b b >d'
 
index 1eb14989df0bffe7d41111607d67da6a90c1fbf5..3c9932edf3f3dd44eaef98c0509a6cbe4b0091f5 100755 (executable)
 test_description='patience diff algorithm'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
 
-cat >file1 <<\EOF
-#include <stdio.h>
+test_diff_frobnitz "patience"
 
-// Frobs foo heartily
-int frobnitz(int foo)
-{
-    int i;
-    for(i = 0; i < 10; i++)
-    {
-        printf("Your answer is: ");
-        printf("%d\n", foo);
-    }
-}
-
-int fact(int n)
-{
-    if(n > 1)
-    {
-        return fact(n-1) * n;
-    }
-    return 1;
-}
-
-int main(int argc, char **argv)
-{
-    frobnitz(fact(10));
-}
-EOF
-
-cat >file2 <<\EOF
-#include <stdio.h>
-
-int fib(int n)
-{
-    if(n > 2)
-    {
-        return fib(n-1) + fib(n-2);
-    }
-    return 1;
-}
-
-// Frobs foo heartily
-int frobnitz(int foo)
-{
-    int i;
-    for(i = 0; i < 10; i++)
-    {
-        printf("%d\n", foo);
-    }
-}
-
-int main(int argc, char **argv)
-{
-    frobnitz(fib(10));
-}
-EOF
-
-cat >expect <<\EOF
-diff --git a/file1 b/file2
-index 6faa5a3..e3af329 100644
---- a/file1
-+++ b/file2
-@@ -1,26 +1,25 @@
- #include <stdio.h>
-+int fib(int n)
-+{
-+    if(n > 2)
-+    {
-+        return fib(n-1) + fib(n-2);
-+    }
-+    return 1;
-+}
-+
- // Frobs foo heartily
- int frobnitz(int foo)
- {
-     int i;
-     for(i = 0; i < 10; i++)
-     {
--        printf("Your answer is: ");
-         printf("%d\n", foo);
-     }
- }
--int fact(int n)
--{
--    if(n > 1)
--    {
--        return fact(n-1) * n;
--    }
--    return 1;
--}
--
- int main(int argc, char **argv)
- {
--    frobnitz(fact(10));
-+    frobnitz(fib(10));
- }
-EOF
-
-test_expect_success 'patience diff' '
-
-       test_must_fail git diff --no-index --patience file1 file2 > output &&
-       test_cmp expect output
-
-'
-
-test_expect_success 'patience diff output is valid' '
-
-       mv file2 expect &&
-       git apply < output &&
-       test_cmp expect file2
-
-'
-
-cat >uniq1 <<\EOF
-1
-2
-3
-4
-5
-6
-EOF
-
-cat >uniq2 <<\EOF
-a
-b
-c
-d
-e
-f
-EOF
-
-cat >expect <<\EOF
-diff --git a/uniq1 b/uniq2
-index b414108..0fdf397 100644
---- a/uniq1
-+++ b/uniq2
-@@ -1,6 +1,6 @@
--1
--2
--3
--4
--5
--6
-+a
-+b
-+c
-+d
-+e
-+f
-EOF
-
-test_expect_success 'completely different files' '
-
-       test_must_fail git diff --no-index --patience uniq1 uniq2 > output &&
-       test_cmp expect output
-
-'
+test_diff_unique "patience"
 
 test_done
diff --git a/t/t4050-diff-histogram.sh b/t/t4050-diff-histogram.sh
new file mode 100755 (executable)
index 0000000..fd3e86a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='histogram diff algorithm'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-alternative.sh
+
+test_diff_frobnitz "histogram"
+
+test_diff_unique "histogram"
+
+test_done
index 9e3ba98fc8d6f0242af38c3f7dc062e6c6ab9604..d9068981f8475c37d30e584bc6b795075a2f3063 100755 (executable)
@@ -266,7 +266,7 @@ test_expect_success 'archive --list mentions user filter' '
        grep "^bar\$" output
 '
 
-test_expect_success 'archive --list shows only enabled remote filters' '
+test_expect_success NOT_MINGW 'archive --list shows only enabled remote filters' '
        git archive --list --remote=. >output &&
        ! grep "^tar\.foo\$" output &&
        grep "^bar\$" output
@@ -298,7 +298,7 @@ test_expect_success 'extension matching requires dot' '
        test_cmp b.tar config-implicittar.foo
 '
 
-test_expect_success 'only enabled filters are available remotely' '
+test_expect_success NOT_MINGW 'only enabled filters are available remotely' '
        test_must_fail git archive --remote=. --format=tar.foo HEAD \
                >remote.tar.foo &&
        git archive --remote=. --format=bar >remote.bar HEAD &&
@@ -341,12 +341,12 @@ test_expect_success GZIP,GUNZIP 'extract tgz file' '
        test_cmp b.tar j.tar
 '
 
-test_expect_success GZIP 'remote tar.gz is allowed by default' '
+test_expect_success GZIP,NOT_MINGW 'remote tar.gz is allowed by default' '
        git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
        test_cmp j.tgz remote.tar.gz
 '
 
-test_expect_success GZIP 'remote tar.gz can be disabled' '
+test_expect_success GZIP,NOT_MINGW 'remote tar.gz can be disabled' '
        git config tar.tar.gz.remote false &&
        test_must_fail git archive --remote=. --format=tar.gz HEAD \
                >remote.tar.gz
diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
new file mode 100755 (executable)
index 0000000..cc0b31f
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='fetch/push involving ref namespaces'
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_tick &&
+       git init original &&
+       (
+               cd original &&
+               echo 0 >count &&
+               git add count &&
+               test_commit 0 &&
+               echo 1 >count &&
+               git add count &&
+               test_commit 1 &&
+               git remote add pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+               git remote add pushee-unnamespaced ../pushee
+       ) &&
+       commit0=$(cd original && git rev-parse HEAD^) &&
+       commit1=$(cd original && git rev-parse HEAD) &&
+       git init pushee &&
+       git init puller
+'
+
+test_expect_success 'pushing into a repository using a ref namespace' '
+       (
+               cd original &&
+               git push pushee-namespaced master &&
+               git ls-remote pushee-namespaced >actual &&
+               printf "$commit1\trefs/heads/master\n" >expected &&
+               test_cmp expected actual &&
+               git push pushee-namespaced --tags &&
+               git ls-remote pushee-namespaced >actual &&
+               printf "$commit0\trefs/tags/0\n" >>expected &&
+               printf "$commit1\trefs/tags/1\n" >>expected &&
+               test_cmp expected actual &&
+               # Verify that the GIT_NAMESPACE environment variable works as well
+               GIT_NAMESPACE=namespace git ls-remote "ext::git %s ../pushee" >actual &&
+               test_cmp expected actual &&
+               # Verify that --namespace overrides GIT_NAMESPACE
+               GIT_NAMESPACE=garbage git ls-remote pushee-namespaced >actual &&
+               test_cmp expected actual &&
+               # Try a namespace with no content
+               git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual &&
+               test_cmp /dev/null actual &&
+               git ls-remote pushee-unnamespaced >actual &&
+               sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced &&
+               test_cmp expected.unnamespaced actual
+       )
+'
+
+test_expect_success 'pulling from a repository using a ref namespace' '
+       (
+               cd puller &&
+               git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
+               git for-each-ref refs/ >actual &&
+               printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected &&
+               printf "$commit0 commit\trefs/tags/0\n" >>expected &&
+               printf "$commit1 commit\trefs/tags/1\n" >>expected &&
+               test_cmp expected actual
+       )
+'
+
+# This test with clone --mirror checks for possible regressions in clone
+# or the machinery underneath it. It ensures that no future change
+# causes clone to ignore refs in refs/namespaces/*. In particular, it
+# protects against a regression caused by any future change to the refs
+# machinery that might cause it to ignore refs outside of refs/heads/*
+# or refs/tags/*. More generally, this test also checks the high-level
+# functionality of using clone --mirror to back up a set of repos hosted
+# in the namespaces of a single repo.
+test_expect_success 'mirroring a repository using a ref namespace' '
+       git clone --mirror pushee mirror &&
+       (
+               cd mirror &&
+               git for-each-ref refs/ >actual &&
+               printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected &&
+               printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected &&
+               printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected &&
+               test_cmp expected actual
+       )
+'
+
+test_done
diff --git a/t/t5707-clone-detached.sh b/t/t5707-clone-detached.sh
new file mode 100755 (executable)
index 0000000..8b0d607
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='test cloning a repository with detached HEAD'
+. ./test-lib.sh
+
+head_is_detached() {
+       git --git-dir=$1/.git rev-parse --verify HEAD &&
+       test_must_fail git --git-dir=$1/.git symbolic-ref HEAD
+}
+
+test_expect_success 'setup' '
+       echo one >file &&
+       git add file &&
+       git commit -m one &&
+       echo two >file &&
+       git commit -a -m two &&
+       git tag two &&
+       echo three >file &&
+       git commit -a -m three
+'
+
+test_expect_success 'clone repo (detached HEAD points to branch)' '
+       git checkout master^0 &&
+       git clone "file://$PWD" detached-branch
+'
+test_expect_success 'cloned HEAD matches' '
+       echo three >expect &&
+       git --git-dir=detached-branch/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_failure 'cloned HEAD is detached' '
+       head_is_detached detached-branch
+'
+
+test_expect_success 'clone repo (detached HEAD points to tag)' '
+       git checkout two^0 &&
+       git clone "file://$PWD" detached-tag
+'
+test_expect_success 'cloned HEAD matches' '
+       echo two >expect &&
+       git --git-dir=detached-tag/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-tag
+'
+
+test_expect_success 'clone repo (detached HEAD points to history)' '
+       git checkout two^ &&
+       git clone "file://$PWD" detached-history
+'
+test_expect_success 'cloned HEAD matches' '
+       echo one >expect &&
+       git --git-dir=detached-history/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-history
+'
+
+test_expect_success 'clone repo (orphan detached HEAD)' '
+       git checkout master^0 &&
+       echo four >file &&
+       git commit -a -m four &&
+       git clone "file://$PWD" detached-orphan
+'
+test_expect_success 'cloned HEAD matches' '
+       echo four >expect &&
+       git --git-dir=detached-orphan/.git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+test_expect_success 'cloned HEAD is detached' '
+       head_is_detached detached-orphan
+'
+
+test_done
index 1fb6380fceb294f1a7cc73359f5e39740260d8f0..1c62001fce76a73cc951dd18bcf6cb6f650f69e8 100755 (executable)
@@ -7,17 +7,27 @@ test_description='Test remote-helper import and export commands'
 
 . ./test-lib.sh
 
-if test_have_prereq PYTHON && "$PYTHON_PATH" -c '
+if ! test_have_prereq PYTHON ; then
+       skip_all='skipping git-remote-hg tests, python not available'
+       test_done
+fi
+
+"$PYTHON_PATH" -c '
 import sys
 if sys.hexversion < 0x02040000:
     sys.exit(1)
-'
-then
-    # Requires Python 2.4 or newer
-       test_set_prereq PYTHON_24
-fi
+' || {
+       skip_all='skipping git-remote-hg tests, python version < 2.4'
+       test_done
+}
+
+compare_refs() {
+       git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
+       git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
+       test_cmp expect actual
+}
 
-test_expect_success PYTHON_24 'setup repository' '
+test_expect_success 'setup repository' '
        git init --bare server/.git &&
        git clone server public &&
        (cd public &&
@@ -27,54 +37,99 @@ test_expect_success PYTHON_24 'setup repository' '
         git push origin master)
 '
 
-test_expect_success PYTHON_24 'cloning from local repo' '
+test_expect_success 'cloning from local repo' '
        git clone "testgit::${PWD}/server" localclone &&
        test_cmp public/file localclone/file
 '
 
-test_expect_success PYTHON_24 'cloning from remote repo' '
+test_expect_success 'cloning from remote repo' '
        git clone "testgit::file://${PWD}/server" clone &&
        test_cmp public/file clone/file
 '
 
-test_expect_success PYTHON_24 'create new commit on remote' '
+test_expect_success 'create new commit on remote' '
        (cd public &&
         echo content >>file &&
         git commit -a -m two &&
         git push)
 '
 
-test_expect_success PYTHON_24 'pulling from local repo' '
+test_expect_success 'pulling from local repo' '
        (cd localclone && git pull) &&
        test_cmp public/file localclone/file
 '
 
-test_expect_success PYTHON_24 'pulling from remote remote' '
+test_expect_success 'pulling from remote remote' '
        (cd clone && git pull) &&
        test_cmp public/file clone/file
 '
 
-test_expect_success PYTHON_24 'pushing to local repo' '
+test_expect_success 'pushing to local repo' '
        (cd localclone &&
        echo content >>file &&
        git commit -a -m three &&
        git push) &&
-       HEAD=$(git --git-dir=localclone/.git rev-parse --verify HEAD) &&
-       test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+       compare_refs localclone HEAD server HEAD
 '
 
-test_expect_success PYTHON_24 'synch with changes from localclone' '
+test_expect_success 'synch with changes from localclone' '
        (cd clone &&
         git pull)
 '
 
-test_expect_success PYTHON_24 'pushing remote local repo' '
+test_expect_success 'pushing remote local repo' '
        (cd clone &&
        echo content >>file &&
        git commit -a -m four &&
        git push) &&
-       HEAD=$(git --git-dir=clone/.git rev-parse --verify HEAD) &&
-       test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+       compare_refs clone HEAD server HEAD
+'
+
+test_expect_success 'fetch new branch' '
+       (cd public &&
+        git checkout -b new &&
+        echo content >>file &&
+        git commit -a -m five &&
+        git push origin new
+       ) &&
+       (cd localclone &&
+        git fetch origin new
+       ) &&
+       compare_refs public HEAD localclone FETCH_HEAD
+'
+
+test_expect_success 'fetch multiple branches' '
+       (cd localclone &&
+        git fetch
+       ) &&
+       compare_refs server master localclone refs/remotes/origin/master &&
+       compare_refs server new localclone refs/remotes/origin/new
+'
+
+test_expect_success 'push when remote has extra refs' '
+       (cd clone &&
+        echo content >>file &&
+        git commit -a -m six &&
+        git push
+       ) &&
+       compare_refs clone master server master
+'
+
+test_expect_success 'push new branch by name' '
+       (cd clone &&
+        git checkout -b new-name  &&
+        echo content >>file &&
+        git commit -a -m seven &&
+        git push origin new-name
+       ) &&
+       compare_refs clone HEAD server refs/heads/new-name
+'
+
+test_expect_failure 'push new branch with old:new refspec' '
+       (cd clone &&
+        git push origin new-name:new-refspec
+       ) &&
+       compare_refs clone HEAD server refs/heads/new-refspec
 '
 
 test_done
index d9f343942c7eb9d1e80e98db2252cb29135544fa..432f086c063198e82ad8a2f7c8dd175a8685fdf5 100755 (executable)
@@ -154,7 +154,7 @@ test_expect_success "expected conflict markers" "test_cmp expect out"
 
 test_expect_success 'binary files cannot be merged' '
        test_must_fail git merge-file -p \
-               orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
+               orig.txt "$TEST_DIRECTORY"/test-binary-1.png new1.txt 2> merge.err &&
        grep "Cannot merge binary files" merge.err
 '
 
index b519626ca09255a433b0d1cb32780a6feafa09f6..07735410b9536ba7639134c7ca0cda4f486f8291 100755 (executable)
@@ -6,7 +6,7 @@ test_description='ask merge-recursive to merge binary files'
 
 test_expect_success setup '
 
-       cat "$TEST_DIRECTORY"/test4012.png >m &&
+       cat "$TEST_DIRECTORY"/test-binary-1.png >m &&
        git add m &&
        git ls-files -s | sed -e "s/ 0  / 1     /" >E1 &&
        test_tick &&
index b5063b6fe6c37f4b41a89b71c6d52ac0b5c07127..62125eca8163f1ca52d57959fb872340479b9002 100755 (executable)
@@ -126,6 +126,18 @@ test_expect_success 'bisect reset removes packed refs' '
        test -z "$(git for-each-ref "refs/heads/bisect")"
 '
 
+test_expect_success 'bisect reset removes bisect state after --no-checkout' '
+       git bisect reset &&
+       git bisect start --no-checkout &&
+       git bisect good $HASH1 &&
+       git bisect bad $HASH3 &&
+       git bisect next &&
+       git bisect reset &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       test -z "$(git for-each-ref "refs/heads/bisect")" &&
+       test -z "$(git for-each-ref "BISECT_HEAD")"
+'
+
 test_expect_success 'bisect start: back in good branch' '
        git branch > branch.output &&
        grep "* other" branch.output > /dev/null &&
@@ -138,15 +150,23 @@ test_expect_success 'bisect start: back in good branch' '
        grep "* other" branch.output > /dev/null
 '
 
-test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
-       git bisect start $HASH4 $HASH1 -- &&
-       git bisect good &&
+test_expect_success 'bisect start: no ".git/BISECT_START" created if junk rev' '
+       git bisect reset &&
        test_must_fail git bisect start $HASH4 foo -- &&
        git branch > branch.output &&
        grep "* other" branch.output > /dev/null &&
        test_must_fail test -e .git/BISECT_START
 '
 
+test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if junk rev' '
+       git bisect start $HASH4 $HASH1 -- &&
+       git bisect good &&
+       cp .git/BISECT_START saved &&
+       test_must_fail git bisect start $HASH4 foo -- &&
+       git branch > branch.output &&
+       grep "* (no branch)" branch.output > /dev/null &&
+       test_cmp saved .git/BISECT_START
+'
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
        git bisect start $HASH4 $HASH1 -- &&
        git bisect good &&
@@ -572,6 +592,155 @@ test_expect_success 'erroring out when using bad path parameters' '
        grep "bad path parameters" error.txt
 '
 
+test_expect_success 'test bisection on bare repo - --no-checkout specified' '
+       git clone --bare . bare.nocheckout &&
+       (
+               cd bare.nocheckout &&
+               git bisect start --no-checkout &&
+               git bisect good $HASH1 &&
+               git bisect bad $HASH4 &&
+               git bisect run eval \
+                       "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+                       >../nocheckout.log &&
+               git bisect reset
+       ) &&
+       grep "$HASH3 is the first bad commit" nocheckout.log
+'
+
+
+test_expect_success 'test bisection on bare repo - --no-checkout defaulted' '
+       git clone --bare . bare.defaulted &&
+       (
+               cd bare.defaulted &&
+               git bisect start &&
+               git bisect good $HASH1 &&
+               git bisect bad $HASH4 &&
+               git bisect run eval \
+                       "test \$(git rev-list BISECT_HEAD ^$HASH2 --max-count=1 | wc -l) = 0" \
+                       >../defaulted.log &&
+               git bisect reset
+       ) &&
+       grep "$HASH3 is the first bad commit" defaulted.log
+'
+
 #
+# This creates a broken branch which cannot be checked out because
+# the tree created has been deleted.
 #
+# H1-H2-H3-H4-H5-H6-H7  <--other
+#            \
+#             S5-S6'-S7'-S8'-S9  <--broken
+#
+# Commits marked with ' have a missing tree.
+#
+test_expect_success 'broken branch creation' '
+       git bisect reset &&
+       git checkout -b broken $HASH4 &&
+       git tag BROKEN_HASH4 $HASH4 &&
+       add_line_into_file "5(broken): first line on a broken branch" hello2 &&
+       git tag BROKEN_HASH5 &&
+       mkdir missing &&
+       :> missing/MISSING &&
+       git add missing/MISSING &&
+       git commit -m "6(broken): Added file that will be deleted"
+       git tag BROKEN_HASH6 &&
+       add_line_into_file "7(broken): second line on a broken branch" hello2 &&
+       git tag BROKEN_HASH7 &&
+       add_line_into_file "8(broken): third line on a broken branch" hello2 &&
+       git tag BROKEN_HASH8 &&
+       git rm missing/MISSING &&
+       git commit -m "9(broken): Remove missing file"
+       git tag BROKEN_HASH9 &&
+       rm .git/objects/39/f7e61a724187ab767d2e08442d9b6b9dab587d
+'
+
+echo "" > expected.ok
+cat > expected.missing-tree.default <<EOF
+fatal: unable to read tree 39f7e61a724187ab767d2e08442d9b6b9dab587d
+EOF
+
+test_expect_success 'bisect fails if tree is broken on start commit' '
+       git bisect reset &&
+       test_must_fail git bisect start BROKEN_HASH7 BROKEN_HASH4 2>error.txt &&
+       test_cmp expected.missing-tree.default error.txt
+'
+
+test_expect_success 'bisect fails if tree is broken on trial commit' '
+       git bisect reset &&
+       test_must_fail git bisect start BROKEN_HASH9 BROKEN_HASH4 2>error.txt &&
+       git reset --hard broken &&
+       git checkout broken &&
+       test_cmp expected.missing-tree.default error.txt
+'
+
+check_same()
+{
+       echo "Checking $1 is the same as $2" &&
+       git rev-parse "$1" > expected.same &&
+       git rev-parse "$2" > expected.actual &&
+       test_cmp expected.same expected.actual
+}
+
+test_expect_success 'bisect: --no-checkout - start commit bad' '
+       git bisect reset &&
+       git bisect start BROKEN_HASH7 BROKEN_HASH4 --no-checkout &&
+       check_same BROKEN_HASH6 BISECT_HEAD &&
+       git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - trial commit bad' '
+       git bisect reset &&
+       git bisect start broken BROKEN_HASH4 --no-checkout &&
+       check_same BROKEN_HASH6 BISECT_HEAD &&
+       git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target before breakage' '
+       git bisect reset &&
+       git bisect start broken BROKEN_HASH4 --no-checkout &&
+       check_same BROKEN_HASH6 BISECT_HEAD &&
+       git bisect bad BISECT_HEAD &&
+       check_same BROKEN_HASH5 BISECT_HEAD &&
+       git bisect bad BISECT_HEAD &&
+       check_same BROKEN_HASH5 bisect/bad &&
+       git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target in breakage' '
+       git bisect reset &&
+       git bisect start broken BROKEN_HASH4 --no-checkout &&
+       check_same BROKEN_HASH6 BISECT_HEAD &&
+       git bisect bad BISECT_HEAD &&
+       check_same BROKEN_HASH5 BISECT_HEAD &&
+       git bisect good BISECT_HEAD &&
+       check_same BROKEN_HASH6 bisect/bad &&
+       git bisect reset
+'
+
+test_expect_success 'bisect: --no-checkout - target after breakage' '
+       git bisect reset &&
+       git bisect start broken BROKEN_HASH4 --no-checkout &&
+       check_same BROKEN_HASH6 BISECT_HEAD &&
+       git bisect good BISECT_HEAD &&
+       check_same BROKEN_HASH8 BISECT_HEAD &&
+       git bisect good BISECT_HEAD &&
+       check_same BROKEN_HASH9 bisect/bad &&
+       git bisect reset
+'
+
+test_expect_success 'bisect: demonstrate identification of damage boundary' "
+       git bisect reset &&
+       git checkout broken &&
+       git bisect start broken master --no-checkout &&
+       git bisect run sh -c '
+               GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
+               git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
+               git pack-objects --stdout >/dev/null < tmp.\$\$
+               rc=\$?
+               rm -f tmp.\$\$
+               test \$rc = 0' &&
+       check_same BROKEN_HASH6 bisect/bad &&
+       git bisect reset
+"
+
 test_done
index f1cfc9ac959ee281d50f7b8a51fdcefd2d0670e7..b096dc88c2f4caf28887cf8fea5d700f0132f5a3 100755 (executable)
@@ -429,6 +429,21 @@ test_expect_success '--mixed refreshes the index' '
        test_i18ncmp expect output
 '
 
+test_expect_success 'resetting specific path that is unmerged' '
+       git rm --cached file2 &&
+       F1=$(git rev-parse HEAD:file1) &&
+       F2=$(git rev-parse HEAD:file2) &&
+       F3=$(git rev-parse HEAD:secondfile) &&
+       {
+               echo "100644 $F1 1      file2" &&
+               echo "100644 $F2 2      file2" &&
+               echo "100644 $F3 3      file2"
+       } | git update-index --index-info &&
+       git ls-files -u &&
+       test_must_fail git reset HEAD file2 &&
+       git diff-index --exit-code --cached HEAD
+'
+
 test_expect_success 'disambiguation (1)' '
 
        git reset --hard &&
index 5afe6ccd78f7171c6bdbb5be35d9663a618c1c73..69115269c2a284dcfc99abd2f7feb3d87f4e5bcc 100755 (executable)
@@ -47,8 +47,10 @@ test_expect_success 'setup - repository to add submodules to' '
 '
 
 # The 'submodule add' tests need some repository to add as a submodule.
-# The trash directory is a good one as any.
-submodurl=$TRASH_DIRECTORY
+# The trash directory is a good one as any. We need to canonicalize
+# the name, though, as some tests compare it to the absolute path git
+# generates, which will expand symbolic links.
+submodurl=$(pwd -P)
 
 listbranches() {
        git for-each-ref --format='%(refname)' 'refs/heads/*'
@@ -75,7 +77,8 @@ test_expect_success 'submodule add' '
 
        (
                cd addtest &&
-               git submodule add "$submodurl" submod &&
+               git submodule add -q "$submodurl" submod >actual &&
+               test ! -s actual &&
                git submodule init
        ) &&
 
@@ -273,7 +276,8 @@ test_expect_success 'update should work when path is an empty dir' '
        echo "$rev1" >expect &&
 
        mkdir init &&
-       git submodule update &&
+       git submodule update -q >update.out &&
+       test ! -s update.out &&
 
        inspect init &&
        test_cmp expect head-sha1
index a29ae45b399f89123f886f5b35580bcdc0e803f1..0d600163c8284a318fbd21f3a00dd7853b2f8956 100755 (executable)
@@ -509,6 +509,20 @@ test_expect_success 'grep -p -B5' '
        test_cmp expected actual
 '
 
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-       printf("Hello world.\n");
+hello.c:       return 0;
+hello.c-       /* char ?? */
+hello.c-}
+EOF
+
+test_expect_success 'grep -W' '
+       git grep -W return >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep from a subdirectory to search wider area (1)' '
        mkdir -p s &&
        (
index e5da65b99fa1459836e516772fe8d13d038f502e..41db05cb4af6f42ef3065ba0576d8cfa3509c0ca 100755 (executable)
@@ -50,8 +50,8 @@ test_expect_success \
     'mkdir A B C D E F &&
      echo hello1 >A/newfile1.txt &&
      echo hello2 >B/newfile2.txt &&
-     cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
-     cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png C/newfile3.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png D/newfile4.png &&
      git add A/newfile1.txt &&
      git add B/newfile2.txt &&
      git add C/newfile3.png &&
@@ -76,8 +76,8 @@ test_expect_success \
      rm -f B/newfile2.txt &&
      rm -f C/newfile3.png &&
      echo Hello5  >E/newfile5.txt &&
-     cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
-     cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
+     cp "$TEST_DIRECTORY"/test-binary-2.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test-binary-1.png F/newfile6.png &&
      git add E/newfile5.txt &&
      git add F/newfile6.png &&
      git commit -a -m "Test: Remove, add and update" &&
@@ -165,7 +165,7 @@ test_expect_success \
      'mkdir "G g" &&
       echo ok then >"G g/with spaces.txt" &&
       git add "G g/with spaces.txt" && \
-      cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
+      cp "$TEST_DIRECTORY"/test-binary-1.png "G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "With spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -177,7 +177,7 @@ test_expect_success \
 test_expect_success \
      'Update file with spaces in file name' \
      'echo Ok then >>"G g/with spaces.txt" &&
-      cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
+      cat "$TEST_DIRECTORY"/test-binary-1.png >>"G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -202,7 +202,7 @@ test_expect_success \
      'mkdir -p Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö &&
       echo Foo >Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö/gÃ¥rdetsÃ¥gÃ¥rdet.txt &&
       git add Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö/gÃ¥rdetsÃ¥gÃ¥rdet.txt &&
-      cp "$TEST_DIRECTORY"/test9200a.png Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö/gÃ¥rdetsÃ¥gÃ¥rdet.png &&
+      cp "$TEST_DIRECTORY"/test-binary-1.png Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö/gÃ¥rdetsÃ¥gÃ¥rdet.png &&
       git add Ã…/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/Ã¥/ä/ö/gÃ¥rdetsÃ¥gÃ¥rdet.png &&
       git commit -a -m "GÃ¥r det sÃ¥ gÃ¥r det" && \
       id=$(git rev-list --max-count=1 HEAD) &&
index 2a53640c5b29c19a2327570148638dbf94bc2afa..f256475020549a157297481e21859e235a5cfcc1 100755 (executable)
@@ -2197,6 +2197,48 @@ test_expect_success 'R: quiet option results in no stats being output' '
     test_cmp empty output
 '
 
+test_expect_success 'R: feature done means terminating "done" is mandatory' '
+       echo feature done | test_must_fail git fast-import &&
+       test_must_fail git fast-import --done </dev/null
+'
+
+test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
+       git fast-import <<-\EOF &&
+       feature done
+       done
+       trailing gibberish
+       EOF
+       git fast-import <<-\EOF
+       done
+       more trailing gibberish
+       EOF
+'
+
+test_expect_success 'R: terminating "done" within commit' '
+       cat >expect <<-\EOF &&
+       OBJID
+       :000000 100644 OBJID OBJID A    hello.c
+       :000000 100644 OBJID OBJID A    hello2.c
+       EOF
+       git fast-import <<-EOF &&
+       commit refs/heads/done-ends
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<EOT
+       Commit terminated by "done" command
+       EOT
+       M 100644 inline hello.c
+       data <<EOT
+       Hello, world.
+       EOT
+       C hello.c hello2.c
+       done
+       EOF
+       git rev-list done-ends |
+       git diff-tree -r --stdin --root --always |
+       sed -e "s/$_x40/OBJID/g" >actual &&
+       test_cmp expect actual
+'
+
 cat >input <<EOF
 option git non-existing-option
 EOF
index f823c05305e5021a8cca98d49256af3b1c008fb9..950d0ff498fda58c2d3d68dfb2cf50512f8f81ba 100755 (executable)
@@ -228,7 +228,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
        mkdir new &&
        git --git-dir=new/.git init &&
        git fast-export -C -C --signed-tags=strip --all > output &&
-       grep "^C \"file6\" \"file7\"\$" output &&
+       grep "^C file6 file7\$" output &&
        cat output |
        (cd new &&
         git fast-import &&
@@ -414,4 +414,30 @@ test_expect_success SYMLINKS 'directory becomes symlink'        '
        (cd result && git show master:foo)
 '
 
+test_expect_success 'fast-export quotes pathnames' '
+       git init crazy-paths &&
+       (cd crazy-paths &&
+        blob=`echo foo | git hash-object -w --stdin` &&
+        git update-index --add \
+               --cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
+               --cacheinfo 100644 $blob "path with \"quote\"" \
+               --cacheinfo 100644 $blob "path with \\backslash" \
+               --cacheinfo 100644 $blob "path with space" &&
+        git commit -m addition &&
+        git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index &&
+        git read-tree --empty &&
+        git update-index -z --index-info <index &&
+        git commit -m rename &&
+        git read-tree --empty &&
+        git commit -m deletion &&
+        git fast-export HEAD >export.out &&
+        git rev-list HEAD >expect &&
+        git init result &&
+        cd result &&
+        git fast-import <../export.out &&
+        git rev-list HEAD >actual &&
+        test_cmp ../expect actual
+       )
+'
+
 test_done
index 33b0127651da0c4c178e62c269179b641e15f9f6..97ec9753b45b4f2786e247e2897b3185b7b66df8 100755 (executable)
@@ -45,29 +45,33 @@ test_expect_success 'add p4 files' '
        cd "$TRASH_DIRECTORY"
 '
 
+cleanup_git() {
+       cd "$TRASH_DIRECTORY" &&
+       rm -rf "$git" &&
+       mkdir "$git"
+}
+
 test_expect_success 'basic git-p4 clone' '
        "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        git log --oneline >lines &&
-       test_line_count = 1 lines &&
-       cd .. &&
-       rm -rf "$git" && mkdir "$git"
+       test_line_count = 1 lines
 '
 
 test_expect_success 'git-p4 clone @all' '
        "$GITP4" clone --dest="$git" //depot@all &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        git log --oneline >lines &&
-       test_line_count = 2 lines &&
-       cd .. &&
-       rm -rf "$git" && mkdir "$git"
+       test_line_count = 2 lines
 '
 
 test_expect_success 'git-p4 sync uninitialized repo' '
        test_create_repo "$git" &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
-       test_must_fail "$GITP4" sync &&
-       rm -rf "$git" && mkdir "$git"
+       test_must_fail "$GITP4" sync
 '
 
 #
@@ -76,19 +80,18 @@ test_expect_success 'git-p4 sync uninitialized repo' '
 #
 test_expect_success 'git-p4 sync new branch' '
        test_create_repo "$git" &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        test_commit head &&
        "$GITP4" sync --branch=refs/remotes/p4/depot //depot@all &&
        git log --oneline p4/depot >lines &&
-       cat lines &&
-       test_line_count = 2 lines &&
-       cd .. &&
-       rm -rf "$git" && mkdir "$git"
+       test_line_count = 2 lines
 '
 
 test_expect_success 'exit when p4 fails to produce marshaled output' '
        badp4dir="$TRASH_DIRECTORY/badp4dir" &&
        mkdir -p "$badp4dir" &&
+       test_when_finished "rm -rf $badp4dir" &&
        cat >"$badp4dir"/p4 <<-EOF &&
        #!$SHELL_PATH
        exit 1
@@ -106,29 +109,26 @@ test_expect_success 'add p4 files with wildcards in the names' '
        echo file-wild-at >file-wild@at &&
        echo file-wild-percent >file-wild%percent &&
        p4 add -f file-wild* &&
-       p4 submit -d "file wildcards" &&
-       cd "$TRASH_DIRECTORY"
+       p4 submit -d "file wildcards"
 '
 
 test_expect_success 'wildcard files git-p4 clone' '
        "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        test -f file-wild#hash &&
        test -f file-wild\*star &&
        test -f file-wild@at &&
-       test -f file-wild%percent &&
-       cd "$TRASH_DIRECTORY" &&
-       rm -rf "$git" && mkdir "$git"
+       test -f file-wild%percent
 '
 
 test_expect_success 'clone bare' '
        "$GITP4" clone --dest="$git" --bare //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        test ! -d .git &&
        bare=`git config --get core.bare` &&
-       test "$bare" = true &&
-       cd "$TRASH_DIRECTORY" &&
-       rm -rf "$git" && mkdir "$git"
+       test "$bare" = true
 '
 
 p4_add_user() {
@@ -173,6 +173,7 @@ test_expect_success 'preserve users' '
        p4_add_user bob Bob &&
        p4_grant_admin alice &&
        "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
        echo "username: a change by alice" >> file1 &&
        echo "username: a change by bob" >> file2 &&
@@ -181,29 +182,28 @@ test_expect_success 'preserve users' '
        git config git-p4.skipSubmitEditCheck true &&
        P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
        p4_check_commit_author file1 alice &&
-       p4_check_commit_author file2 bob &&
-       cd "$TRASH_DIRECTORY" &&
-       rm -rf "$git" && mkdir "$git"
+       p4_check_commit_author file2 bob
 '
 
 # Test username support, submitting as bob, who lacks admin rights. Should
 # not submit change to p4 (git diff should show deltas).
 test_expect_success 'refuse to preserve users without perms' '
        "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
+       git config git-p4.skipSubmitEditCheck true &&
        echo "username-noperms: a change by alice" >> file1 &&
        git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
        ! P4EDITOR=touch P4USER=bob P4PASSWD=secret "$GITP4" commit --preserve-user &&
-       ! git diff --exit-code HEAD..p4/master > /dev/null &&
-       cd "$TRASH_DIRECTORY" &&
-       rm -rf "$git" && mkdir "$git"
+       ! git diff --exit-code HEAD..p4/master > /dev/null
 '
 
 # What happens with unknown author? Without allowMissingP4Users it should fail.
 test_expect_success 'preserve user where author is unknown to p4' '
        "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
        cd "$git" &&
-       git config git-p4.skipSubmitEditCheck true
+       git config git-p4.skipSubmitEditCheck true &&
        echo "username-bob: a change by bob" >> file1 &&
        git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
        echo "username-unknown: a change by charlie" >> file1 &&
@@ -215,9 +215,7 @@ test_expect_success 'preserve user where author is unknown to p4' '
        git config git-p4.preserveUser true &&
        P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit &&
        git diff --exit-code HEAD..p4/master > /dev/null &&
-       p4_check_commit_author file1 alice &&
-       cd "$TRASH_DIRECTORY" &&
-       rm -rf "$git" && mkdir "$git"
+       p4_check_commit_author file1 alice
 '
 
 # If we're *not* using --preserve-user, git-p4 should warn if we're submitting
@@ -226,33 +224,50 @@ test_expect_success 'preserve user where author is unknown to p4' '
 # Test: warning disabled and user is the same.
 test_expect_success 'not preserving user with mixed authorship' '
        "$GITP4" clone --dest="$git" //depot &&
-       (
-               cd "$git" &&
-               git config git-p4.skipSubmitEditCheck true &&
-               p4_add_user derek Derek &&
-
-               make_change_by_user usernamefile3 Derek derek@localhost &&
-               P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-               grep "git author derek@localhost does not match" actual &&
-
-               make_change_by_user usernamefile3 Charlie charlie@localhost &&
-               P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-               grep "git author charlie@localhost does not match" actual &&
-
-               make_change_by_user usernamefile3 alice alice@localhost &&
-               P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-               ! grep "git author.*does not match" actual &&
-
-               git config git-p4.skipUserNameCheck true &&
-               make_change_by_user usernamefile3 Charlie charlie@localhost &&
-               P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
-               ! grep "git author.*does not match" actual &&
-
-               p4_check_commit_author usernamefile3 alice
-       ) &&
-       rm -rf "$git" && mkdir "$git"
+       test_when_finished cleanup_git &&
+       cd "$git" &&
+       git config git-p4.skipSubmitEditCheck true &&
+       p4_add_user derek Derek &&
+
+       make_change_by_user usernamefile3 Derek derek@localhost &&
+       P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
+       grep "git author derek@localhost does not match" actual &&
+
+       make_change_by_user usernamefile3 Charlie charlie@localhost &&
+       P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
+       grep "git author charlie@localhost does not match" actual &&
+
+       make_change_by_user usernamefile3 alice alice@localhost &&
+       P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
+       ! grep "git author.*does not match" actual &&
+
+       git config git-p4.skipUserNameCheck true &&
+       make_change_by_user usernamefile3 Charlie charlie@localhost &&
+       P4EDITOR=cat P4USER=alice P4PASSWD=secret "$GITP4" commit >actual &&
+       ! grep "git author.*does not match" actual &&
+
+       p4_check_commit_author usernamefile3 alice
 '
 
+marshal_dump() {
+       what=$1
+       python -c 'import marshal, sys; d = marshal.load(sys.stdin); print d["'$what'"]'
+}
+
+# Sleep a bit so that the top-most p4 change did not happen "now".  Then
+# import the repo and make sure that the initial import has the same time
+# as the top-most change.
+test_expect_success 'initial import time from top change time' '
+       p4change=$(p4 -G changes -m 1 //depot/... | marshal_dump change) &&
+       p4time=$(p4 -G changes -m 1 //depot/... | marshal_dump time) &&
+       sleep 3 &&
+       "$GITP4" clone --dest="$git" //depot &&
+       test_when_finished cleanup_git &&
+       cd "$git" &&
+       gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+       echo $p4time $gittime &&
+       test $p4time = $gittime
+'
 
 test_expect_success 'shutdown' '
        pid=`pgrep -f p4d` &&
similarity index 100%
rename from t/test4012.png
rename to t/test-binary-1.png
similarity index 100%
rename from t/test9200b.png
rename to t/test-binary-2.png
diff --git a/t/test9200a.png b/t/test9200a.png
deleted file mode 100644 (file)
index 7b181d1..0000000
Binary files a/t/test9200a.png and /dev/null differ
index 660147f9e96be7b69379a9040c4220c141c7be4c..4eab844d4abfa29ce53b6245a5821ee7be03641b 100644 (file)
@@ -23,6 +23,8 @@ struct helper_data {
                push : 1,
                connect : 1,
                no_disconnect_req : 1;
+       char *export_marks;
+       char *import_marks;
        /* These go from remote name (as in "list") to private name */
        struct refspec *refspecs;
        int refspec_nr;
@@ -105,6 +107,12 @@ static struct child_process *get_helper(struct transport *transport)
        int refspec_alloc = 0;
        int duped;
        int code;
+       char git_dir_buf[sizeof(GIT_DIR_ENVIRONMENT) + PATH_MAX + 1];
+       const char *helper_env[] = {
+               git_dir_buf,
+               NULL
+       };
+
 
        if (data->helper)
                return data->helper;
@@ -120,6 +128,10 @@ static struct child_process *get_helper(struct transport *transport)
        helper->argv[2] = remove_ext_force(transport->url);
        helper->git_cmd = 0;
        helper->silent_exec_failure = 1;
+
+       snprintf(git_dir_buf, sizeof(git_dir_buf), "%s=%s", GIT_DIR_ENVIRONMENT, get_git_dir());
+       helper->env = helper_env;
+
        code = start_command(helper);
        if (code < 0 && errno == ENOENT)
                die("Unable to find remote helper for '%s'", data->name);
@@ -171,14 +183,19 @@ static struct child_process *get_helper(struct transport *transport)
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
-                       refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
+                       refspecs[refspec_nr++] = strdup(capname + strlen("refspec "));
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
-               } else if (!strcmp(buf.buf, "gitdir")) {
-                       struct strbuf gitdir = STRBUF_INIT;
-                       strbuf_addf(&gitdir, "gitdir %s\n", get_git_dir());
-                       sendline(data, &gitdir);
-                       strbuf_release(&gitdir);
+               } else if (!prefixcmp(capname, "export-marks ")) {
+                       struct strbuf arg = STRBUF_INIT;
+                       strbuf_addstr(&arg, "--export-marks=");
+                       strbuf_addstr(&arg, capname + strlen("export-marks "));
+                       data->export_marks = strbuf_detach(&arg, NULL);
+               } else if (!prefixcmp(capname, "import-marks")) {
+                       struct strbuf arg = STRBUF_INIT;
+                       strbuf_addstr(&arg, "--import-marks=");
+                       strbuf_addstr(&arg, capname + strlen("import-marks "));
+                       data->import_marks = strbuf_detach(&arg, NULL);
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
                            "helper probably needs newer version of Git.\n",
@@ -204,6 +221,7 @@ static int disconnect_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
+       int res = 0;
 
        if (data->helper) {
                if (debug)
@@ -215,13 +233,13 @@ static int disconnect_helper(struct transport *transport)
                close(data->helper->in);
                close(data->helper->out);
                fclose(data->out);
-               finish_command(data->helper);
+               res = finish_command(data->helper);
                free((char *)data->helper->argv[0]);
                free(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
-       return 0;
+       return res;
 }
 
 static const char *unsupported_options[] = {
@@ -299,12 +317,13 @@ static void standard_options(struct transport *t)
 
 static int release_helper(struct transport *transport)
 {
+       int res = 0;
        struct helper_data *data = transport->data;
        free_refspec(data->refspec_nr, data->refspecs);
        data->refspecs = NULL;
-       disconnect_helper(transport);
+       res = disconnect_helper(transport);
        free(transport->data);
-       return 0;
+       return res;
 }
 
 static int fetch_with_fetch(struct transport *transport,
@@ -362,10 +381,9 @@ static int get_importer(struct transport *transport, struct child_process *fasti
 
 static int get_exporter(struct transport *transport,
                        struct child_process *fastexport,
-                       const char *export_marks,
-                       const char *import_marks,
                        struct string_list *revlist_args)
 {
+       struct helper_data *data = transport->data;
        struct child_process *helper = get_helper(transport);
        int argc = 0, i;
        memset(fastexport, 0, sizeof(*fastexport));
@@ -373,12 +391,13 @@ static int get_exporter(struct transport *transport,
        /* we need to duplicate helper->in because we want to use it after
         * fastexport is done with it. */
        fastexport->out = dup(helper->in);
-       fastexport->argv = xcalloc(4 + revlist_args->nr, sizeof(*fastexport->argv));
+       fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));
        fastexport->argv[argc++] = "fast-export";
-       if (export_marks)
-               fastexport->argv[argc++] = export_marks;
-       if (import_marks)
-               fastexport->argv[argc++] = import_marks;
+       fastexport->argv[argc++] = "--use-done-feature";
+       if (data->export_marks)
+               fastexport->argv[argc++] = data->export_marks;
+       if (data->import_marks)
+               fastexport->argv[argc++] = data->import_marks;
 
        for (i = 0; i < revlist_args->nr; i++)
                fastexport->argv[argc++] = revlist_args->items[i].string;
@@ -410,8 +429,11 @@ static int fetch_with_import(struct transport *transport,
                sendline(data, &buf);
                strbuf_reset(&buf);
        }
-       disconnect_helper(transport);
-       finish_command(&fastimport);
+
+       write_constant(data->helper->in, "\n");
+
+       if (finish_command(&fastimport))
+               die("Error while running fast-import");
        free(fastimport.argv);
        fastimport.argv = NULL;
 
@@ -554,6 +576,88 @@ static int fetch(struct transport *transport,
        return -1;
 }
 
+static void push_update_ref_status(struct strbuf *buf,
+                                  struct ref **ref,
+                                  struct ref *remote_refs)
+{
+       char *refname, *msg;
+       int status;
+
+       if (!prefixcmp(buf->buf, "ok ")) {
+               status = REF_STATUS_OK;
+               refname = buf->buf + 3;
+       } else if (!prefixcmp(buf->buf, "error ")) {
+               status = REF_STATUS_REMOTE_REJECT;
+               refname = buf->buf + 6;
+       } else
+               die("expected ok/error, helper said '%s'\n", buf->buf);
+
+       msg = strchr(refname, ' ');
+       if (msg) {
+               struct strbuf msg_buf = STRBUF_INIT;
+               const char *end;
+
+               *msg++ = '\0';
+               if (!unquote_c_style(&msg_buf, msg, &end))
+                       msg = strbuf_detach(&msg_buf, NULL);
+               else
+                       msg = xstrdup(msg);
+               strbuf_release(&msg_buf);
+
+               if (!strcmp(msg, "no match")) {
+                       status = REF_STATUS_NONE;
+                       free(msg);
+                       msg = NULL;
+               }
+               else if (!strcmp(msg, "up to date")) {
+                       status = REF_STATUS_UPTODATE;
+                       free(msg);
+                       msg = NULL;
+               }
+               else if (!strcmp(msg, "non-fast forward")) {
+                       status = REF_STATUS_REJECT_NONFASTFORWARD;
+                       free(msg);
+                       msg = NULL;
+               }
+       }
+
+       if (*ref)
+               *ref = find_ref_by_name(*ref, refname);
+       if (!*ref)
+               *ref = find_ref_by_name(remote_refs, refname);
+       if (!*ref) {
+               warning("helper reported unexpected status of %s", refname);
+               return;
+       }
+
+       if ((*ref)->status != REF_STATUS_NONE) {
+               /*
+                * Earlier, the ref was marked not to be pushed, so ignore the ref
+                * status reported by the remote helper if the latter is 'no match'.
+                */
+               if (status == REF_STATUS_NONE)
+                       return;
+       }
+
+       (*ref)->status = status;
+       (*ref)->remote_status = msg;
+}
+
+static void push_update_refs_status(struct helper_data *data,
+                                   struct ref *remote_refs)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct ref *ref = remote_refs;
+       for (;;) {
+               recvline(data, &buf);
+               if (!buf.len)
+                       break;
+
+               push_update_ref_status(&buf, &ref, remote_refs);
+       }
+       strbuf_release(&buf);
+}
+
 static int push_refs_with_push(struct transport *transport,
                struct ref *remote_refs, int flags)
 {
@@ -608,76 +712,9 @@ static int push_refs_with_push(struct transport *transport,
 
        strbuf_addch(&buf, '\n');
        sendline(data, &buf);
-
-       ref = remote_refs;
-       while (1) {
-               char *refname, *msg;
-               int status;
-
-               recvline(data, &buf);
-               if (!buf.len)
-                       break;
-
-               if (!prefixcmp(buf.buf, "ok ")) {
-                       status = REF_STATUS_OK;
-                       refname = buf.buf + 3;
-               } else if (!prefixcmp(buf.buf, "error ")) {
-                       status = REF_STATUS_REMOTE_REJECT;
-                       refname = buf.buf + 6;
-               } else
-                       die("expected ok/error, helper said '%s'\n", buf.buf);
-
-               msg = strchr(refname, ' ');
-               if (msg) {
-                       struct strbuf msg_buf = STRBUF_INIT;
-                       const char *end;
-
-                       *msg++ = '\0';
-                       if (!unquote_c_style(&msg_buf, msg, &end))
-                               msg = strbuf_detach(&msg_buf, NULL);
-                       else
-                               msg = xstrdup(msg);
-                       strbuf_release(&msg_buf);
-
-                       if (!strcmp(msg, "no match")) {
-                               status = REF_STATUS_NONE;
-                               free(msg);
-                               msg = NULL;
-                       }
-                       else if (!strcmp(msg, "up to date")) {
-                               status = REF_STATUS_UPTODATE;
-                               free(msg);
-                               msg = NULL;
-                       }
-                       else if (!strcmp(msg, "non-fast forward")) {
-                               status = REF_STATUS_REJECT_NONFASTFORWARD;
-                               free(msg);
-                               msg = NULL;
-                       }
-               }
-
-               if (ref)
-                       ref = find_ref_by_name(ref, refname);
-               if (!ref)
-                       ref = find_ref_by_name(remote_refs, refname);
-               if (!ref) {
-                       warning("helper reported unexpected status of %s", refname);
-                       continue;
-               }
-
-               if (ref->status != REF_STATUS_NONE) {
-                       /*
-                        * Earlier, the ref was marked not to be pushed, so ignore the ref
-                        * status reported by the remote helper if the latter is 'no match'.
-                        */
-                       if (status == REF_STATUS_NONE)
-                               continue;
-               }
-
-               ref->status = status;
-               ref->remote_status = msg;
-       }
        strbuf_release(&buf);
+
+       push_update_refs_status(data, remote_refs);
        return 0;
 }
 
@@ -687,7 +724,6 @@ static int push_refs_with_export(struct transport *transport,
        struct ref *ref;
        struct child_process *helper, exporter;
        struct helper_data *data = transport->data;
-       char *export_marks = NULL, *import_marks = NULL;
        struct string_list revlist_args = STRING_LIST_INIT_NODUP;
        struct strbuf buf = STRBUF_INIT;
 
@@ -695,26 +731,6 @@ static int push_refs_with_export(struct transport *transport,
 
        write_constant(helper->in, "export\n");
 
-       recvline(data, &buf);
-       if (debug)
-               fprintf(stderr, "Debug: Got export_marks '%s'\n", buf.buf);
-       if (buf.len) {
-               struct strbuf arg = STRBUF_INIT;
-               strbuf_addstr(&arg, "--export-marks=");
-               strbuf_addbuf(&arg, &buf);
-               export_marks = strbuf_detach(&arg, NULL);
-       }
-
-       recvline(data, &buf);
-       if (debug)
-               fprintf(stderr, "Debug: Got import_marks '%s'\n", buf.buf);
-       if (buf.len) {
-               struct strbuf arg = STRBUF_INIT;
-               strbuf_addstr(&arg, "--import-marks=");
-               strbuf_addbuf(&arg, &buf);
-               import_marks = strbuf_detach(&arg, NULL);
-       }
-
        strbuf_reset(&buf);
 
        for (ref = remote_refs; ref; ref = ref->next) {
@@ -728,18 +744,23 @@ static int push_refs_with_export(struct transport *transport,
                        strbuf_addf(&buf, "^%s", private);
                        string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
                }
+               free(private);
+
+               if (ref->deletion) {
+                       die("remote-helpers do not support ref deletion");
+               }
 
-               string_list_append(&revlist_args, ref->name);
+               if (ref->peer_ref)
+                       string_list_append(&revlist_args, ref->peer_ref->name);
 
        }
 
-       if (get_exporter(transport, &exporter,
-                        export_marks, import_marks, &revlist_args))
+       if (get_exporter(transport, &exporter, &revlist_args))
                die("Couldn't run fast-export");
 
-       data->no_disconnect_req = 1;
-       finish_command(&exporter);
-       disconnect_helper(transport);
+       if (finish_command(&exporter))
+               die("Error while running fast-export");
+       push_update_refs_status(data, remote_refs);
        return 0;
 }
 
index c9c8056f9de69bd378cd271d70363b5560f13e07..98c577804f177b1c6f9df0e34e5fc3a656a81d27 100644 (file)
@@ -482,14 +482,18 @@ static int set_git_option(struct git_transport_options *opts,
 static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
        struct git_transport_data *data = transport->data;
+       struct strbuf sb = STRBUF_INIT;
 
        if (data->conn)
                return 0;
 
-       data->conn = git_connect(data->fd, transport->url,
-                                for_push ? data->options.receivepack :
-                                data->options.uploadpack,
+       strbuf_addstr(&sb, for_push ? data->options.receivepack :
+                                data->options.uploadpack);
+       if (for_push && transport->verbose < 0)
+               strbuf_addstr(&sb, " --quiet");
+       data->conn = git_connect(data->fd, transport->url, sb.buf,
                                 verbose ? CONNECT_VERBOSE : 0);
+       strbuf_release(&sb);
 
        return 0;
 }
index 3a61d821ee85a9cfb026909c6ae90def360a7681..cc616c3f991a655d10fcac15055fcdfafa8620fd 100644 (file)
@@ -1168,11 +1168,22 @@ static int verify_uptodate_1(struct cache_entry *ce,
 {
        struct stat st;
 
-       if (o->index_only || (!((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) && (o->reset || ce_uptodate(ce))))
+       if (o->index_only)
+               return 0;
+
+       /*
+        * CE_VALID and CE_SKIP_WORKTREE cheat, we better check again
+        * if this entry is truly up-to-date because this file may be
+        * overwritten.
+        */
+       if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
+               ; /* keep checking */
+       else if (o->reset || ce_uptodate(ce))
                return 0;
 
        if (!lstat(ce->name, &st)) {
-               unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+               int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
+               unsigned changed = ie_match_stat(o->src_index, ce, &st, flags);
                if (!changed)
                        return 0;
                /*
index ce5cbbea6be45a6dfa79bcc8c8244ae0b99c7ca3..6420918abea3a9a33ad967d263153e4548daab29 100644 (file)
@@ -10,6 +10,7 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "run-command.h"
+#include "sigchain.h"
 
 static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 
@@ -498,11 +499,95 @@ static int get_common_commits(void)
        }
 }
 
+static void check_non_tip(void)
+{
+       static const char *argv[] = {
+               "rev-list", "--stdin", NULL,
+       };
+       static struct child_process cmd;
+       struct object *o;
+       char namebuf[42]; /* ^ + SHA-1 + LF */
+       int i;
+
+       /* In the normal in-process case non-tip request can never happen */
+       if (!stateless_rpc)
+               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;
+
+       /*
+        * If rev-list --stdin encounters an unknown commit, it
+        * terminates, which will cause SIGPIPE in the write loop
+        * below.
+        */
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       namebuf[0] = '^';
+       namebuf[41] = '\n';
+       for (i = get_max_object_index(); 0 < i; ) {
+               o = get_indexed_object(--i);
+               if (!(o->flags & OUR_REF))
+                       continue;
+               memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
+               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 (o->flags & OUR_REF)
+                       continue;
+               memcpy(namebuf, sha1_to_hex(o->sha1), 40);
+               if (write_in_full(cmd.in, namebuf, 41) < 0)
+                       goto error;
+       }
+       close(cmd.in);
+
+       sigchain_pop(SIGPIPE);
+
+       /*
+        * The commits out of the rev-list are not ancestors of
+        * our ref.
+        */
+       i = read_in_full(cmd.out, namebuf, 1);
+       if (i)
+               goto error;
+       close(cmd.out);
+
+       /*
+        * rev-list may have died by encountering a bad commit
+        * in the history, in which case we do want to bail out
+        * even when it showed no commit.
+        */
+       if (finish_command(&cmd))
+               goto error;
+
+       /* 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;
+               if (!(o->flags & OUR_REF))
+                       die("git upload-pack: not our ref %s",
+                           sha1_to_hex(o->sha1));
+       }
+}
+
 static void receive_needs(void)
 {
        struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
+       int has_non_tip = 0;
 
        shallow_nr = 0;
        if (debug_fd)
@@ -559,26 +644,30 @@ static void receive_needs(void)
                if (strstr(line+45, "include-tag"))
                        use_include_tag = 1;
 
-               /* We have sent all our refs already, and the other end
-                * should have chosen out of them; otherwise they are
-                * asking for nonsense.
-                *
-                * Hmph.  We may later want to allow "want" line that
-                * asks for something like "master~10" (symbolic)...
-                * would it make sense?  I don't know.
-                */
                o = lookup_object(sha1_buf);
-               if (!o || !(o->flags & OUR_REF))
+               if (!o)
                        die("git upload-pack: not our ref %s",
                            sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
+                       if (!(o->flags & OUR_REF))
+                               has_non_tip = 1;
                        add_object_array(o, NULL, &want_obj);
                }
        }
        if (debug_fd)
                write_str_in_full(debug_fd, "#E\n");
 
+       /*
+        * We have sent all our refs already, and the other end
+        * should have chosen out of them. When we are operating
+        * in the stateless RPC mode, however, their choice may
+        * have been based on the set of older refs advertised
+        * by another process that handled the initial request.
+        */
+       if (has_non_tip)
+               check_non_tip();
+
        if (!use_sideband && daemon_mode)
                no_progress = 1;
 
@@ -641,16 +730,17 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
                " side-band-64k ofs-delta shallow no-progress"
                " include-tag multi_ack_detailed";
        struct object *o = parse_object(sha1);
+       const char *refname_nons = strip_namespace(refname);
 
        if (!o)
                die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
 
        if (capabilities)
-               packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname,
+               packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
                             stateless_rpc ? " no-done" : "");
        else
-               packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
+               packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
        capabilities = NULL;
        if (!(o->flags & OUR_REF)) {
                o->flags |= OUR_REF;
@@ -659,7 +749,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, refname, 0);
                if (o)
-                       packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
+                       packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
        }
        return 0;
 }
@@ -680,12 +770,12 @@ static void upload_pack(void)
 {
        if (advertise_refs || !stateless_rpc) {
                reset_timeout();
-               head_ref(send_ref, NULL);
-               for_each_ref(send_ref, NULL);
+               head_ref_namespaced(send_ref, NULL);
+               for_each_namespaced_ref(send_ref, NULL);
                packet_flush(1);
        } else {
-               head_ref(mark_our_ref, NULL);
-               for_each_ref(mark_our_ref, NULL);
+               head_ref_namespaced(mark_our_ref, NULL);
+               for_each_namespaced_ref(mark_our_ref, NULL);
        }
        if (advertise_refs)
                return;
diff --git a/usage.c b/usage.c
index b5e67e3d0d4a46a69d72371f4c6a0002e40a0bf1..a2a667800474e315a362e1d0623c17dafaad1022 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "git-compat-util.h"
+#include "cache.h"
 
 void vreportf(const char *prefix, const char *err, va_list params)
 {
@@ -12,6 +13,18 @@ void vreportf(const char *prefix, const char *err, va_list params)
        fprintf(stderr, "%s%s\n", prefix, msg);
 }
 
+void vwritef(int fd, const char *prefix, const char *err, va_list params)
+{
+       char msg[4096];
+       int len = vsnprintf(msg, sizeof(msg), err, params);
+       if (len > sizeof(msg))
+               len = sizeof(msg);
+
+       write_in_full(fd, prefix, strlen(prefix));
+       write_in_full(fd, msg, len);
+       write_in_full(fd, "\n", 1);
+}
+
 static NORETURN void usage_builtin(const char *err, va_list params)
 {
        vreportf("usage: ", err, params);
@@ -46,6 +59,11 @@ void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list param
        die_routine = routine;
 }
 
+void set_error_routine(void (*routine)(const char *err, va_list params))
+{
+       error_routine = routine;
+}
+
 void NORETURN usagef(const char *err, ...)
 {
        va_list params;
index 711048ea36dda1d814b395efa8bc69ab142425ca..4beb10c678702a34b14914f8b292dd65b5c273cd 100644 (file)
@@ -33,6 +33,7 @@ extern "C" {
 #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
 #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
 #define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
 #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
 
 #define XDL_PATCH_NORMAL '-'
@@ -105,7 +106,6 @@ typedef struct s_bdiffparam {
 #define xdl_realloc(ptr,x) realloc(ptr,x)
 
 void *xdl_mmfile_first(mmfile_t *mmf, long *size);
-void *xdl_mmfile_next(mmfile_t *mmf, long *size);
 long xdl_mmfile_size(mmfile_t *mmf);
 
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
index da67c04357dfe4d3283c589f5d47be9c5f2b7fcf..75a39227501715504cdd12ccc1b4854568a54ad7 100644 (file)
@@ -331,6 +331,9 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        if (xpp->flags & XDF_PATIENCE_DIFF)
                return xdl_do_patience_diff(mf1, mf2, xpp, xe);
 
+       if (xpp->flags & XDF_HISTOGRAM_DIFF)
+               return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
        if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
 
                return -1;
index ad033a8e6a79600b6a3ba0cc16244ede0e9437ea..7a92ea9c4d84a559ae1d0bd90ebe667828d8f9cb 100644 (file)
@@ -57,5 +57,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg);
 int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+               xdfenv_t *env);
 
 #endif /* #if !defined(XDIFFI_H) */
diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c
new file mode 100644 (file)
index 0000000..18f6f99
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR        UINT_MAX
+#define MAX_CNT        UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+       struct record {
+               unsigned int ptr, cnt;
+               struct record *next;
+       } **records, /* an ocurrence */
+         **line_map; /* map of line to record chain */
+       chastore_t rcha;
+       unsigned int *next_ptrs;
+       unsigned int table_bits,
+                    records_size,
+                    line_map_size;
+
+       unsigned int max_chain_length,
+                    key_shift,
+                    ptr_shift;
+
+       unsigned int cnt,
+                    has_common;
+
+       xdfenv_t *env;
+       xpparam_t const *xpp;
+};
+
+struct region {
+       unsigned int begin1, end1;
+       unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+       (index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+       ((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+       (env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+       xrecord_t *r1, xrecord_t *r2)
+{
+       return r1->ha == r2->ha &&
+               xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+                           xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+       (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+       (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+       XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, int line1, int count1)
+{
+       unsigned int ptr, tbl_idx;
+       unsigned int chain_len;
+       struct record **rec_chain, *rec;
+
+       for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+               tbl_idx = TABLE_HASH(index, 1, ptr);
+               rec_chain = index->records + tbl_idx;
+               rec = *rec_chain;
+
+               chain_len = 0;
+               while (rec) {
+                       if (CMP(index, 1, rec->ptr, 1, ptr)) {
+                               /*
+                                * ptr is identical to another element. Insert
+                                * it onto the front of the existing element
+                                * chain.
+                                */
+                               NEXT_PTR(index, ptr) = rec->ptr;
+                               rec->ptr = ptr;
+                               /* cap rec->cnt at MAX_CNT */
+                               rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+                               LINE_MAP(index, ptr) = rec;
+                               goto continue_scan;
+                       }
+
+                       rec = rec->next;
+                       chain_len++;
+               }
+
+               if (chain_len == index->max_chain_length)
+                       return -1;
+
+               /*
+                * This is the first time we have ever seen this particular
+                * element in the sequence. Construct a new chain for it.
+                */
+               if (!(rec = xdl_cha_alloc(&index->rcha)))
+                       return -1;
+               rec->ptr = ptr;
+               rec->cnt = 1;
+               rec->next = *rec_chain;
+               *rec_chain = rec;
+               LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+               ; /* no op */
+       }
+
+       return 0;
+}
+
+static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
+       int line1, int count1, int line2, int count2)
+{
+       unsigned int b_next = b_ptr + 1;
+       struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+       unsigned int as, ae, bs, be, np, rc;
+       int should_break;
+
+       for (; rec; rec = rec->next) {
+               if (rec->cnt > index->cnt) {
+                       if (!index->has_common)
+                               index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+                       continue;
+               }
+
+               as = rec->ptr;
+               if (!CMP(index, 1, as, 2, b_ptr))
+                       continue;
+
+               index->has_common = 1;
+               for (;;) {
+                       should_break = 0;
+                       np = NEXT_PTR(index, as);
+                       bs = b_ptr;
+                       ae = as;
+                       be = bs;
+                       rc = rec->cnt;
+
+                       while (line1 < as && line2 < bs
+                               && CMP(index, 1, as - 1, 2, bs - 1)) {
+                               as--;
+                               bs--;
+                               if (1 < rc)
+                                       rc = XDL_MIN(rc, CNT(index, as));
+                       }
+                       while (ae < LINE_END(1) && be < LINE_END(2)
+                               && CMP(index, 1, ae + 1, 2, be + 1)) {
+                               ae++;
+                               be++;
+                               if (1 < rc)
+                                       rc = XDL_MIN(rc, CNT(index, ae));
+                       }
+
+                       if (b_next <= be)
+                               b_next = be + 1;
+                       if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+                               lcs->begin1 = as;
+                               lcs->begin2 = bs;
+                               lcs->end1 = ae;
+                               lcs->end2 = be;
+                               index->cnt = rc;
+                       }
+
+                       if (np == 0)
+                               break;
+
+                       while (np <= ae) {
+                               np = NEXT_PTR(index, np);
+                               if (np == 0) {
+                                       should_break = 1;
+                                       break;
+                               }
+                       }
+
+                       if (should_break)
+                               break;
+
+                       as = np;
+               }
+       }
+       return b_next;
+}
+
+static int find_lcs(struct histindex *index, struct region *lcs,
+       int line1, int count1, int line2, int count2) {
+       int b_ptr;
+
+       if (scanA(index, line1, count1))
+               return -1;
+
+       index->cnt = index->max_chain_length + 1;
+
+       for (b_ptr = line2; b_ptr <= LINE_END(2); )
+               b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+       return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+               int line1, int count1, int line2, int count2)
+{
+       xpparam_t xpp;
+       xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+
+       return xdl_fall_back_diff(index->env, &xpp,
+                                 line1, count1, line2, count2);
+}
+
+static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
+       int line1, int count1, int line2, int count2)
+{
+       struct histindex index;
+       struct region lcs;
+       int sz;
+       int result = -1;
+
+       if (count1 <= 0 && count2 <= 0)
+               return 0;
+
+       if (LINE_END(1) >= MAX_PTR)
+               return -1;
+
+       if (!count1) {
+               while(count2--)
+                       env->xdf2.rchg[line2++ - 1] = 1;
+               return 0;
+       } else if (!count2) {
+               while(count1--)
+                       env->xdf1.rchg[line1++ - 1] = 1;
+               return 0;
+       }
+
+       memset(&index, 0, sizeof(index));
+
+       index.env = env;
+       index.xpp = xpp;
+
+       index.records = NULL;
+       index.line_map = NULL;
+       /* in case of early xdl_cha_free() */
+       index.rcha.head = NULL;
+
+       index.table_bits = xdl_hashbits(count1);
+       sz = index.records_size = 1 << index.table_bits;
+       sz *= sizeof(struct record *);
+       if (!(index.records = (struct record **) xdl_malloc(sz)))
+               goto cleanup;
+       memset(index.records, 0, sz);
+
+       sz = index.line_map_size = count1;
+       sz *= sizeof(struct record *);
+       if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+               goto cleanup;
+       memset(index.line_map, 0, sz);
+
+       sz = index.line_map_size;
+       sz *= sizeof(unsigned int);
+       if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+               goto cleanup;
+       memset(index.next_ptrs, 0, sz);
+
+       /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+       if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+               goto cleanup;
+
+       index.ptr_shift = line1;
+       index.max_chain_length = 64;
+
+       memset(&lcs, 0, sizeof(lcs));
+       if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+               result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+       else {
+               if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+                       while (count1--)
+                               env->xdf1.rchg[line1++ - 1] = 1;
+                       while (count2--)
+                               env->xdf2.rchg[line2++ - 1] = 1;
+                       result = 0;
+               } else {
+                       result = histogram_diff(xpp, env,
+                                               line1, lcs.begin1 - line1,
+                                               line2, lcs.begin2 - line2);
+                       if (result)
+                               goto cleanup;
+                       result = histogram_diff(xpp, env,
+                                               lcs.end1 + 1, LINE_END(1) - lcs.end1,
+                                               lcs.end2 + 1, LINE_END(2) - lcs.end2);
+                       if (result)
+                               goto cleanup;
+               }
+       }
+
+cleanup:
+       xdl_free(index.records);
+       xdl_free(index.line_map);
+       xdl_free(index.next_ptrs);
+       xdl_cha_free(&index.rcha);
+
+       return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+       xpparam_t const *xpp, xdfenv_t *env)
+{
+       if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+               return -1;
+
+       return histogram_diff(xpp, env,
+               env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+               env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
index e42c16a807609b967c6a807a99d4ecbe2e1e0af8..fdd7d0263f576a8dc1a8e791ef50f8dbe25c7ee5 100644 (file)
@@ -287,34 +287,11 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first,
 static int fall_back_to_classic_diff(struct hashmap *map,
                int line1, int count1, int line2, int count2)
 {
-       /*
-        * This probably does not work outside Git, since
-        * we have a very simple mmfile structure.
-        *
-        * Note: ideally, we would reuse the prepared environment, but
-        * the libxdiff interface does not (yet) allow for diffing only
-        * ranges of lines instead of the whole files.
-        */
-       mmfile_t subfile1, subfile2;
        xpparam_t xpp;
-       xdfenv_t env;
-
-       subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr;
-       subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr +
-               map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
-       subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr;
-       subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr +
-               map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
        xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
-       if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0)
-               return -1;
 
-       memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
-       memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
-
-       xdl_free_env(&env);
-
-       return 0;
+       return xdl_fall_back_diff(map->env, &xpp,
+                                 line1, count1, line2, count2);
 }
 
 /*
index 16890852350cb62bb9f9aec5e52eea8ba46f1192..620fc9a657e2246d3a382c916c2cdd4f820c0c44 100644 (file)
@@ -26,6 +26,8 @@
 #define XDL_KPDIS_RUN 4
 #define XDL_MAX_EQLIMIT 1024
 #define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
 
 
 typedef struct s_xdlclass {
@@ -64,8 +66,6 @@ static int xdl_optimize_ctxs(xdfile_t *xdf1, xdfile_t *xdf2);
 
 
 static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
-       long i;
-
        cf->flags = flags;
 
        cf->hbits = xdl_hashbits((unsigned int) size);
@@ -80,8 +80,7 @@ static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
                xdl_cha_free(&cf->ncha);
                return -1;
        }
-       for (i = 0; i < cf->hsize; i++)
-               cf->rchash[i] = NULL;
+       memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
 
        cf->count = 0;
 
@@ -136,7 +135,7 @@ static int xdl_classify_record(xdlclassifier_t *cf, xrecord_t **rhash, unsigned
 static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
                           xdlclassifier_t *cf, xdfile_t *xdf) {
        unsigned int hbits;
-       long i, nrec, hsize, bsize;
+       long nrec, hsize, bsize;
        unsigned long hav;
        char const *blk, *cur, *top, *prev;
        xrecord_t *crec;
@@ -146,96 +145,59 @@ static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
        char *rchg;
        long *rindex;
 
-       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) {
-
-               return -1;
-       }
-       if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) {
-
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
-
-       hbits = xdl_hashbits((unsigned int) narec);
-       hsize = 1 << hbits;
-       if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) {
-
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
+       ha = NULL;
+       rindex = NULL;
+       rchg = NULL;
+       rhash = NULL;
+       recs = NULL;
+
+       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+               goto abort;
+       if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+               goto abort;
+
+       if (xpp->flags & XDF_HISTOGRAM_DIFF)
+               hbits = hsize = 0;
+       else {
+               hbits = xdl_hashbits((unsigned int) narec);
+               hsize = 1 << hbits;
+               if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+                       goto abort;
+               memset(rhash, 0, hsize * sizeof(xrecord_t *));
        }
-       for (i = 0; i < hsize; i++)
-               rhash[i] = NULL;
 
        nrec = 0;
        if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
-               for (top = blk + bsize;;) {
-                       if (cur >= top) {
-                               if (!(cur = blk = xdl_mmfile_next(mf, &bsize)))
-                                       break;
-                               top = blk + bsize;
-                       }
+               for (top = blk + bsize; cur < top; ) {
                        prev = cur;
                        hav = xdl_hash_record(&cur, top, xpp->flags);
                        if (nrec >= narec) {
                                narec *= 2;
-                               if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) {
-
-                                       xdl_free(rhash);
-                                       xdl_free(recs);
-                                       xdl_cha_free(&xdf->rcha);
-                                       return -1;
-                               }
+                               if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+                                       goto abort;
                                recs = rrecs;
                        }
-                       if (!(crec = xdl_cha_alloc(&xdf->rcha))) {
-
-                               xdl_free(rhash);
-                               xdl_free(recs);
-                               xdl_cha_free(&xdf->rcha);
-                               return -1;
-                       }
+                       if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+                               goto abort;
                        crec->ptr = prev;
                        crec->size = (long) (cur - prev);
                        crec->ha = hav;
                        recs[nrec++] = crec;
 
-                       if (xdl_classify_record(cf, rhash, hbits, crec) < 0) {
-
-                               xdl_free(rhash);
-                               xdl_free(recs);
-                               xdl_cha_free(&xdf->rcha);
-                               return -1;
-                       }
+                       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+                               xdl_classify_record(cf, rhash, hbits, crec) < 0)
+                               goto abort;
                }
        }
 
-       if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) {
-
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
+       if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+               goto abort;
        memset(rchg, 0, (nrec + 2) * sizeof(char));
 
-       if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) {
-
-               xdl_free(rchg);
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
-       if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) {
-
-               xdl_free(rindex);
-               xdl_free(rchg);
-               xdl_free(rhash);
-               xdl_free(recs);
-               xdl_cha_free(&xdf->rcha);
-               return -1;
-       }
+       if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+               goto abort;
+       if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+               goto abort;
 
        xdf->nrec = nrec;
        xdf->recs = recs;
@@ -249,6 +211,15 @@ static int xdl_prepare_ctx(mmfile_t *mf, long narec, xpparam_t const *xpp,
        xdf->dend = nrec - 1;
 
        return 0;
+
+abort:
+       xdl_free(ha);
+       xdl_free(rindex);
+       xdl_free(rchg);
+       xdl_free(rhash);
+       xdl_free(recs);
+       xdl_cha_free(&xdf->rcha);
+       return -1;
 }
 
 
@@ -265,13 +236,23 @@ static void xdl_free_ctx(xdfile_t *xdf) {
 
 int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                    xdfenv_t *xe) {
-       long enl1, enl2;
+       long enl1, enl2, sample;
        xdlclassifier_t cf;
 
-       enl1 = xdl_guess_lines(mf1) + 1;
-       enl2 = xdl_guess_lines(mf2) + 1;
+       /*
+        * For histogram diff, we can afford a smaller sample size and
+        * thus a poorer estimate of the number of lines, as the hash
+        * table (rhash) won't be filled up/grown. The number of lines
+        * (nrecs) will be updated correctly anyway by
+        * xdl_prepare_ctx().
+        */
+       sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+       enl1 = xdl_guess_lines(mf1, sample) + 1;
+       enl2 = xdl_guess_lines(mf2, sample) + 1;
 
-       if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+               xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
 
                return -1;
        }
@@ -288,9 +269,11 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
 
-       xdl_free_classifier(&cf);
+       if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+               xdl_free_classifier(&cf);
 
        if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+                       !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
                        xdl_optimize_ctxs(&xe->xdf1, &xe->xdf2) < 0) {
 
                xdl_free_ctx(&xe->xdf2);
index ab6503460f760356a393a3c7b3b8438213e1ee94..0de084e53f5144153373cc66cae7b523b4ae2812 100644 (file)
 
 
 
-#define XDL_GUESS_NLINES 256
-
-
-
 
 long xdl_bogosqrt(long n) {
        long i;
@@ -71,12 +67,6 @@ void *xdl_mmfile_first(mmfile_t *mmf, long *size)
 }
 
 
-void *xdl_mmfile_next(mmfile_t *mmf, long *size)
-{
-       return NULL;
-}
-
-
 long xdl_mmfile_size(mmfile_t *mmf)
 {
        return mmf->size;
@@ -159,18 +149,12 @@ void *xdl_cha_next(chastore_t *cha) {
 }
 
 
-long xdl_guess_lines(mmfile_t *mf) {
+long xdl_guess_lines(mmfile_t *mf, long sample) {
        long nl = 0, size, tsize = 0;
        char const *data, *cur, *top;
 
        if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
-               for (top = data + size; nl < XDL_GUESS_NLINES;) {
-                       if (cur >= top) {
-                               tsize += (long) (cur - data);
-                               if (!(cur = data = xdl_mmfile_next(mf, &size)))
-                                       break;
-                               top = data + size;
-                       }
+               for (top = data + size; nl < sample && cur < top; ) {
                        nl++;
                        if (!(cur = memchr(cur, '\n', top - cur)))
                                cur = top;
@@ -402,3 +386,34 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
 
        return 0;
 }
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+               int line1, int count1, int line2, int count2)
+{
+       /*
+        * This probably does not work outside Git, since
+        * we have a very simple mmfile structure.
+        *
+        * Note: ideally, we would reuse the prepared environment, but
+        * the libxdiff interface does not (yet) allow for diffing only
+        * ranges of lines instead of the whole files.
+        */
+       mmfile_t subfile1, subfile2;
+       xdfenv_t env;
+
+       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+               diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+               diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+       if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+               return -1;
+
+       memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+       memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+       xdl_free_env(&env);
+
+       return 0;
+}
index d5de8292e05e7c36c4b68857c1cf9855e3d2f70a..714719a89cba9170820bf7d54b9c569d42861aa4 100644 (file)
@@ -33,7 +33,7 @@ void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 void *xdl_cha_first(chastore_t *cha);
 void *xdl_cha_next(chastore_t *cha);
-long xdl_guess_lines(mmfile_t *mf);
+long xdl_guess_lines(mmfile_t *mf, long sample);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);
@@ -41,6 +41,8 @@ int xdl_num_out(char *out, long val);
 long xdl_atol(char const *str, char const **next);
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
                      const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+                      int line1, int count1, int line2, int count2);