Merge branch 'bp/mediawiki-preview'
authorJunio C Hamano <gitster@pobox.com>
Thu, 18 Jul 2013 19:59:34 +0000 (12:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 18 Jul 2013 19:59:34 +0000 (12:59 -0700)
Add a command to allow previewing the contents locally before
pushing it out, when working with a MediaWiki remote.

I personally do not think this belongs to Git. If you are working
on a set of AsciiDoc source files, you sure do want to locally
format to preview what you will be pushing out, and if you are
working on a set of C or Java source files, you do want to test it
before pushing it out, too. That kind of thing belongs to your
build script, not to your SCM.

But I'll let it pass, as this is only a contrib/ thing.

* bp/mediawiki-preview:
git-remote-mediawiki: add preview subcommand into git mw
git-remote-mediawiki: add git-mw command
git-remote-mediawiki: factoring code between git-remote-mediawiki and Git::Mediawiki
git-remote-mediawiki: update tests to run with the new bin-wrapper
git-remote-mediawiki: add a git bin-wrapper for developement
wrap-for-bin: make bin-wrappers chainable
git-remote-mediawiki: introduction of Git::Mediawiki.pm

155 files changed:
.gitignore
.mailmap
Documentation/RelNotes/1.8.3.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.4.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-am.txt
Documentation/git-check-ignore.txt
Documentation/git-clone.txt
Documentation/git-config.txt
Documentation/git-diff.txt
Documentation/git-fast-export.txt
Documentation/git-format-patch.txt
Documentation/git-log.txt
Documentation/git-ls-remote.txt
Documentation/git-merge.txt
Documentation/git-name-rev.txt
Documentation/git-p4.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-rev-parse.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitweb.conf.txt
Documentation/glossary-content.txt
Documentation/rev-list-options.txt
Documentation/technical/api-builtin.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/racy-git.txt
Documentation/urls.txt
Documentation/user-manual.txt
Makefile
builtin/apply.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/help.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/push.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/shortlog.c
builtin/show-branch.c
cache.h
commit-slab.h [new file with mode: 0644]
commit.c
commit.h
configure.ac
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/mw-to-git/git-remote-mediawiki.perl
diff.c
diffcore-pickaxe.c
dir.c
fetch-pack.c
fixup-builtins [deleted file]
git-add--interactive.perl
git-am.sh
git-p4.py
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-send-email.perl
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
gitweb/gitweb.perl
http.c
line-log.c
lockfile.c
log-tree.c
pretty.c
prio-queue.c [new file with mode: 0644]
prio-queue.h [new file with mode: 0644]
read-cache.c
refs.c
refs.h
remote-curl.c
remote.c
revision.c
revision.h
sequencer.c
sha1_name.c
submodule.c
t/README
t/check-non-portable-shell.pl
t/lib-rebase.sh
t/lib-t6000.sh
t/perf/README
t/perf/perf-lib.sh
t/t0000-basic.sh
t/t0009-prio-queue.sh [new file with mode: 0755]
t/t0020-crlf.sh
t/t1512-rev-parse-disambiguation.sh
t/t1513-rev-parse-prefix.sh [new file with mode: 0755]
t/t2012-checkout-last.sh
t/t3010-ls-files-killed-modified.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3900-i18n-commit.sh
t/t3903-stash.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4041-diff-submodule-option.sh
t/t4111-apply-subdir.sh
t/t4205-log-pretty-formats.sh
t/t4211/expect.multiple-superset
t/t5505-remote.sh
t/t5520-pull.sh
t/t5528-push-default.sh
t/t5710-info-alternate.sh
t/t6002-rev-list-bisect.sh
t/t6003-rev-list-topo-order.sh
t/t6006-rev-list-format.sh
t/t7011-skip-worktree-reading.sh
t/t7102-reset.sh
t/t7400-submodule-basic.sh
t/t7401-submodule-summary.sh
t/t7403-submodule-sync.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7500-commit.sh
t/t7508-status.sh
t/t7512-status-help.sh
t/t7600-merge.sh
t/t9001-send-email.sh
t/t9802-git-p4-filetype.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
t/test-lib.sh
t/valgrind/valgrind.sh
test-prio-queue.c [new file with mode: 0644]
transport.c
transport.h
wt-status.c
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xdiffi.h
xdiff/xemit.c
xdiff/xemit.h
xdiff/xutils.c
xdiff/xutils.h
index c0e00eb37bb370ce9deee72468fb419bc25cfd62..efa8db00358772852e637af8758f8f76719dec01 100644 (file)
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-prio-queue
 /test-read-cache
 /test-regex
 /test-revision-walking
index 48d7acf013c611d4885cddffdf516bb5d0ea5a86..345cce698a3bfe1575db0f439d2879d3bf9c05b2 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -24,6 +24,7 @@ Eric S. Raymond <esr@thyrsus.com>
 Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
 Fredrik Kuivinen <freku045@student.liu.se>
 Frédéric Heitzmann <frederic.heitzmann@gmail.com>
+H. Merijn Brand <h.m.brand@xs4all.nl> H.Merijn Brand <h.m.brand@xs4all.nl>
 H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
 H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
 H. Peter Anvin <hpa@trantor.hos.anvin.org>
diff --git a/Documentation/RelNotes/1.8.3.2.txt b/Documentation/RelNotes/1.8.3.2.txt
new file mode 100644 (file)
index 0000000..26ae142
--- /dev/null
@@ -0,0 +1,59 @@
+Git v1.8.3.2 Release Notes
+==========================
+
+Fixes since v1.8.3.1
+--------------------
+
+ * Cloning with "git clone --depth N" while fetch.fsckobjects (or
+   transfer.fsckobjects) is set to true did not tell the cut-off
+   points of the shallow history to the process that validates the
+   objects and the history received, causing the validation to fail.
+
+ * "git checkout foo" DWIMs the intended "upstream" and turns it into
+   "git checkout -t -b foo remotes/origin/foo". This codepath has been
+   updated to correctly take existing remote definitions into account.
+
+ * "git fetch" into a shallow repository from a repository that does
+   not know about the shallow boundary commits (e.g. a different fork
+   from the repository the current shallow repository was cloned from)
+   did not work correctly.
+
+ * "git subtree" (in contrib/) had one codepath with loose error
+   checks to lose data at the remote side.
+
+ * "git log --ancestry-path A...B" did not work as expected, as it did
+   not pay attention to the fact that the merge base between A and B
+   was the bottom of the range being specified.
+
+ * "git diff -c -p" was not showing a deleted line from a hunk when
+   another hunk immediately begins where the earlier one ends.
+
+ * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
+   incorrectly when your previous branch was "frotz" (it should be
+   rewritten to "git merge frotz~22" instead).
+
+ * "git commit --allow-empty-message -m ''" should not start an
+   editor.
+
+ * "git push --[no-]verify" was not documented.
+
+ * An entry for "file://" scheme in the enumeration of URL types Git
+   can take in the HTML documentation was made into a clickable link
+   by mistake.
+
+ * zsh prompt script that borrowed from bash prompt script did not
+   work due to slight differences in array variable notation between
+   these two shells.
+
+ * The bash prompt code (in contrib/) displayed the name of the branch
+   being rebased when "rebase -i/-m/-p" modes are in use, but not the
+   plain vanilla "rebase".
+
+ * "git push $there HEAD:branch" did not resolve HEAD early enough, so
+   it was easy to flip it around while push is still going on and push
+   out a branch that the user did not originally intended when the
+   command was started.
+
+ * "difftool --dir-diff" did not copy back changes made by the
+   end-user in the diff tool backend to the working tree in some
+   cases.
diff --git a/Documentation/RelNotes/1.8.3.3.txt b/Documentation/RelNotes/1.8.3.3.txt
new file mode 100644 (file)
index 0000000..9ba4f4d
--- /dev/null
@@ -0,0 +1,47 @@
+Git v1.8.3.3 Release Notes
+==========================
+
+Fixes since v1.8.3.2
+--------------------
+
+ * "git apply" parsed patches that add new files, generated by programs
+   other than Git, incorrectly.  This is an old breakage in v1.7.11.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index.
+
+ * Many "git submodule" operations did not work on a submodule at a
+   path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in its error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+   "A U. Thor" <author@example.xz>, where the human readable part
+   needs to be quoted (the user input may not have the double quotes
+   around the name, and comparison was done between quoted and
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+   request, mistakenly carrying over the previous search to a new one
+   when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+   renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch",
+   e.g. "git log @{u}", we did not say which branch and worse said
+   "branch ''" in the error messages.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+   bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+   their CommonCrypto, not with OpenSSL.
+
+Also contains various minor documentation updates.
index 69ab64efae71d966337f16853d3f5c9cfb79a17e..ba36cc45879aa143bd26a70df3a607a993ed112e 100644 (file)
@@ -1,11 +1,55 @@
 Git v1.8.4 Release Notes
 ========================
 
+Backward compatibility notes (for Git 2.0)
+------------------------------------------
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there).  In Git 2.0, the default will change to the "simple"
+semantics that pushes:
+
+ - only the current branch to the branch with the same name, and only
+   when the current branch is set to integrate with that remote
+   branch, if you are pushing to the same remote as you fetch from; or
+
+ - only the current branch to the branch with the same name, if you
+   are pushing to a remote that is not where you usually fetch from.
+
+Use the user preference configuration variable "push.default" to
+change this.  If you are an old-timer who is used to the "matching"
+semantics, you can set the variable to "matching" to keep the
+traditional behaviour.  If you want to live in the future early, you
+can set it to "simple" today without waiting for Git 2.0.
+
+When "git add -u" (and "git add -A") is run inside a subdirectory and
+does not specify which paths to add on the command line, it
+will operate on the entire tree in Git 2.0 for consistency
+with "git commit -a" and other commands.  There will be no
+mechanism to make plain "git add -u" behave like "git add -u .".
+Current users of "git add -u" (without a pathspec) should start
+training their fingers to explicitly say "git add -u ."
+before Git 2.0 comes.  A warning is issued when these commands are
+run without a pathspec and when you have local changes outside the
+current directory, because the behaviour in Git 2.0 will be different
+from today's version in such a situation.
+
+In Git 2.0, "git add <path>" will behave as "git add -A <path>", so
+that "git add dir/" will notice paths you removed from the directory
+and record the removal.  Versions before Git 2.0, including this
+release, will keep ignoring removals, but the users who rely on this
+behaviour are encouraged to start using "git add --ignore-removal <path>"
+now before 2.0 is released.
+
+
 Updates since v1.8.3
 --------------------
 
 Foreign interfaces, subsystems and ports.
 
+ * "git rebase -i" now honors --strategy and -X options.
+
  * Git-gui has been updated to its 0.18.0 version.
 
  * MediaWiki remote helper (in contrib/) has been updated to use the
@@ -35,6 +79,54 @@ Foreign interfaces, subsystems and ports.
 
 UI, Workflows & Features
 
+ * In addition to the choice from "rebase, merge, or checkout-detach",
+   "submodule update" can allow a custom command to be used in to
+   update the working tree of submodules via the "submodule.*.update"
+   configuration variable.
+
+ * "git submodule update" can optionally clone the submodule
+   repositories shallowly.
+
+ * "git format-patch" learned "--from[=whom]" option, which sets the
+   "From: " header to the specified person (or the person who runs the
+   command, if "=whom" part is missing) and move the original author
+   information to an in-body From: header as necessary.
+
+ * The configuration variable "merge.ff" was cleary a tri-state to
+   choose one from "favor fast-forward when possible", "always create
+   a merge even when the history could fast-forward" and "do not
+   create any merge, only update when the history fast-forwards", but
+   the command line parser did not implement the usual convention of
+   "last one wins, and command line overrides the configuration"
+   correctly.
+
+ * "gitweb" learned to optionally place extra links that point at the
+   levels higher than the Gitweb pages themselves in the breadcrumbs,
+   so that it can be used as part of a larger installation.
+
+ * "git log --format=" now honors i18n.logoutputencoding configuration
+   variable.
+
+ * The "push.default=simple" mode of "git push" has been updated to
+   behave like "current" without requiring a remote tracking
+   information, when you push to a remote that is different from where
+   you fetch from (i.e. a triangular workflow).
+
+ * Having multiple "fixup!" on a line in the rebase instruction sheet
+   did not work very well with "git rebase -i --autosquash".
+
+ * "git log" learned the "--author-date-order" option, with which the
+   output is topologically sorted and commits in parallel histories
+   are shown intermixed together based on the author timestamp.
+
+ * Various subcommands of "git submodule" refused to run from anywhere
+   other than the top of the working tree of the superproject, but
+   they have been taught to let you run from a subdirectory.
+
+ * "git diff" learned a mode that ignores hunks whose change consists
+   only of additions and removals of blank lines, which is the same as
+   "diff -B" (ignore blank lines) of GNU diff.
+
  * "git rm" gives a single message followed by list of paths to report
    multiple paths that cannot be removed.
 
@@ -45,9 +137,12 @@ UI, Workflows & Features
    thing after you set "user.name/email" to introduce yourselves to
    Git.  Now the variable defaults to "auto".
 
-### * "git status" learned status.branch and status.short configuration
-###   variables to use --branch and --short options by default (override
-###   with --no-branch and --no-short options from the command line).
+ * On Cygwin, "cygstart" is now recognised as a possible way to start
+   a web browser (used in "help -w" and "instaweb" among others).
+
+ * "git status" learned status.branch and status.short configuration
+   variables to use --branch and --short options by default (override
+   with --no-branch and --no-short options from the command line).
 
  * "git cmd <name>", when <name> happens to be a 40-hex string,
    directly uses the 40-hex string as an object name, even if a ref
@@ -105,6 +200,25 @@ UI, Workflows & Features
 
 Performance, Internal Implementation, etc.
 
+ * Fetching between repositories with many refs employed O(n^2)
+   algorithm to match up the common objects, which has been corrected.
+
+ * The original way to specify remote repository using .git/branches/
+   used to have a nifty feature.  The code to support the feature was
+   still in a function but the caller was changed not to call it 5
+   years ago, breaking that feature and leaving the supporting code
+   unreachable.  The dead code has been removed.
+
+ * "git pack-refs" that races with new ref creation or deletion have
+   been susceptible to lossage of refs under right conditions, which
+   has been tightened up.
+
+ * We read loose and packed rerferences in two steps, but after
+   deciding to read a loose ref but before actually opening it to read
+   it, another process racing with us can unlink it, which would cause
+   us to barf.  The codepath has been updated to retry when such a
+   race is detected, instead of outright failing.
+
  * Uses of the platform fnmatch(3) function (many places in the code,
    matching pathspec, .gitignore and .gitattributes to name a few)
    have been replaced with wildmatch, allowing "foo/**/bar" that would
@@ -154,15 +268,64 @@ Unless otherwise noted, all the fixes since v1.8.3 in the maintenance
 track are contained in this release (see release notes to them for
 details).
 
+ * Pass port number as a separate argument when "send-email" initializes
+   Net::SMTP, instead of as a part of the hostname, i.e. host:port.
+   This allows GSSAPI codepath to match with the hostname given.
+   (merge 1a741bf bc/send-email-use-port-as-separate-param later to maint).
+
+ * "git diff" refused to even show difference when core.safecrlf is
+   set to true (i.e. error out) and there are offending lines in the
+   working tree files.
+   (merge 5430bb2 jc/maint-diff-core-safecrlf later to maint).
+
+ * A test that should have failed but didn't revealed a bug that needs
+   to be corrected.
+   (merge 94d75d1 jc/t1512-fix later to maint).
+
+ * An overlong path to a .git directory may have overflown the
+   temporary path buffer used to create a name for lockfiles.
+   (merge 2fbd4f9 mh/maint-lockfile-overflow later to maint).
+
+ * Invocations of "git checkout" used internally by "git rebase" were
+   counted as "checkout", and affected later "git checkout -" to the
+   the user to an unexpected place.
+   (merge 3bed291 rr/rebase-checkout-reflog later to maint).
+
+ * "git stash save", when your local change turns a tracked file into
+   a directory, has to remove files in that directory in order to
+   revert your working tree to a pristine state.  This will lose
+   untracked files in such a directory, and the command now requires
+   you to "--force" it.
+
+ * The configuration variable column.ui was poorly documented.
+   (merge 5e62cc1 rr/column-doc later to maint).
+
+ * "git name-rev --refs=tags/v*" were forbidden, which was a bit
+   inconvenient (you had to give a pattern to match refs fully, like
+   --refs=refs/tags/v*).
+   (merge 98c5c4a nk/name-rev-abbreviated-refs later to maint).
+
+ * "git apply" parsed patches that add new files, generated by
+   programs other than Git, incorrectly.  This is an old breakage in
+   v1.7.11 and will need to be merged down to the maintanance tracks.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index, and this avoids it.
+
+ * Many "git submodule" operations do not work on a submodule at a
+   path whose name is not in ASCII.
+
  * "cherry-pick" had a small leak in an error codepath.
-   (merge 706728a fc/sequencer-plug-leak later to maint).
 
  * Logic used by git-send-email to suppress cc mishandled names like
    "A U. Thor" <author@example.xz>, where the human readable part
    needs to be quoted (the user input may not have the double quotes
    around the name, and comparison was done between quoted and
-   unquoted strings).
-   (merge 1495266 mt/send-email-cc-match-fix later to maint).
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
 
  * Call to discard_cache/discard_index (used when we use different
    contents of the index in-core, in many operations like commit,
@@ -173,102 +336,79 @@ details).
  * "gitweb" forgot to clear a global variable $search_regexp upon each
    request, mistakenly carrying over the previous search to a new one
    when used as a persistent CGI.
-   (merge ca7a5dc cm/gitweb-project-list-persistent-cgi-fix later to maint).
 
  * The wildmatch engine did not honor WM_CASEFOLD option correctly.
-   (merge b79c0c3 ar/wildmatch-foldcase later to maint).
 
  * "git log -c --follow $path" segfaulted upon hitting the commit that
    renamed the $path being followed.
-   (merge 46ec510 cb/log-follow-with-combined later to maint).
 
  * When a reflog notation is used for implicit "current branch", we
    did not say which branch and worse said "branch ''".
-   (merge 305ebea rr/die-on-missing-upstream later to maint).
 
  * "difftool --dir-diff" did not copy back changes made by the
    end-user in the diff tool backend to the working tree in some
    cases.
-   (merge 32eaf1d ks/difftool-dir-diff-copy-fix later to maint).
 
  * "git push $there HEAD:branch" did not resolve HEAD early enough, so
    it was easy to flip it around while push is still going on and push
    out a branch that the user did not originally intended when the
    command was started.
-   (merge 0f075b2 rr/push-head later to maint).
 
  * The bash prompt code (in contrib/) displayed the name of the branch
    being rebased when "rebase -i/-m/-p" modes are in use, but not the
    plain vanilla "rebase".
-   (merge 1306321 fc/show-branch-in-rebase-am later to maint).
 
  * Handling of negative exclude pattern for directories "!dir" was
    broken in the update to v1.8.3.
-   (merge c3c327d kb/status-ignored-optim-2 later to maint).
 
  * zsh prompt script that borrowed from bash prompt script did not
    work due to slight differences in array variable notation between
    these two shells.
-   (merge d0583da tg/maint-zsh-svn-remote-prompt later to maint).
 
  * An entry for "file://" scheme in the enumeration of URL types Git
    can take in the HTML documentation was made into a clickable link
    by mistake.
-   (merge 4c32e36 nd/urls-doc-no-file-hyperlink-fix later to maint).
 
  * "git push --[no-]verify" was not documented.
-   (merge 90d32d1 tr/push-no-verify-doc later to maint).
 
  * Stop installing the git-remote-testpy script that is only used for
    testing.
-   (merge 416fda6 fc/makefile later to maint).
 
  * "git commit --allow-empty-message -m ''" should not start an
    editor.
-   (merge 2520677 rs/commit-m-no-edit later to maint).
 
  * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
    incorrectly when your previous branch was "frotz" (it should be
    rewritten to "git merge frotz~22" instead).
-   (merge 84cf246 jc/strbuf-branchname-fix later to maint).
 
  * "git diff -c -p" was not showing a deleted line from a hunk when
    another hunk immediately begins where the earlier one ends.
-   (merge aac3857 mk/combine-diff-context-horizon-fix later to maint).
 
  * "git log --ancestry-path A...B" did not work as expected, as it did
    not pay attention to the fact that the merge base between A and B
    was the bottom of the range being specified.
-   (merge a765499 kb/ancestry-path-threedots later to maint).
 
  * Mac OS X does not like to write(2) more than INT_MAX number of
    bytes; work it around by chopping write(2) into smaller pieces.
-   (merge 6c642a8 fc/macos-x-clipped-write later to maint).
 
  * Newer MacOS X encourages the programs to compile and link with
    their CommonCrypto, not with OpenSSL.
-   (merge be4c828 da/darwin later to maint).
 
  * "git clone foo/bar:baz" cannot be a request to clone from a remote
    over git-over-ssh specified in the scp style.  This case is now
    detected and clones from a local repository at "foo/bar:baz".
-   (merge 6000334 nd/clone-local-with-colon later to maint).
 
  * When $HOME is misconfigured to point at an unreadable directory, we
    used to complain and die. Loosen the check.
-   (merge 4698c8f jn/config-ignore-inaccessible later to maint).
 
  * "git subtree" (in contrib/) had one codepath with loose error
    checks to lose data at the remote side.
-   (merge 3212d56 jk/subtree-do-not-push-if-split-fails later to maint).
 
  * "git fetch" into a shallow repository from a repository that does
    not know about the shallow boundary commits (e.g. a different fork
    from the repository the current shallow repository was cloned from)
    did not work correctly.
-   (merge 71d5f93 mh/fetch-into-shallow later to maint).
 
  * "git checkout foo" DWIMs the intended "upstream" and turns it into
    "git checkout -t -b foo remotes/origin/foo". This codepath has been
    updated to correctly take existing remote definitions into account.
-   (merge 229177a jh/checkout-auto-tracking later to maint).
index 1153585aa2e2d19dd41f8cf81465e8b6d858a028..81856dd5a2559149b9dcdf263ed5c3212a07d480 100644 (file)
@@ -934,6 +934,9 @@ column.ui::
        This variable consists of a list of tokens separated by spaces
        or commas:
 +
+These options control when the feature should be enabled
+(defaults to 'never'):
++
 --
 `always`;;
        always show in columns
@@ -941,19 +944,30 @@ column.ui::
        never show in columns
 `auto`;;
        show in columns if the output is to the terminal
+--
++
+These options control layout (defaults to 'column').  Setting any
+of these implies 'always' if none of 'always', 'never', or 'auto' are
+specified.
++
+--
 `column`;;
-       fill columns before rows (default)
+       fill columns before rows
 `row`;;
        fill rows before columns
 `plain`;;
        show in one column
+--
++
+Finally, these options can be combined with a layout option (defaults
+to 'nodense'):
++
+--
 `dense`;;
        make unequal size columns to utilize more space
 `nodense`;;
        make equal size columns
 --
-+
-This option defaults to 'never'.
 
 column.branch::
        Specify whether to output branch listing in `git branch` in columns.
@@ -1830,39 +1844,59 @@ pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
 push.default::
-       Defines the action `git push` should take if no refspec is given
-       on the command line, no refspec is configured in the remote, and
-       no refspec is implied by any of the options given on the command
-       line. Possible values are:
+       Defines the action `git push` should take if no refspec is
+       explicitly given.  Different values are well-suited for
+       specific workflows; for instance, in a purely central workflow
+       (i.e. the fetch source is equal to the push destination),
+       `upstream` is probably what you want.  Possible values are:
 +
 --
-* `nothing` - do not push anything.
-* `matching` - push all branches having the same name in both ends.
-  This is for those who prepare all the branches into a publishable
-  shape and then push them out with a single command.  It is not
-  appropriate for pushing into a repository shared by multiple users,
-  since locally stalled branches will attempt a non-fast forward push
-  if other users updated the branch.
-  +
-  This is currently the default, but Git 2.0 will change the default
-  to `simple`.
-* `upstream` - push the current branch to its upstream branch
-  (`tracking` is a deprecated synonym for this).
-  With this, `git push` will update the same remote ref as the one which
-  is merged by `git pull`, making `push` and `pull` symmetrical.
-  See "branch.<name>.merge" for how to configure the upstream branch.
-* `simple` - like `upstream`, but refuses to push if the upstream
-  branch's name is different from the local one. This is the safest
-  option and is well-suited for beginners. It will become the default
-  in Git 2.0.
-* `current` - push the current branch to a branch of the same name.
---
+
+* `nothing` - do not push anything (error out) unless a refspec is
+  explicitly given. This is primarily meant for people who want to
+  avoid mistakes by always being explicit.
+
+* `current` - push the current branch to update a branch with the same
+  name on the receiving end.  Works in both central and non-central
+  workflows.
+
+* `upstream` - push the current branch back to the branch whose
+  changes are usually integrated into the current branch (which is
+  called `@{upstream}`).  This mode only makes sense if you are
+  pushing to the same repository you would normally pull from
+  (i.e. central workflow).
+
+* `simple` - in centralized workflow, work like `upstream` with an
+  added safety to refuse to push if the upstream branch's name is
+  different from the local one.
++
+When pushing to a remote that is different from the remote you normally
+pull from, work as `current`.  This is the safest option and is suited
+for beginners.
++
+This mode will become the default in Git 2.0.
+
+* `matching` - push all branches having the same name on both ends.
+  This makes the repository you are pushing to remember the set of
+  branches that will be pushed out (e.g. if you always push 'maint'
+  and 'master' there and no other branches, the repository you push
+  to will have these two branches, and your local 'maint' and
+  'master' will be pushed there).
 +
-The `simple`, `current` and `upstream` modes are for those who want to
-push out a single branch after finishing work, even when the other
-branches are not yet ready to be pushed out. If you are working with
-other people to push into the same shared repository, you would want
-to use one of these.
+To use this mode effectively, you have to make sure _all_ the
+branches you would push out are ready to be pushed out before
+running 'git push', as the whole point of this mode is to allow you
+to push all of the branches in one go.  If you usually finish work
+on only one branch and push out the result, while other branches are
+unfinished, this mode is not for you.  Also this mode is not
+suitable for pushing into a shared central repository, as other
+people may add new branches there, or update the tip of existing
+branches outside your control.
++
+This is currently the default, but Git 2.0 will change the default
+to `simple`.
+
+--
 
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
@@ -2078,6 +2112,14 @@ status.relativePaths::
        relative to the repository root (this was the default for Git
        prior to v1.5.4).
 
+status.short::
+       Set to true to enable --short by default in linkgit:git-status[1].
+       The option --no-short takes precedence over this variable.
+
+status.branch::
+       Set to true to enable --branch by default in linkgit:git-status[1].
+       The option --no-branch takes precedence over this variable.
+
 status.showUntrackedFiles::
        By default, linkgit:git-status[1] and linkgit:git-commit[1] show
        files which are not currently tracked by Git. Directories which
index a85288f23ef13512042a783460c5dfcd347b83f1..87e92d6537a097a172d37b5893dd4c8c092a2368 100644 (file)
@@ -333,7 +333,7 @@ endif::git-log[]
        a fraction, with a decimal point before it.  I.e., `-M5` becomes
        0.5, and is thus the same as `-M50%`.  Similarly, `-M05` is
        the same as `-M5%`.  To limit detection to exact renames, use
-       `-M100%`.
+       `-M100%`.  The default similarity index is 50%.
 
 -C[<n>]::
 --find-copies[=<n>]::
@@ -461,6 +461,9 @@ endif::git-format-patch[]
        differences even if one line has whitespace where the other
        line has none.
 
+--ignore-blank-lines::
+       Ignore changes whose lines are all blank.
+
 --inter-hunk-context=<lines>::
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
index 5bbe7b6d10de652658871048329c5dd60f1e12e0..54d8461d61b2f2ad5dbf5ce8c9106534d1a74289 100644 (file)
@@ -132,7 +132,7 @@ default.   You can use `--no-utf8` to override this.
 --resolvemsg=<msg>::
        When a patch failure occurs, <msg> will be printed
        to the screen before exiting.  This overrides the
-       standard message informing you to use `--resolved`
+       standard message informing you to use `--continue`
        or `--skip` to handle the failure.  This is solely
        for internal use between 'git rebase' and 'git am'.
 
@@ -176,7 +176,7 @@ aborts in the middle.  You can recover from this in one of two ways:
 
 . hand resolve the conflict in the working directory, and update
   the index file to bring it into a state that the patch should
-  have produced.  Then run the command with the '--resolved' option.
+  have produced.  Then run the command with the '--continue' option.
 
 The command refuses to process new mailboxes until the current
 operation is finished, so if you decide to start over from scratch,
index 8e1f7ab7ea93af4b7c496efc2251469ed553a55f..d2df487aa20a89264afee08bcdddf681d7701fda 100644 (file)
@@ -102,7 +102,7 @@ SEE ALSO
 --------
 linkgit:gitignore[5]
 linkgit:gitconfig[5]
-linkgit:git-ls-files[5]
+linkgit:git-ls-files[1]
 
 GIT
 ---
index a0727d7759aefef4dede0fb40008b7b1ee957b2d..85769b884e0f018c68fc0393d77b167a09d3ef44 100644 (file)
@@ -182,11 +182,13 @@ objects from the source repository into a pack in the cloned repository.
 --depth <depth>::
        Create a 'shallow' clone with a history truncated to the
        specified number of revisions.  A shallow repository has a
-       number of limitations (you cannot clone or fetch from
-       it, nor push from nor into it), but is adequate if you
-       are only interested in the recent history of a large project
-       with a long history, and would want to send in fixes
-       as patches.
+       number of limitations (you cannot clone or fetch from it, nor
+       push into it), but is adequate if you are only interested in
+       the recent history of a large project with a long history.
++
+Pushing from a shallow clone should be avoided if the git version on
+the receiver end is older than v1.7.10, or any other git
+implementation that does not perform connectivity check.
 
 --[no-]single-branch::
        Clone only the history leading to the tip of a single branch,
@@ -239,8 +241,8 @@ Examples
 * Clone from upstream:
 +
 ------------
-$ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
-$ cd my2.6
+$ git clone git://git.kernel.org/pub/scm/.../linux.git my-linux
+$ cd my-linux
 $ make
 ------------
 
@@ -257,10 +259,10 @@ $ git show-branch
 * Clone from upstream while borrowing from an existing local directory:
 +
 ------------
-$ git clone --reference my2.6 \
-       git://git.kernel.org/pub/scm/.../linux-2.7 \
-       my2.7
-$ cd my2.7
+$ git clone --reference /git/linux.git \
+       git://git.kernel.org/pub/scm/.../linux.git \
+       my-linux
+$ cd my-linux
 ------------
 
 
@@ -271,13 +273,6 @@ $ git clone --bare -l /home/proj/.git /pub/scm/proj.git
 ------------
 
 
-* Create a repository on the kernel.org machine that borrows from Linus:
-+
-------------
-$ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
-    /pub/scm/.../me/subsys-2.6.git
-------------
-
 GIT
 ---
 Part of the linkgit:git[1] suite
index 19a7be085693f7f9386fd3856fa38ebeed364f99..99dc497b6f25243dbe01ce47d6e53ec6952d1737 100644 (file)
@@ -82,7 +82,7 @@ OPTIONS
 --get::
        Get the value for a given key (optionally filtered by a regex
        matching the value). Returns error code 1 if the key was not
-       found and error code 2 if multiple key values were found.
+       found and the last value if multiple key values were found.
 
 --get-all::
        Like get, but does not fail if the number of values for the key
@@ -206,12 +206,8 @@ FILES
 If not set explicitly with '--file', there are four files where
 'git config' will search for configuration options:
 
-$GIT_DIR/config::
-       Repository specific configuration file.
-
-~/.gitconfig::
-       User-specific configuration file. Also called "global"
-       configuration file.
+$(prefix)/etc/gitconfig::
+       System-wide configuration file.
 
 $XDG_CONFIG_HOME/git/config::
        Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
@@ -221,8 +217,12 @@ $XDG_CONFIG_HOME/git/config::
        you sometimes use older versions of Git, as support for this
        file was added fairly recently.
 
-$(prefix)/etc/gitconfig::
-       System-wide configuration file.
+~/.gitconfig::
+       User-specific configuration file. Also called "global"
+       configuration file.
+
+$GIT_DIR/config::
+       Repository specific configuration file.
 
 If no further options are given, all reading options will read all of these
 files that are available. If the global or the system-wide configuration
@@ -230,6 +230,10 @@ file are not available they will be ignored. If the repository configuration
 file is not available or readable, 'git config' will exit with a non-zero
 error code. However, in neither case will an error message be issued.
 
+The files are read in the order given above, with last value found taking
+precedence over values read earlier.  When multiple values are taken then all
+values of a key from all files will be used.
+
 All writing options will per default write to the repository specific
 configuration file. Note that this also affects options like '--replace-all'
 and '--unset'. *'git config' will only ever change one file at a time*.
index a7b46208f62c81b63ef4b8fdc5cf3ac67bd8b541..78d6d50489502638013509fbf72c811b50bfee86 100644 (file)
@@ -18,8 +18,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Show changes between the working tree and the index or a tree, changes
-between the index and a tree, changes between two trees, or changes
-between two files on disk.
+between the index and a tree, changes between two trees, changes between
+two blob objects, or changes between two files on disk.
 
 'git diff' [--options] [--] [<path>...]::
 
@@ -56,11 +56,6 @@ directories. This behavior can be forced by --no-index.
        This is to view the changes between two arbitrary
        <commit>.
 
-'git diff' [options] <blob> <blob>::
-
-       This form is to view the differences between the raw
-       contents of two blob objects.
-
 'git diff' [--options] <commit>..<commit> [--] [<path>...]::
 
        This is synonymous to the previous form.  If <commit> on
@@ -87,6 +82,11 @@ and the range notations ("<commit>..<commit>" and
 "<commit>\...<commit>") do not mean a range as defined in the
 "SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
+'git diff' [options] <blob> <blob>::
+
+       This form is to view the differences between the raw
+       contents of two blob objects.
+
 OPTIONS
 -------
 :git-diff: 1
index efb03806f507789877219f87217841d6deb5cd26..85f1f30fdf5c0a741c3c575bfa393fc7d88d308f 100644 (file)
@@ -141,7 +141,7 @@ Limitations
 -----------
 
 Since 'git fast-import' cannot tag trees, you will not be
-able to export the linux-2.6.git repository completely, as it contains
+able to export the linux.git repository completely, as it contains
 a tag referencing a tree instead of a commit.
 
 GIT
index 39118774afbbd730e4464f44e24a05af91432c30..e394276b1a2e2edfbbf8ab1dea9fcd30ca66e61f 100644 (file)
@@ -187,6 +187,21 @@ will want to ensure that threading is disabled for `git send-email`.
        The negated form `--no-cc` discards all `Cc:` headers added so
        far (from config or command line).
 
+--from::
+--from=<ident>::
+       Use `ident` in the `From:` header of each commit email. If the
+       author ident of the commit is not textually identical to the
+       provided `ident`, place a `From:` header in the body of the
+       message with the original author. If no `ident` is given, use
+       the committer ident.
++
+Note that this option is only useful if you are actually sending the
+emails and want to identify yourself as the sender, but retain the
+original author (and `git am` will correctly pick up the in-body
+header). Note also that `git send-email` already handles this
+transformation for you, and this option should not be used if you are
+feeding the result to `git send-email`.
+
 --add-header=<header>::
        Add an arbitrary header to the email headers.  This is in addition
        to any configured headers, and may be used multiple times.
index 4687fe8192594b358f7058dcdec985ab30c2a3fb..2ee6962a720f9fb252ae4524b6748857c36bc2a7 100644 (file)
@@ -128,9 +128,9 @@ Examples
        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
+       Shows the commits that changed builtin/rev-list.c, including
        those commits that occurred before the file was given its
        present name.
 
@@ -153,7 +153,7 @@ Examples
        This makes sense only when following a strict policy of merging all
        topic branches when staying on a single integration branch.
 
-git log -L '/int main/',/^}/:main.c::
+`git log -L '/int main/',/^}/:main.c`::
 
        Shows how the function `main()` in the file 'main.c' evolved
        over time.
index 774de5e9d9fc2d4d88e4f820be177ea6c0a09360..2e22915eb857bd960f09afc725107198642bfd22 100644 (file)
@@ -48,9 +48,9 @@ OPTIONS
        exit without talking to the remote.
 
 <repository>::
-       Location of the repository.  The shorthand defined in
-       $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
-       the local repository.
+       The "remote" repository to query.  This parameter can be
+       either a URL or the name of a remote (see the GIT URLS and
+       REMOTES sections of linkgit:git-fetch[1]).
 
 <refs>...::
        When unspecified, all references, after filtering done
@@ -70,9 +70,8 @@ EXAMPLES
        $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
        5fe978a5381f1fbad26a80e682ddd2a401966740        refs/heads/master
        c781a84b5204fb294c9ccc79f8b3baceeb32c061        refs/heads/pu
-       b1d096f2926c4e37c9c0b6a7bf2119bedaa277cb        refs/heads/rc
-       $ echo http://www.kernel.org/pub/scm/git/git.git >.git/branches/public
-       $ git ls-remote --tags public v\*
+       $ git remote add korg http://www.kernel.org/pub/scm/git/git.git
+       $ git ls-remote --tags korg v\*
        d6602ec5194c87b0fc87103ca4d67251c76f233a        refs/tags/v0.99
        f25a265a342aed6041ab0cc484224d9ca54b6f41        refs/tags/v0.99.1
        c5db5456ae3b0873fc659c19fafdde22313cc441        refs/tags/v0.99.2
index 67ca99cd92e371ae7518d4574e2a8507d78e0823..8c7f2f66d86e5d41113d6158b496068cc6b72bce 100644 (file)
@@ -56,8 +56,8 @@ especially if those changes were further modified after the merge
 was started), 'git merge --abort' will in some cases be unable to
 reconstruct the original (pre-merge) changes. Therefore:
 
-*Warning*: Running 'git merge' with uncommitted changes is
-discouraged: while possible, it leaves you in a state that is hard to
+*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
 
index ad1d1468c9936d206701d379003177e7457c884e..15b00e09916fff47a8244432ce396c9b6f7fbec1 100644 (file)
@@ -25,14 +25,17 @@ OPTIONS
        Do not use branch names, but only tags to name the commits
 
 --refs=<pattern>::
-       Only use refs whose names match a given shell pattern.
+       Only use refs whose names match a given shell pattern.  The pattern
+       can be one of branch name, tag name or fully qualified ref name.
 
 --all::
        List all commits reachable from all refs
 
 --stdin::
-       Read from stdin, append "(<rev_name>)" to all sha1's of nameable
-       commits, and pass to stdout
+       Transform stdin by substituting all the 40-character SHA-1
+       hexes (say $hex) with "$hex ($rev_name)".  When used with
+       --name-only, substitute with "$rev_name", omitting $hex
+       altogether.  Intended for the scripter's use.
 
 --name-only::
        Instead of printing both the SHA-1 and the name, print only
index c579fbc2b858d0a6fb5cd8dcbcaa25d3b3fca2a5..8cba16d67f29ec48738b00a607add4a20bc7d4b2 100644 (file)
@@ -176,13 +176,16 @@ Sync options
 These options can be used in the initial 'clone' as well as in
 subsequent 'sync' operations.
 
---branch <branch>::
-       Import changes into given branch.  If the branch starts with
-       'refs/', it will be used as is.  Otherwise if it does not start
-       with 'p4/', that prefix is added.  The branch is assumed to
-       name a remote tracking, but this can be modified using
-       '--import-local', or by giving a full ref name.  The default
-       branch is 'master'.
+--branch <ref>::
+       Import changes into <ref> instead of refs/remotes/p4/master.
+       If <ref> starts with refs/, it is used as is.  Otherwise, if
+       it does not start with p4/, that prefix is added.
++
+By default a <ref> not starting with refs/ is treated as the
+name of a remote-tracking branch (under refs/remotes/).  This
+behavior can be modified using the --import-local option.
++
+The default <ref> is "master".
 +
 This example imports a new remote "p4/proj2" into an existing
 Git repository:
index 24ab07a3f8f80c56cbbd7feffc1b5bf65e137968..6ef8d599d31ac20982fa74f213b81494ceae162a 100644 (file)
@@ -3,7 +3,7 @@ git-pull(1)
 
 NAME
 ----
-git-pull - Fetch from and merge with another repository or a local branch
+git-pull - Fetch from and integrate with another repository or a local branch
 
 
 SYNOPSIS
index df5be268ba69f34fe5e91c3148f8af8c1e0091d6..f7dfe48d2813324e1a699efa530afe8974d7c400 100644 (file)
@@ -136,6 +136,15 @@ already exists on the remote side.
        not an ancestor of the local ref used to overwrite it.
        This flag disables the check.  This can cause the
        remote repository to lose commits; use it with care.
+       Note that `--force` applies to all the refs that are pushed,
+       hence using it with `push.default` set to `matching` or with
+       multiple push destinations configured with `remote.*.push`
+       may overwrite refs other than the current branch (including
+       local refs that are strictly behind their remote counterpart).
+       To force a push to only one branch, use a `+` in front of the
+       refspec to push (e.g `git push origin +master` to force a push
+       to the `master` branch). See the `<refspec>...` section above
+       for details.
 
 --repo=<repository>::
        This option is only relevant if no <repository> argument is
index c84854ae87df094683600c559e88c13a8b770e01..6b2e1c86ab6c1084421e06b5f677264dc4099b6a 100644 (file)
@@ -389,7 +389,9 @@ squash/fixup series.
        the same ..., automatically modify the todo list of rebase -i
        so that the commit marked for squashing comes right after the
        commit to be modified, and change the action of the moved
-       commit from `pick` to `squash` (or `fixup`).
+       commit from `pick` to `squash` (or `fixup`).  Ignores subsequent
+       "fixup! " or "squash! " after the first, in case you referred to an
+       earlier fixup/squash with `git commit --fixup/--squash`.
 +
 This option is only valid when the '--interactive' option is used.
 +
index 581bb4c413fb70956430bb7359bbee3bbc1a24e6..9c3e3bf83a2721678a2c7a430ec88bee0d39dd0a 100644 (file)
@@ -187,18 +187,25 @@ Examples
 $ git remote
 origin
 $ git branch -r
-origin/master
-$ git remote add linux-nfs git://linux-nfs.org/pub/linux/nfs-2.6.git
+  origin/HEAD -> origin/master
+  origin/master
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
 $ git remote
-linux-nfs
 origin
-$ git fetch
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+staging
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 $ git branch -r
-origin/master
-linux-nfs/master
-$ git checkout -b nfs linux-nfs/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
+$ git checkout -b staging staging/master
 ...
 ------------
 
index 947d62fd25e4b326af44a6f6933e98d9cc3e7153..993903c9f123831fdfb25c8e22e213fd6c6a7c96 100644 (file)
@@ -59,6 +59,22 @@ OPTIONS
        If there is no parameter given by the user, use `<arg>`
        instead.
 
+--prefix <arg>::
+       Behave as if 'git rev-parse' was invoked from the `<arg>`
+       subdirectory of the working tree.  Any relative filenames are
+       resolved as if they are prefixed by `<arg>` and will be printed
+       in that form.
++
+This can be used to convert arguments to a command run in a subdirectory
+so that they can still be used after moving to the top-level of the
+repository.  For example:
++
+----
+prefix=$(git rev-parse --show-prefix)
+cd "$(git rev-parse --show-toplevel)"
+eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+----
+
 --verify::
        Verify that exactly one parameter is provided, and that it
        can be turned into a raw 20-byte SHA-1 that can be used to
index 711ffe17a7574511de6c20cb85b6cdd5a4fff5a5..7c8b648fbe8f76d523f781699447586c838b12fc 100644 (file)
@@ -13,10 +13,12 @@ SYNOPSIS
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-            [-u|--include-untracked] [-a|--all] [<message>]]
+'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+            [-u|--include-untracked] [-a|--all] [-f|--force]
+            [<message>]]
 'git stash' clear
-'git stash' create
+'git stash' create [<message>]
+'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
 
 DESCRIPTION
 -----------
@@ -43,7 +45,7 @@ is also possible).
 OPTIONS
 -------
 
-save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+save [-p|--patch] [--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-f|--force] [<message>]::
 
        Save your local modifications to a new 'stash', and run `git reset
        --hard` to revert them.  The <message> part is optional and gives
@@ -70,6 +72,13 @@ linkgit:git-add[1] to learn how to operate the `--patch` mode.
 +
 The `--patch` option implies `--keep-index`.  You can use
 `--no-keep-index` to override this.
++
+In some cases, saving a stash could mean irretrievably removing some
+data - if a directory with untracked files replaces a tracked file of
+the same name, the new untracked files are not saved (except in case
+of `--include-untracked`) but the original tracked file shall be restored.
+By default, `stash save` will abort in such a case; `--force` will allow
+it to remove the untracked files.
 
 list [<options>]::
 
@@ -151,7 +160,15 @@ create::
 
        Create a stash (which is a regular commit object) and return its
        object name, without storing it anywhere in the ref namespace.
+       This is intended to be useful for scripts.  It is probably not
+       the command you want to use; see "save" above.
+
+store::
 
+       Store a given stash created via 'git stash create' (which is a
+       dangling merge commit) in the stash ref, updating the stash
+       reflog.  This is intended to be useful for scripts.  It is
+       probably not the command you want to use; see "save" above.
 
 DISCUSSION
 ----------
index e5767134b1a2a84c284e87c900948e4530b25576..bfef8a0c62a56865cfe2335841efcb4535642287 100644 (file)
@@ -10,12 +10,12 @@ SYNOPSIS
 --------
 [verse]
 'git submodule' [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
-             [--reference <repository>] [--] <repository> [<path>]
+             [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
-             [-f|--force] [--rebase] [--reference <repository>]
+             [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>]
              [--merge] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
@@ -159,7 +159,9 @@ update::
        This will make the submodules HEAD be detached unless `--rebase` or
        `--merge` is specified or the key `submodule.$name.update` is set to
        `rebase`, `merge` or `none`. `none` can be overridden by specifying
-       `--checkout`.
+       `--checkout`. Setting the key `submodule.$name.update` to `!command`
+       will cause `command` to be run. `command` can be any arbitrary shell
+       command that takes a single argument, namely the sha1 to update to.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -262,7 +264,7 @@ OPTIONS
 --remote::
        This option is only valid for the update command.  Instead of using
        the superproject's recorded SHA-1 to update the submodule, use the
-       status of the submodule's remote tracking branch.  The remote used
+       status of the submodule's remote-tracking branch.  The remote used
        is branch's remote (`branch.<name>.remote`), defaulting to `origin`.
        The remote branch used defaults to `master`, but the branch name may
        be overridden by setting the `submodule.<name>.branch` option in
@@ -328,6 +330,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
        only in the submodules of the current repo, but also
        in any nested submodules inside those submodules (and so on).
 
+--depth::
+       This option is valid for add and update commands. Create a 'shallow'
+       clone with a history truncated to the specified number of revisions.
+       See linkgit:git-clone[1]
+
+
 <path>...::
        Paths to submodule(s). When specified this will restrict the command
        to only operate on the submodules found at the specified paths.
index ba79cb4f35a1f525c98c0bc6b95acf4c46ddc1dc..5aec4ecffb8c9dd58c444319d24064c8fee1770e 100644 (file)
@@ -34,6 +34,7 @@ The following browsers (or commands) are currently supported:
 * dillo
 * open (this is the default under Mac OS X GUI)
 * start (this is the default under MinGW)
+* cygstart (this is the default under Cygwin)
 
 Custom commands may also be specified.
 
index 894454609fab8f3773afff7e1e3fc4aadfb825f0..e9b4922110012eec5b6d5874b81685261565bdd1 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of Git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.3.1/git.html[documentation for release 1.8.3.1]
+* link:v1.8.3.3/git.html[documentation for release 1.8.3.3]
 
 * release notes for
+  link:RelNotes/1.8.3.3.txt[1.8.3.3],
+  link:RelNotes/1.8.3.2.txt[1.8.3.2],
   link:RelNotes/1.8.3.1.txt[1.8.3.1],
   link:RelNotes/1.8.3.txt[1.8.3].
 
index ea0526ecc4019802b7088316cba9aed401fa6ee8..305db633ccb1165e819a1061e8ec8b60e33e9eed 100644 (file)
@@ -336,8 +336,26 @@ $home_link_str::
        used as the first component of gitweb's "breadcrumb trail":
        `<home link> / <project> / <action>`.  Can be set at build time using
        the `GITWEB_HOME_LINK_STR` variable.  By default it is set to "projects",
-       as this link leads to the list of projects.  Other popular choice it to
-       set it to the name of site.
+       as this link leads to the list of projects.  Another popular choice is to
+       set it to the name of site.  Note that it is treated as raw HTML so it
+       should not be set from untrusted sources.
+
+@extra_breadcrumbs::
+       Additional links to be added to the start of the breadcrumb trail before
+       the home link, to pages that are logically "above" the gitweb projects
+       list, such as the organization and department which host the gitweb
+       server. Each element of the list is a reference to an array, in which
+       element 0 is the link text (equivalent to `$home_link_str`) and element
+       1 is the target URL (equivalent to `$home_link`).
++
+For example, the following setting produces a breadcrumb trail like
+"home / dev / projects / ..." where "projects" is the home link.
+----------------------------------------------------------------------------
+    our @extra_breadcrumbs = (
+      [ 'home' => 'https://www.example.org/' ],
+      [ 'dev'  => 'https://dev.example.org/' ],
+    );
+----------------------------------------------------------------------------
 
 $logo_url::
 $logo_label::
index db2a74df934f3acd93521e42510b6cd00c9eed6f..dba5062b379cdfcdde31ee0f6809e964f979b5f5 100644 (file)
@@ -113,7 +113,7 @@ Note that commands that operate on the history of the current branch
 while the HEAD is detached. They update the HEAD to point at the tip
 of the updated history without affecting any branch.  Commands that
 update or inquire information _about_ the current branch (e.g. `git
-branch --set-upstream-to` that sets what remote tracking branch the
+branch --set-upstream-to` that sets what remote-tracking branch the
 current branch integrates with) obviously do not work, as there is no
 (real) current branch to ask about in this state.
 
@@ -267,7 +267,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        The default upstream <<def_repository,repository>>. Most projects have
        at least one upstream project which they track. By default
        'origin' is used for that purpose. New upstream updates
-       will be fetched into remote <<def_remote_tracking_branch,remote-tracking branches>> named
+       will be fetched into <<def_remote_tracking_branch,remote-tracking branches>> named
        origin/name-of-upstream-branch, which you can see using
        `git branch -r`.
 
index b462f17f62d4c0066b1178b405b011963643cdd9..e157ec3fe7e6bc72e271337f6cb0b4c1d791a2eb 100644 (file)
@@ -625,6 +625,10 @@ By default, the commits are shown in reverse chronological order.
        Show no parents before all of its children are shown, but
        otherwise show commits in the commit timestamp order.
 
+--author-date-order::
+       Show no parents before all of its children are shown, but
+       otherwise show commits in the author timestamp order.
+
 --topo-order::
        Show no parents before all of its children are shown, and
        avoid showing commits on multiple lines of history
index 4a4228b896df1c06b565f330b6d7eb9d2cd9b10c..f3c1357b7cc037681b5d0f28974b199e326f8c2d 100644 (file)
@@ -39,7 +39,7 @@ where options is the bitwise-or of:
        on bare repositories.
        This only makes sense when `RUN_SETUP` is also set.
 
-. Add `builtin-foo.o` to `BUILTIN_OBJS` in `Makefile`.
+. Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
 
 Additionally, if `foo` is a new command, there are 3 more things to do:
 
index 1317db4d6ca798c0ffa5150a7373c37b5599ecd9..0be2b5159f1b349f0c263eb9e8ba696e2a57ebc0 100644 (file)
@@ -275,10 +275,10 @@ Examples
 --------
 
 See `test-parse-options.c` and
-`builtin-add.c`,
-`builtin-clone.c`,
-`builtin-commit.c`,
-`builtin-fetch.c`,
-`builtin-fsck.c`,
-`builtin-rm.c`
+`builtin/add.c`,
+`builtin/clone.c`,
+`builtin/commit.c`,
+`builtin/fetch.c`,
+`builtin/fsck.c`,
+`builtin/rm.c`
 for real-world examples.
index 6dc82ca5a8a8b744fad1aad6ee7578c3b02495d1..f716d6d97f1cbb66b36a1e06ec2c268abf0a5ed1 100644 (file)
@@ -135,9 +135,9 @@ them, and give the same timestamp to the index file:
   $ git ls-files | git update-index --stdin
   $ touch -r .datestamp .git/index
 
-This will make all index entries racily clean.  The linux-2.6
-project, for example, there are over 20,000 files in the working
-tree.  On my Athlon 64 X2 3800+, after the above:
+This will make all index entries racily clean.  The linux project, for
+example, there are over 20,000 files in the working tree.  On my
+Athlon 64 X2 3800+, after the above:
 
   $ /usr/bin/time git diff-files
   1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
index 5e185f87c20a2124a7fb9bf2046f6035942b4f92..9ccb24677e95b8aff605c9020bc7a0537e23f751 100644 (file)
@@ -11,6 +11,9 @@ and ftps can be used for fetching and rsync can be used for fetching
 and pushing, but these are inefficient and deprecated; do not use
 them).
 
+The native transport (i.e. git:// URL) does no authentication and
+should be used with caution on unsecured networks.
+
 The following syntaxes may be used with them:
 
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
index e831cc2020b2b728d972c9e33f7fef8041381547..e364007d7c2e5f9b1aa5472c7968cb248eb8cf3f 100644 (file)
@@ -57,17 +57,17 @@ download a copy of an existing repository.  If you don't already have a
 project in mind, here are some interesting examples:
 
 ------------------------------------------------
-       # Git itself (approx. 10MB download):
+       # Git itself (approx. 40MB download):
 $ git clone git://git.kernel.org/pub/scm/git/git.git
-       # the Linux kernel (approx. 150MB download):
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+       # the Linux kernel (approx. 640MB download):
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
 ------------------------------------------------
 
 The initial clone may be time-consuming for a large project, but you
 will only need to clone once.
 
-The clone command creates a new directory named after the project (`git`
-or `linux-2.6` in the examples above).  After you cd into this
+The clone command creates a new directory named after the project
+(`git` or `linux` in the examples above).  After you cd into this
 directory, you will see that it contains a copy of the project files,
 called the <<def_working_tree,working tree>>, together with a special
 top-level directory named `.git`, which contains all the information
@@ -431,19 +431,25 @@ You can also track branches from repositories other than the one you
 cloned from, using linkgit:git-remote[1]:
 
 -------------------------------------------------
-$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
-$ git fetch linux-nfs
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 -------------------------------------------------
 
 New remote-tracking branches will be stored under the shorthand name
-that you gave `git remote add`, in this case `linux-nfs`:
+that you gave `git remote add`, in this case `staging`:
 
 -------------------------------------------------
 $ git branch -r
-linux-nfs/master
-origin/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
 -------------------------------------------------
 
 If you run `git fetch <remote>` later, the remote-tracking branches
@@ -455,9 +461,9 @@ a new stanza:
 -------------------------------------------------
 $ cat .git/config
 ...
-[remote "linux-nfs"]
-       url = git://linux-nfs.org/pub/nfs-2.6.git
-       fetch = +refs/heads/*:refs/remotes/linux-nfs/*
+[remote "staging"]
+       url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+       fetch = +refs/heads/*:refs/remotes/staging/*
 ...
 -------------------------------------------------
 
@@ -1835,7 +1841,7 @@ Once the index is updated with the results of the conflict
 resolution, instead of creating a new commit, just run
 
 -------------------------------------------------
-$ git am --resolved
+$ git am --continue
 -------------------------------------------------
 
 and Git will create the commit for you and continue applying the
@@ -2156,7 +2162,7 @@ To set this up, first create your work tree by cloning Linus's public
 tree:
 
 -------------------------------------------------
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work
 $ cd work
 -------------------------------------------------
 
@@ -2198,7 +2204,7 @@ make it easy to push both branches to your public tree.  (See
 -------------------------------------------------
 $ cat >> .git/config <<EOF
 [remote "mytree"]
-       url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
+       url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git
        push = release
        push = test
 EOF
@@ -4256,15 +4262,16 @@ no longer need to call `setup_pager()` directly).
 Nowadays, `git log` is a builtin, which means that it is _contained_ in the
 command `git`.  The source side of a builtin is
 
-- a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
-  and declared in `builtin.h`,
+- a function called `cmd_<bla>`, typically defined in `builtin/<bla.c>`
+  (note that older versions of Git used to have it in `builtin-<bla>.c`
+  instead), and declared in `builtin.h`.
 
 - an entry in the `commands[]` array in `git.c`, and
 
 - an entry in `BUILTIN_OBJECTS` in the `Makefile`.
 
 Sometimes, more than one builtin is contained in one source file.  For
-example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin-log.c`,
+example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin/log.c`,
 since they share quite a bit of code.  In that case, the commands which are
 _not_ named like the `.c` file in which they live have to be listed in
 `BUILT_INS` in the `Makefile`.
@@ -4287,10 +4294,10 @@ For the sake of clarity, let's stay with `git cat-file`, because it
 - is plumbing, and
 
 - was around even in the initial commit (it literally went only through
-  some 20 revisions as `cat-file.c`, was renamed to `builtin-cat-file.c`
+  some 20 revisions as `cat-file.c`, was renamed to `builtin/cat-file.c`
   when made a builtin, and then saw less than 10 versions).
 
-So, look into `builtin-cat-file.c`, search for `cmd_cat_file()` and look what
+So, look into `builtin/cat-file.c`, search for `cmd_cat_file()` and look what
 it does.
 
 ------------------------------------------------------------------
@@ -4366,7 +4373,7 @@ Another example: Find out what to do in order to make some script a
 builtin:
 
 -------------------------------------------------
-$ git log --no-merges --diff-filter=A builtin-*.c
+$ git log --no-merges --diff-filter=A builtin/*.c
 -------------------------------------------------
 
 You see, Git is actually the best tool to find out about the source of Git
index e1583761dfa58c3ede93b2076e4aa0963f2aa379..0600eb46db487c88e24dc3b4089db062ad6350ac 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -569,6 +569,7 @@ TEST_PROGRAMS_NEED_X += test-mergesort
 TEST_PROGRAMS_NEED_X += test-mktemp
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-prio-queue
 TEST_PROGRAMS_NEED_X += test-read-cache
 TEST_PROGRAMS_NEED_X += test-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
@@ -705,6 +706,7 @@ LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pathspec.h
 LIB_H += pkt-line.h
+LIB_H += prio-queue.h
 LIB_H += progress.h
 LIB_H += prompt.h
 LIB_H += quote.h
@@ -846,6 +848,7 @@ LIB_OBJS += pathspec.o
 LIB_OBJS += pkt-line.o
 LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
+LIB_OBJS += prio-queue.o
 LIB_OBJS += progress.o
 LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
@@ -2302,9 +2305,6 @@ check: common-cmds.h
                exit 1; \
        fi
 
-remove-dashes:
-       ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
-
 ### Installation rules
 
 ifneq ($(filter /%,$(firstword $(template_dir))),)
index faf8e3088340ab2787c45b0e105a600fd54e51d3..0e9b631db6db422b1e938d6168b608e0a2ae8618 100644 (file)
@@ -906,7 +906,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                        patch->old_name = name;
                } else {
                        patch->old_name = name;
-                       patch->new_name = xstrdup(name);
+                       patch->new_name = null_strdup(name);
                }
        }
        if (!name)
index f5b50e520feb42c50cd1783871eb63d009e0a0e0..7fe0bffaf8b875b40b5e4e0e5c207f978c932691 100644 (file)
@@ -587,7 +587,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                                   struct branch_info *new)
 {
        struct strbuf msg = STRBUF_INIT;
-       const char *old_desc;
+       const char *old_desc, *reflog_msg;
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
                        if (opts->new_branch_log && !log_all_ref_updates) {
@@ -620,8 +620,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        old_desc = old->name;
        if (!old_desc && old->commit)
                old_desc = sha1_to_hex(old->commit->object.sha1);
-       strbuf_addf(&msg, "checkout: moving from %s to %s",
-                   old_desc ? old_desc : "(invalid)", new->name);
+
+       reflog_msg = getenv("GIT_REFLOG_ACTION");
+       if (!reflog_msg)
+               strbuf_addf(&msg, "checkout: moving from %s to %s",
+                       old_desc ? old_desc : "(invalid)", new->name);
+       else
+               strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
 
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
@@ -838,13 +843,16 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
        memset(&query, 0, sizeof(struct refspec));
        query.src = cb->src_ref;
        if (remote_find_tracking(remote, &query) ||
-           get_sha1(query.dst, cb->dst_sha1))
+           get_sha1(query.dst, cb->dst_sha1)) {
+               free(query.dst);
                return 0;
+       }
        if (cb->dst_ref) {
+               free(query.dst);
                cb->unique = 0;
                return 0;
        }
-       cb->dst_ref = xstrdup(query.dst);
+       cb->dst_ref = query.dst;
        return 0;
 }
 
index 66bff5700f1e33c009c69fc44ba327dff1acb135..430307b298b4f9ea03ff4caea283b133fde0077e 100644 (file)
@@ -493,13 +493,16 @@ static void write_remote_refs(const struct ref *local_refs)
 {
        const struct ref *r;
 
+       lock_packed_refs(LOCK_DIE_ON_ERROR);
+
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
                add_packed_ref(r->peer_ref->name, r->old_sha1);
        }
 
-       pack_refs(PACK_REFS_ALL);
+       if (commit_packed_refs())
+               die_errno("unable to overwrite old ref-pack file");
 }
 
 static void write_followtags(const struct ref *refs, const char *msg)
@@ -542,17 +545,20 @@ static void update_remote_refs(const struct ref *refs,
                               const struct ref *remote_head_points_at,
                               const char *branch_top,
                               const char *msg,
-                              struct transport *transport)
+                              struct transport *transport,
+                              int check_connectivity)
 {
        const struct ref *rm = mapped_refs;
 
-       if (0 <= option_verbosity)
-               printf(_("Checking connectivity... "));
-       if (check_everything_connected_with_transport(iterate_ref_map,
-                                                     0, &rm, transport))
-               die(_("remote did not send all necessary objects"));
-       if (0 <= option_verbosity)
-               printf(_("done\n"));
+       if (check_connectivity) {
+               if (0 <= option_verbosity)
+                       printf(_("Checking connectivity... "));
+               if (check_everything_connected_with_transport(iterate_ref_map,
+                                                             0, &rm, transport))
+                       die(_("remote did not send all necessary objects"));
+               if (0 <= option_verbosity)
+                       printf(_("done\n"));
+       }
 
        if (refs) {
                write_remote_refs(mapped_refs);
@@ -698,7 +704,7 @@ static void write_refspec_config(const char* src_ref_prefix,
                        /*
                         * otherwise, the next "git fetch" will
                         * simply fetch from HEAD without updating
-                        * any remote tracking branch, which is what
+                        * any remote-tracking branch, which is what
                         * we want.
                         */
                } else {
@@ -960,7 +966,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_fetch_refs(transport, mapped_refs);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
-                          branch_top.buf, reflog_msg.buf, transport);
+                          branch_top.buf, reflog_msg.buf, transport, !is_local);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
index 6b693c16d8842b70b0e1c7bd7df5095947ed0a64..790e5ab5cfee7b0f5760aafed7bf6f3f5927871e 100644 (file)
@@ -112,12 +112,14 @@ static int show_ignored_in_status, have_option_m;
 static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
-static enum {
+static enum status_format {
        STATUS_FORMAT_NONE = 0,
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN
-} status_format;
+       STATUS_FORMAT_PORCELAIN,
+
+       STATUS_FORMAT_UNSPECIFIED
+} status_format = STATUS_FORMAT_UNSPECIFIED;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
@@ -460,6 +462,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
@@ -961,6 +966,42 @@ static const char *read_commit_message(const char *name)
        return logmsg_reencode(commit, NULL, out_enc);
 }
 
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+       enum status_format status_format;
+       int show_branch;
+} status_deferred_config = {
+       STATUS_FORMAT_UNSPECIFIED,
+       -1 /* unspecified */
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+       int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+                                  !s->null_termination);
+
+       if (s->null_termination) {
+               if (status_format == STATUS_FORMAT_NONE ||
+                   status_format == STATUS_FORMAT_UNSPECIFIED)
+                       status_format = STATUS_FORMAT_PORCELAIN;
+               else if (status_format == STATUS_FORMAT_LONG)
+                       die(_("--long and -z are incompatible"));
+       }
+
+       if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = status_deferred_config.status_format;
+       if (status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = STATUS_FORMAT_NONE;
+
+       if (use_deferred_config && s->show_branch < 0)
+               s->show_branch = status_deferred_config.show_branch;
+       if (s->show_branch < 0)
+               s->show_branch = 0;
+}
+
 static int parse_and_validate_options(int argc, const char *argv[],
                                      const struct option *options,
                                      const char * const usage[],
@@ -971,6 +1012,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        int f = 0;
 
        argc = parse_options(argc, argv, prefix, options, usage, 0);
+       finalize_deferred_config(s);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
@@ -1055,12 +1097,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
 
-       if (s->null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
        if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
 
@@ -1113,6 +1149,17 @@ static int git_status_config(const char *k, const char *v, void *cb)
                        s->submodule_summary = -1;
                return 0;
        }
+       if (!strcmp(k, "status.short")) {
+               if (git_config_bool(k, v))
+                       status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+               else
+                       status_deferred_config.status_format = STATUS_FORMAT_NONE;
+               return 0;
+       }
+       if (!strcmp(k, "status.branch")) {
+               status_deferred_config.show_branch = git_config_bool(k, v);
+               return 0;
+       }
        if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
                s->use_color = git_config_colorbool(k, v);
                return 0;
@@ -1155,8 +1202,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
                            N_("show status concisely"), STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN('b', "branch", &s.show_branch,
-                           N_("show branch information")),
+               OPT_BOOL('b', "branch", &s.show_branch,
+                        N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"),
                            STATUS_FORMAT_PORCELAIN),
@@ -1189,13 +1236,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                             builtin_status_options,
                             builtin_status_usage, 0);
        finalize_colopts(&s.colopts, -1);
-
-       if (s.null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
+       finalize_deferred_config(&s);
 
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
@@ -1224,6 +1265,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
@@ -1392,7 +1436,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+               OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
                OPT_SET_INT(0, "long", &status_format,
@@ -1433,6 +1477,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        wt_status_prepare(&s);
        gitmodules_config();
        git_config(git_commit_config, &s);
+       status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        determine_whence(&s);
        s.colopts = 0;
 
index 062957f629af09db81d78b9cb0be1ce60fd75ec4..f1e236b9124f8b7bfd9aa5e926d525c6f4568268 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * builtin-help.c
- *
  * Builtin help command
  */
 #include "cache.h"
index 9e2123295ffa80ecab83977099ed6a43fd4a93f9..2625f9881ad836d1a3e303f4c159d6b76a3ae04f 100644 (file)
@@ -237,7 +237,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
        int i = revs->early_output;
        int show_header = 1;
 
-       sort_in_topological_order(&list, revs->lifo);
+       sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
                switch (simplify_commit(revs, commit)) {
@@ -1112,6 +1112,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+       char **from = opt->value;
+
+       free(*from);
+
+       if (unset)
+               *from = NULL;
+       else if (arg)
+               *from = xstrdup(arg);
+       else
+               *from = xstrdup(git_committer_info(IDENT_NO_DATE));
+       return 0;
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -1134,6 +1149,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
+       char *from = NULL;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("use [PATCH n/m] even with a single patch"),
@@ -1177,6 +1193,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            0, to_callback },
                { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
                            0, cc_callback },
+               { OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+                           N_("set From address to <ident> (or committer ident if absent)"),
+                           PARSE_OPT_OPTARG, from_callback },
                OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
                            N_("make first mail a reply to <message-id>")),
                { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1264,6 +1283,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        rev.extra_headers = strbuf_detach(&buf, NULL);
 
+       if (from) {
+               if (split_ident_line(&rev.from_ident, from, strlen(from)))
+                       die(_("invalid ident line: %s"), from);
+       }
+
        if (start_number < 0)
                start_number = 1;
 
index 87f3b331ca68715f074b2a5396dda77eea3c3154..3a410c35d99ae030a75c4d3065b7023467e0d1c6 100644 (file)
@@ -165,11 +165,13 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
        }
        write_name(ce->name, ce_namelen(ce));
        if (debug_mode) {
-               printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
-               printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
-               printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
-               printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
-               printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+               struct stat_data *sd = &ce->ce_stat_data;
+
+               printf("  ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+               printf("  mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+               printf("  dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+               printf("  uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+               printf("  size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
        }
 }
 
index 2ebe732896c1b3bfd920941b5182b793468c0afe..9505e7e426d302522f5e065d741fccd37290ea21 100644 (file)
@@ -47,8 +47,8 @@ static const char * const builtin_merge_usage[] = {
 };
 
 static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit = -1;
+static int option_commit = 1;
+static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
@@ -76,6 +76,14 @@ static struct strategy all_strategy[] = {
 
 static const char *pull_twohead, *pull_octopus;
 
+enum ff_type {
+       FF_NO,
+       FF_ALLOW,
+       FF_ONLY
+};
+
+static enum ff_type fast_forward = FF_ALLOW;
+
 static int option_parse_message(const struct option *opt,
                                const char *arg, int unset)
 {
@@ -178,6 +186,13 @@ static int option_parse_n(const struct option *opt,
        return 0;
 }
 
+static int option_parse_ff_only(const struct option *opt,
+                         const char *arg, int unset)
+{
+       fast_forward = FF_ONLY;
+       return 0;
+}
+
 static struct option builtin_merge_options[] = {
        { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
                N_("do not show a diffstat at the end of the merge"),
@@ -194,10 +209,10 @@ static struct option builtin_merge_options[] = {
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
-       OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-               N_("allow fast-forward (default)")),
-       OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
-               N_("abort if fast-forward is not possible")),
+       OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
+       { OPTION_CALLBACK, 0, "ff-only", NULL, NULL,
+               N_("abort if fast-forward is not possible"),
+               PARSE_OPT_NOARG | PARSE_OPT_NONEG, option_parse_ff_only },
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_BOOL(0, "verify-signatures", &verify_signatures,
                N_("Verify that the named commit has a valid GPG signature")),
@@ -581,10 +596,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        else if (!strcmp(k, "merge.ff")) {
                int boolval = git_config_maybe_bool(k, v);
                if (0 <= boolval) {
-                       allow_fast_forward = boolval;
+                       fast_forward = boolval ? FF_ALLOW : FF_NO;
                } else if (v && !strcmp(v, "only")) {
-                       allow_fast_forward = 1;
-                       fast_forward_only = 1;
+                       fast_forward = FF_ONLY;
                } /* do not barf on values from future versions of git */
                return 0;
        } else if (!strcmp(k, "merge.defaulttoupstream")) {
@@ -863,7 +877,7 @@ static int finish_automerge(struct commit *head,
 
        free_commit_list(common);
        parents = remoteheads;
-       if (!head_subsumed || !allow_fast_forward)
+       if (!head_subsumed || fast_forward == FF_NO)
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
@@ -948,7 +962,7 @@ static int evaluate_result(void)
 }
 
 /*
- * Pretend as if the user told us to merge with the tracking
+ * Pretend as if the user told us to merge with the remote-tracking
  * branch we have for the upstream of the current branch
  */
 static int setup_with_upstream(const char ***argv)
@@ -967,7 +981,7 @@ static int setup_with_upstream(const char ***argv)
        args = xcalloc(branch->merge_nr + 1, sizeof(char *));
        for (i = 0; i < branch->merge_nr; i++) {
                if (!branch->merge[i]->dst)
-                       die(_("No remote tracking branch for %s from %s"),
+                       die(_("No remote-tracking branch for %s from %s"),
                            branch->merge[i]->src, branch->remote_name);
                args[i] = branch->merge[i]->dst;
        }
@@ -1008,7 +1022,7 @@ static void write_merge_state(struct commit_list *remoteheads)
        if (fd < 0)
                die_errno(_("Could not open '%s' for writing"), filename);
        strbuf_reset(&buf);
-       if (!allow_fast_forward)
+       if (fast_forward == FF_NO)
                strbuf_addf(&buf, "no-ff");
        if (write_in_full(fd, buf.buf, buf.len) != buf.len)
                die_errno(_("Could not write to '%s'"), filename);
@@ -1157,14 +1171,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                show_diffstat = 0;
 
        if (squash) {
-               if (!allow_fast_forward)
+               if (fast_forward == FF_NO)
                        die(_("You cannot combine --squash with --no-ff."));
                option_commit = 0;
        }
 
-       if (!allow_fast_forward && fast_forward_only)
-               die(_("You cannot combine --no-ff with --ff-only."));
-
        if (!abort_current_merge) {
                if (!argc) {
                        if (default_to_upstream)
@@ -1206,7 +1217,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                "empty head"));
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
-               if (!allow_fast_forward)
+               if (fast_forward == FF_NO)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
                remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
@@ -1294,11 +1305,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                            sha1_to_hex(commit->object.sha1));
                setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
-               if (!fast_forward_only &&
+               if (fast_forward != FF_ONLY &&
                    merge_remote_util(commit) &&
                    merge_remote_util(commit)->obj &&
                    merge_remote_util(commit)->obj->type == OBJ_TAG)
-                       allow_fast_forward = 0;
+                       fast_forward = FF_NO;
        }
 
        if (option_edit < 0)
@@ -1315,7 +1326,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        for (i = 0; i < use_strategies_nr; i++) {
                if (use_strategies[i]->attr & NO_FAST_FORWARD)
-                       allow_fast_forward = 0;
+                       fast_forward = FF_NO;
                if (use_strategies[i]->attr & NO_TRIVIAL)
                        allow_trivial = 0;
        }
@@ -1345,7 +1356,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 */
                finish_up_to_date("Already up-to-date.");
                goto done;
-       } else if (allow_fast_forward && !remoteheads->next &&
+       } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
                        !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
                /* Again the most common case of merging one remote. */
@@ -1392,7 +1403,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * only one common.
                 */
                refresh_cache(REFRESH_QUIET);
-               if (allow_trivial && !fast_forward_only) {
+               if (allow_trivial && fast_forward != FF_ONLY) {
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
@@ -1433,7 +1444,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (fast_forward_only)
+       if (fast_forward == FF_ONLY)
                die(_("Not possible to fast-forward, aborting."));
 
        /* We are going to make a new commit. */
index 62382479749909d449b718fc2f3bef36906caa4b..87d485496fd5556cbd0f24793ed7e48f11e9b292 100644 (file)
@@ -82,6 +82,20 @@ static void name_rev(struct commit *commit,
        }
 }
 
+static int subpath_matches(const char *path, const char *filter)
+{
+       const char *subpath = path;
+
+       while (subpath) {
+               if (!fnmatch(filter, subpath, 0))
+                       return subpath - path;
+               subpath = strchr(subpath, '/');
+               if (subpath)
+                       subpath++;
+       }
+       return -1;
+}
+
 struct name_ref_data {
        int tags_only;
        int name_only;
@@ -92,13 +106,23 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
 {
        struct object *o = parse_object(sha1);
        struct name_ref_data *data = cb_data;
+       int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
 
        if (data->tags_only && prefixcmp(path, "refs/tags/"))
                return 0;
 
-       if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
-               return 0;
+       if (data->ref_filter) {
+               switch (subpath_matches(path, data->ref_filter)) {
+               case -1: /* did not match */
+                       return 0;
+               case 0:  /* matched fully */
+                       break;
+               default: /* matched subpath */
+                       can_abbreviate_output = 1;
+                       break;
+               }
+       }
 
        while (o && o->type == OBJ_TAG) {
                struct tag *t = (struct tag *) o;
@@ -110,12 +134,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
 
-               if (!prefixcmp(path, "refs/heads/"))
+               if (can_abbreviate_output)
+                       path = shorten_unambiguous_ref(path, 0);
+               else if (!prefixcmp(path, "refs/heads/"))
                        path = path + 11;
-               else if (data->tags_only
-                   && data->name_only
-                   && !prefixcmp(path, "refs/tags/"))
-                       path = path + 10;
                else if (!prefixcmp(path, "refs/"))
                        path = path + 5;
 
index 9ed2508f038abb14fc4ec72ace7ab75250e90e84..e4100c49820306b881a20db4bb3af7c13f12c019 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2010 Johan Herland <johan@herland.net>
  *
  * Based on git-notes.sh by Johannes Schindelin,
- * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
+ * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
  */
 
 #include "cache.h"
index 2d84d10720fad88becdfe957f3770a2c44ac0a8f..6d36c24268bea3cb658a1aa94f3a037f403d5895 100644 (file)
@@ -120,10 +120,11 @@ static const char message_detached_head_die[] =
           "\n"
           "    git push %s HEAD:<name-of-remote-branch>\n");
 
-static void setup_push_upstream(struct remote *remote, int simple)
+static void setup_push_upstream(struct remote *remote, struct branch *branch,
+                               int triangular)
 {
        struct strbuf refspec = STRBUF_INIT;
-       struct branch *branch = branch_get(NULL);
+
        if (!branch)
                die(_(message_detached_head_die), remote->name);
        if (!branch->merge_nr || !branch->merge || !branch->remote_name)
@@ -137,18 +138,29 @@ static void setup_push_upstream(struct remote *remote, int simple)
        if (branch->merge_nr != 1)
                die(_("The current branch %s has multiple upstream branches, "
                    "refusing to push."), branch->name);
-       if (strcmp(branch->remote_name, remote->name))
+       if (triangular)
                die(_("You are pushing to remote '%s', which is not the upstream of\n"
                      "your current branch '%s', without telling me what to push\n"
                      "to update which remote branch."),
                    remote->name, branch->name);
-       if (simple && strcmp(branch->refname, branch->merge[0]->src))
-               die_push_simple(branch, remote);
+
+       if (push_default == PUSH_DEFAULT_SIMPLE) {
+               /* Additional safety */
+               if (strcmp(branch->refname, branch->merge[0]->src))
+                       die_push_simple(branch, remote);
+       }
 
        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
        add_refspec(refspec.buf);
 }
 
+static void setup_push_current(struct remote *remote, struct branch *branch)
+{
+       if (!branch)
+               die(_(message_detached_head_die), remote->name);
+       add_refspec(branch->name);
+}
+
 static char warn_unspecified_push_default_msg[] =
 N_("push.default is unset; its implicit value is changing in\n"
    "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -173,9 +185,16 @@ static void warn_unspecified_push_default_configuration(void)
        warning("%s\n", _(warn_unspecified_push_default_msg));
 }
 
+static int is_workflow_triangular(struct remote *remote)
+{
+       struct remote *fetch_remote = remote_get(NULL);
+       return (fetch_remote && fetch_remote != remote);
+}
+
 static void setup_default_push_refspecs(struct remote *remote)
 {
-       struct branch *branch;
+       struct branch *branch = branch_get(NULL);
+       int triangular = is_workflow_triangular(remote);
 
        switch (push_default) {
        default:
@@ -188,18 +207,18 @@ static void setup_default_push_refspecs(struct remote *remote)
                break;
 
        case PUSH_DEFAULT_SIMPLE:
-               setup_push_upstream(remote, 1);
+               if (triangular)
+                       setup_push_current(remote, branch);
+               else
+                       setup_push_upstream(remote, branch, triangular);
                break;
 
        case PUSH_DEFAULT_UPSTREAM:
-               setup_push_upstream(remote, 0);
+               setup_push_upstream(remote, branch, triangular);
                break;
 
        case PUSH_DEFAULT_CURRENT:
-               branch = branch_get(NULL);
-               if (!branch)
-                       die(_(message_detached_head_die), remote->name);
-               add_refspec(branch->name);
+               setup_push_current(remote, branch);
                break;
 
        case PUSH_DEFAULT_NOTHING:
@@ -211,8 +230,8 @@ static void setup_default_push_refspecs(struct remote *remote)
 
 static const char message_advice_pull_before_push[] =
        N_("Updates were rejected because the tip of your current branch is behind\n"
-          "its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-          "before pushing again.\n"
+          "its remote counterpart. Integrate the remote changes (e.g.\n"
+          "'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_use_upstream[] =
@@ -223,15 +242,15 @@ static const char message_advice_use_upstream[] =
 
 static const char message_advice_checkout_pull_push[] =
        N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-          "counterpart. Check out this branch and merge the remote changes\n"
-          "(e.g. 'git pull') before pushing again.\n"
+          "counterpart. Check out this branch and integrate the remote changes\n"
+          "(e.g. 'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_fetch_first[] =
        N_("Updates were rejected because the remote contains work that you do\n"
           "not have locally. This is usually caused by another repository pushing\n"
-          "to the same ref. You may want to first merge the remote changes (e.g.,\n"
-          "'git pull') before pushing again.\n"
+          "to the same ref. You may want to first integrate the remote changes\n"
+          "(e.g., 'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_already_exists[] =
index 398ccd5eaa243045c5d21af1e37462846665568a..59d31152d0068dd4a542d2c8e076c629d508500f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
  *
- * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * Based on builtin/tag.c by Kristian Høgsberg <krh@redhat.com>
  * and Carlos Rica <jasampler@gmail.com> that was itself based on
  * git-tag.sh and mktag.c by Linus Torvalds.
  */
index 6032131a9087281e97d0abc05365b7485004121b..afa6e020e83ebac739db474c278e44bc22079848 100644 (file)
@@ -93,10 +93,12 @@ static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 static void print_new_head_line(struct commit *commit)
 {
        const char *hex, *body;
+       char *msg;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
-       body = strstr(commit->buffer, "\n\n");
+       msg = logmsg_reencode(commit, NULL, get_log_output_encoding());
+       body = strstr(msg, "\n\n");
        if (body) {
                const char *eol;
                size_t len;
@@ -107,6 +109,7 @@ static void print_new_head_line(struct commit *commit)
        }
        else
                printf("\n");
+       logmsg_free(msg, commit);
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
index 67701be55193db0fd9733ee7c4962db08072045c..a5ec30d74ee1945ddaf99c89307e59aec714714a 100644 (file)
@@ -111,6 +111,7 @@ static void show_commit(struct commit *commit, void *data)
                ctx.date_mode = revs->date_mode;
                ctx.date_mode_explicit = revs->date_mode_explicit;
                ctx.fmt = revs->commit_format;
+               ctx.output_encoding = get_log_output_encoding();
                pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
                        if (buf.len) {
index f267a1d3b57c1f1ab44c83a197f46051f4f13060..de894c75773c78afb2c0f1ea4ea5aff8ec7de6bc 100644 (file)
@@ -212,11 +212,17 @@ static void show_datestring(const char *flag, const char *datestr)
        show(buffer);
 }
 
-static int show_file(const char *arg)
+static int show_file(const char *arg, int output_prefix)
 {
        show_default();
        if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
-               show(arg);
+               if (output_prefix) {
+                       const char *prefix = startup_info->prefix;
+                       show(prefix_filename(prefix,
+                                            prefix ? strlen(prefix) : 0,
+                                            arg));
+               } else
+                       show(arg);
                return 1;
        }
        return 0;
@@ -470,6 +476,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int output_prefix = 0;
        unsigned char sha1[20];
        const char *name = NULL;
 
@@ -503,7 +510,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
 
                if (as_is) {
-                       if (show_file(arg) && as_is < 2)
+                       if (show_file(arg, output_prefix) && as_is < 2)
                                verify_filename(prefix, arg, 0);
                        continue;
                }
@@ -527,7 +534,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                as_is = 2;
                                /* Pass on the "--" if we show anything but files.. */
                                if (filter & (DO_FLAGS | DO_REVS))
-                                       show_file(arg);
+                                       show_file(arg, 0);
                                continue;
                        }
                        if (!strcmp(arg, "--default")) {
@@ -535,6 +542,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                i++;
                                continue;
                        }
+                       if (!strcmp(arg, "--prefix")) {
+                               prefix = argv[i+1];
+                               startup_info->prefix = prefix;
+                               output_prefix = 1;
+                               i++;
+                               continue;
+                       }
                        if (!strcmp(arg, "--revs-only")) {
                                filter &= ~DO_NOREV;
                                continue;
@@ -754,7 +768,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                if (verify)
                        die_no_single_rev(quiet);
                as_is = 1;
-               if (!show_file(arg))
+               if (!show_file(arg, output_prefix))
                        continue;
                verify_filename(prefix, arg, 1);
        }
index 1fd6f8ac5977ee670bf8ba230cec8553393baafd..1434f8fee487751abe069250c611bdaed2fa5857 100644 (file)
@@ -137,6 +137,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
+               ctx.output_encoding = get_log_output_encoding();
                pretty_print_commit(&ctx, commit, &ufbuf);
                buffer = ufbuf.buf;
        } else if (*buffer) {
index 90fc6b1b9d2a397ae43dfc4c0aba6bc192c9823c..99ec4af224464f5c6c82599dcc4ca18b6abaa7f7 100644 (file)
@@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int num_rev, i, extra = 0;
        int all_heads = 0, all_remotes = 0;
        int all_mask, all_revs;
-       int lifo = 1;
+       enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
        char head[128];
        const char *head_p;
        int head_len;
@@ -665,15 +665,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                            N_("show possible merge bases")),
                OPT_BOOLEAN(0, "independent", &independent,
                            N_("show refs unreachable from any other ref")),
-               OPT_BOOLEAN(0, "topo-order", &lifo,
-                           N_("show commits in topological order")),
+               OPT_SET_INT(0, "topo-order", &sort_order,
+                           N_("show commits in topological order"),
+                           REV_SORT_IN_GRAPH_ORDER),
                OPT_BOOLEAN(0, "topics", &topics,
                            N_("show only commits not on the first branch")),
                OPT_SET_INT(0, "sparse", &dense,
                            N_("show merges reachable from only one tip"), 0),
-               OPT_SET_INT(0, "date-order", &lifo,
+               OPT_SET_INT(0, "date-order", &sort_order,
                            N_("show commits where no parent comes before its "
-                              "children"), 0),
+                              "children"),
+                           REV_SORT_BY_COMMIT_DATE),
                { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
                            N_("show <n> most recent ref-log entries starting at "
                               "base"),
@@ -900,7 +902,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                exit(0);
 
        /* Sort topologically */
-       sort_in_topological_order(&seen, lifo);
+       sort_in_topological_order(&seen, sort_order);
 
        /* Give names to commits */
        if (!sha1_name && !no_name)
diff --git a/cache.h b/cache.h
index ec8240f62a77cf718436d3de62f8ab7f66d4a322..dd0fb33a1520edf2a243555474d1875818294598 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -119,15 +119,19 @@ struct cache_time {
        unsigned int nsec;
 };
 
+struct stat_data {
+       struct cache_time sd_ctime;
+       struct cache_time sd_mtime;
+       unsigned int sd_dev;
+       unsigned int sd_ino;
+       unsigned int sd_uid;
+       unsigned int sd_gid;
+       unsigned int sd_size;
+};
+
 struct cache_entry {
-       struct cache_time ce_ctime;
-       struct cache_time ce_mtime;
-       unsigned int ce_dev;
-       unsigned int ce_ino;
+       struct stat_data ce_stat_data;
        unsigned int ce_mode;
-       unsigned int ce_uid;
-       unsigned int ce_gid;
-       unsigned int ce_size;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
@@ -511,6 +515,21 @@ extern int limit_pathspec_to_literal(void);
 #define HASH_FORMAT_CHECK 2
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled.  If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
@@ -1338,4 +1357,31 @@ int checkout_fast_forward(const unsigned char *from,
 
 int sane_execvp(const char *file, char *const argv[]);
 
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+       struct stat_data *sd;
+};
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise.  A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
 #endif /* CACHE_H */
diff --git a/commit-slab.h b/commit-slab.h
new file mode 100644 (file)
index 0000000..7d48163
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef COMMIT_SLAB_H
+#define COMMIT_SLAB_H
+
+/*
+ * define_commit_slab(slabname, elemtype) creates boilerplate code to define
+ * a new struct (struct slabname) that is used to associate a piece of data
+ * of elemtype to commits, and a few functions to use that struct.
+ *
+ * After including this header file, using:
+ *
+ * define_commit_slab(indegee, int);
+ *
+ * will let you call the following functions:
+ *
+ * - int *indegree_at(struct indegree *, struct commit *);
+ *
+ *   This function locates the data associated with the given commit in
+ *   the indegree slab, and returns the pointer to it.
+ *
+ * - void init_indegree(struct indegree *);
+ *   void init_indegree_with_stride(struct indegree *, int);
+ *
+ *   Initializes the indegree slab that associates an array of integers
+ *   to each commit. 'stride' specifies how big each array is.  The slab
+ *   that id initialied by the variant without "_with_stride" associates
+ *   each commit with an array of one integer.
+ */
+
+/* allocate ~512kB at once, allowing for malloc overhead */
+#ifndef COMMIT_SLAB_SIZE
+#define COMMIT_SLAB_SIZE (512*1024-32)
+#endif
+
+#define define_commit_slab(slabname, elemtype)                                 \
+                                                                       \
+struct slabname {                                                      \
+       unsigned slab_size;                                             \
+       unsigned stride;                                                \
+       unsigned slab_count;                                            \
+       elemtype **slab;                                                \
+};                                                                     \
+static int stat_ ##slabname## realloc;                                 \
+                                                                       \
+static void init_ ##slabname## _with_stride(struct slabname *s,                \
+                                           unsigned stride)            \
+{                                                                      \
+       unsigned int elem_size;                                         \
+       if (!stride)                                                    \
+               stride = 1;                                             \
+       s->stride = stride;                                             \
+       elem_size = sizeof(struct slabname) * stride;                   \
+       s->slab_size = COMMIT_SLAB_SIZE / elem_size;                    \
+       s->slab_count = 0;                                              \
+       s->slab = NULL;                                                 \
+}                                                                      \
+                                                                       \
+static void init_ ##slabname(struct slabname *s)                       \
+{                                                                      \
+       init_ ##slabname## _with_stride(s, 1);                          \
+}                                                                      \
+                                                                       \
+static void clear_ ##slabname(struct slabname *s)                      \
+{                                                                      \
+       int i;                                                          \
+       for (i = 0; i < s->slab_count; i++)                             \
+               free(s->slab[i]);                                       \
+       s->slab_count = 0;                                              \
+       free(s->slab);                                                  \
+       s->slab = NULL;                                                 \
+}                                                                      \
+                                                                       \
+static elemtype *slabname## _at(struct slabname *s,                    \
+                               const struct commit *c)                 \
+{                                                                      \
+       int nth_slab, nth_slot, ix;                                     \
+                                                                       \
+       ix = c->index * s->stride;                                      \
+       nth_slab = ix / s->slab_size;                                   \
+       nth_slot = ix % s->slab_size;                                   \
+                                                                       \
+       if (s->slab_count <= nth_slab) {                                \
+               int i;                                                  \
+               s->slab = xrealloc(s->slab,                             \
+                                  (nth_slab + 1) * sizeof(s->slab));   \
+               stat_ ##slabname## realloc++;                           \
+               for (i = s->slab_count; i <= nth_slab; i++)             \
+                       s->slab[i] = NULL;                              \
+               s->slab_count = nth_slab + 1;                           \
+       }                                                               \
+       if (!s->slab[nth_slab])                                         \
+               s->slab[nth_slab] = xcalloc(s->slab_size,               \
+                                           sizeof(**s->slab));         \
+       return &s->slab[nth_slab][nth_slot];                            \
+}                                                                      \
+                                                                       \
+static int stat_ ##slabname## realloc
+
+#endif /* COMMIT_SLAB_H */
index 888e02ae2f65ab566555465e5d88d02bbe52420f..e5862f6d7c813b4bcc5dd672383e56152ceb6f80 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -8,12 +8,15 @@
 #include "notes.h"
 #include "gpg-interface.h"
 #include "mergesort.h"
+#include "commit-slab.h"
+#include "prio-queue.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
+static int commit_count;
 
 static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@ -58,8 +61,11 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
 struct commit *lookup_commit(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
-       if (!obj)
-               return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+       if (!obj) {
+               struct commit *c = alloc_commit_node();
+               c->index = commit_count++;
+               return create_object(sha1, OBJ_COMMIT, c);
+       }
        if (!obj->type)
                obj->type = OBJ_COMMIT;
        return check_commit(obj, sha1, 0);
@@ -506,33 +512,137 @@ struct commit *pop_commit(struct commit_list **stack)
        return item;
 }
 
+/*
+ * Topological sort support
+ */
+
+/* count number of children that have not been emitted */
+define_commit_slab(indegree_slab, int);
+
+/* record author-date for each commit object */
+define_commit_slab(author_date_slab, unsigned long);
+
+static void record_author_date(struct author_date_slab *author_date,
+                              struct commit *commit)
+{
+       const char *buf, *line_end;
+       char *buffer = NULL;
+       struct ident_split ident;
+       char *date_end;
+       unsigned long date;
+
+       if (!commit->buffer) {
+               unsigned long size;
+               enum object_type type;
+               buffer = read_sha1_file(commit->object.sha1, &type, &size);
+               if (!buffer)
+                       return;
+       }
+
+       for (buf = commit->buffer ? commit->buffer : buffer;
+            buf;
+            buf = line_end + 1) {
+               line_end = strchrnul(buf, '\n');
+               if (prefixcmp(buf, "author ")) {
+                       if (!line_end[0] || line_end[1] == '\n')
+                               return; /* end of header */
+                       continue;
+               }
+               if (split_ident_line(&ident,
+                                    buf + strlen("author "),
+                                    line_end - (buf + strlen("author "))) ||
+                   !ident.date_begin || !ident.date_end)
+                       goto fail_exit; /* malformed "author" line */
+               break;
+       }
+
+       date = strtoul(ident.date_begin, &date_end, 10);
+       if (date_end != ident.date_end)
+               goto fail_exit; /* malformed date */
+       *(author_date_slab_at(author_date, commit)) = date;
+
+fail_exit:
+       free(buffer);
+}
+
+static int compare_commits_by_author_date(const void *a_, const void *b_,
+                                         void *cb_data)
+{
+       const struct commit *a = a_, *b = b_;
+       struct author_date_slab *author_date = cb_data;
+       unsigned long a_date = *(author_date_slab_at(author_date, a));
+       unsigned long b_date = *(author_date_slab_at(author_date, b));
+
+       /* newer commits with larger date first */
+       if (a_date < b_date)
+               return 1;
+       else if (a_date > b_date)
+               return -1;
+       return 0;
+}
+
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+{
+       const struct commit *a = a_, *b = b_;
+       /* newer commits with larger date first */
+       if (a->date < b->date)
+               return 1;
+       else if (a->date > b->date)
+               return -1;
+       return 0;
+}
+
 /*
  * Performs an in-place topological sort on the list supplied.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo)
+void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order)
 {
        struct commit_list *next, *orig = *list;
-       struct commit_list *work, **insert;
        struct commit_list **pptr;
+       struct indegree_slab indegree;
+       struct prio_queue queue;
+       struct commit *commit;
+       struct author_date_slab author_date;
 
        if (!orig)
                return;
        *list = NULL;
 
+       init_indegree_slab(&indegree);
+       memset(&queue, '\0', sizeof(queue));
+
+       switch (sort_order) {
+       default: /* REV_SORT_IN_GRAPH_ORDER */
+               queue.compare = NULL;
+               break;
+       case REV_SORT_BY_COMMIT_DATE:
+               queue.compare = compare_commits_by_commit_date;
+               break;
+       case REV_SORT_BY_AUTHOR_DATE:
+               init_author_date_slab(&author_date);
+               queue.compare = compare_commits_by_author_date;
+               queue.cb_data = &author_date;
+               break;
+       }
+
        /* Mark them and clear the indegree */
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
-               commit->indegree = 1;
+               *(indegree_slab_at(&indegree, commit)) = 1;
+               /* also record the author dates, if needed */
+               if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+                       record_author_date(&author_date, commit);
        }
 
        /* update the indegree */
        for (next = orig; next; next = next->next) {
-               struct commit_list * parents = next->item->parents;
+               struct commit_list *parents = next->item->parents;
                while (parents) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
 
-                       if (parent->indegree)
-                               parent->indegree++;
+                       if (*pi)
+                               (*pi)++;
                        parents = parents->next;
                }
        }
@@ -544,34 +654,33 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
         *
         * the tips serve as a starting set for the work queue.
         */
-       work = NULL;
-       insert = &work;
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
 
-               if (commit->indegree == 1)
-                       insert = &commit_list_insert(commit, insert)->next;
+               if (*(indegree_slab_at(&indegree, commit)) == 1)
+                       prio_queue_put(&queue, commit);
        }
 
-       /* process the list in topological order */
-       if (!lifo)
-               commit_list_sort_by_date(&work);
+       /*
+        * This is unfortunate; the initial tips need to be shown
+        * in the order given from the revision traversal machinery.
+        */
+       if (sort_order == REV_SORT_IN_GRAPH_ORDER)
+               prio_queue_reverse(&queue);
+
+       /* We no longer need the commit list */
+       free_commit_list(orig);
 
        pptr = list;
        *list = NULL;
-       while (work) {
-               struct commit *commit;
-               struct commit_list *parents, *work_item;
-
-               work_item = work;
-               work = work_item->next;
-               work_item->next = NULL;
+       while ((commit = prio_queue_get(&queue)) != NULL) {
+               struct commit_list *parents;
 
-               commit = work_item->item;
                for (parents = commit->parents; parents ; parents = parents->next) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
 
-                       if (!parent->indegree)
+                       if (!*pi)
                                continue;
 
                        /*
@@ -579,21 +688,22 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
                         * when all their children have been emitted thereby
                         * guaranteeing topological order.
                         */
-                       if (--parent->indegree == 1) {
-                               if (!lifo)
-                                       commit_list_insert_by_date(parent, &work);
-                               else
-                                       commit_list_insert(parent, &work);
-                       }
+                       if (--(*pi) == 1)
+                               prio_queue_put(&queue, parent);
                }
                /*
-                * work_item is a commit all of whose children
-                * have already been emitted. we can emit it now.
+                * all children of commit have already been
+                * emitted. we can emit it now.
                 */
-               commit->indegree = 0;
-               *pptr = work_item;
-               pptr = &work_item->next;
+               *(indegree_slab_at(&indegree, commit)) = 0;
+
+               pptr = &commit_list_insert(commit, pptr)->next;
        }
+
+       clear_indegree_slab(&indegree);
+       clear_prio_queue(&queue);
+       if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+               clear_author_date_slab(&author_date);
 }
 
 /* merge-base stuff */
@@ -1240,10 +1350,15 @@ int commit_tree(const struct strbuf *msg, unsigned char *tree,
 static int find_invalid_utf8(const char *buf, int len)
 {
        int offset = 0;
+       static const unsigned int max_codepoint[] = {
+               0x7f, 0x7ff, 0xffff, 0x10ffff
+       };
 
        while (len) {
                unsigned char c = *buf++;
                int bytes, bad_offset;
+               unsigned int codepoint;
+               unsigned int min_val, max_val;
 
                len--;
                offset++;
@@ -1264,24 +1379,48 @@ static int find_invalid_utf8(const char *buf, int len)
                        bytes++;
                }
 
-               /* Must be between 1 and 5 more bytes */
-               if (bytes < 1 || bytes > 5)
+               /*
+                * Must be between 1 and 3 more bytes.  Longer sequences result in
+                * codepoints beyond U+10FFFF, which are guaranteed never to exist.
+                */
+               if (bytes < 1 || 3 < bytes)
                        return bad_offset;
 
                /* Do we *have* that many bytes? */
                if (len < bytes)
                        return bad_offset;
 
+               /*
+                * Place the encoded bits at the bottom of the value and compute the
+                * valid range.
+                */
+               codepoint = (c & 0x7f) >> bytes;
+               min_val = max_codepoint[bytes-1] + 1;
+               max_val = max_codepoint[bytes];
+
                offset += bytes;
                len -= bytes;
 
                /* And verify that they are good continuation bytes */
                do {
+                       codepoint <<= 6;
+                       codepoint |= *buf & 0x3f;
                        if ((*buf++ & 0xc0) != 0x80)
                                return bad_offset;
                } while (--bytes);
 
-               /* We could/should check the value and length here too */
+               /* Reject codepoints that are out of range for the sequence length. */
+               if (codepoint < min_val || codepoint > max_val)
+                       return bad_offset;
+               /* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */
+               if ((codepoint & 0x1ff800) == 0xd800)
+                       return bad_offset;
+               /* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */
+               if ((codepoint & 0xffffe) == 0xfffe)
+                       return bad_offset;
+               /* So are anything in the range U+FDD0..U+FDEF. */
+               if (codepoint >= 0xfdd0 && codepoint <= 0xfdef)
+                       return bad_offset;
        }
        return -1;
 }
@@ -1291,9 +1430,6 @@ static int find_invalid_utf8(const char *buf, int len)
  *
  * If it isn't, it assumes any non-utf8 characters are Latin1,
  * and does the conversion.
- *
- * Fixme: we should probably also disallow overlong forms and
- * invalid characters. But we don't do that currently.
  */
 static int verify_utf8(struct strbuf *buf)
 {
index 6e9c7cd9d5da7d24d4810b36039681f184325932..35cc4e266bd4afe0e00d2efb2781db2e95906936 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -6,6 +6,7 @@
 #include "strbuf.h"
 #include "decorate.h"
 #include "gpg-interface.h"
+#include "string-list.h"
 
 struct commit_list {
        struct commit *item;
@@ -15,7 +16,7 @@ struct commit_list {
 struct commit {
        struct object object;
        void *util;
-       unsigned int indegree;
+       unsigned int index;
        unsigned long date;
        struct commit_list *parents;
        struct tree *tree;
@@ -79,6 +80,9 @@ enum cmit_fmt {
 };
 
 struct pretty_print_context {
+       /*
+        * Callers should tweak these to change the behavior of pp_* functions.
+        */
        enum cmit_fmt fmt;
        int abbrev;
        const char *subject;
@@ -92,6 +96,15 @@ struct pretty_print_context {
        const char *output_encoding;
        struct string_list *mailmap;
        int color;
+       struct ident_split *from_ident;
+
+       /*
+        * Fields below here are manipulated internally by pp_* functions and
+        * should not be counted on by callers.
+        */
+
+       /* Manipulated by the pp_* functions internally. */
+       struct string_list in_body_headers;
 };
 
 struct userformat_want {
@@ -111,20 +124,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want
 extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
-extern void pretty_print_commit(const struct pretty_print_context *pp,
+extern void pretty_print_commit(struct pretty_print_context *pp,
                                const struct commit *commit,
                                struct strbuf *sb);
 extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
                           struct strbuf *sb);
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding);
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
                   const char *encoding,
                   int need_8bit_cte);
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent);
@@ -142,15 +155,24 @@ void clear_commit_marks(struct commit *commit, unsigned int mark);
 void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
 void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
 
+
+enum rev_sort_order {
+       REV_SORT_IN_GRAPH_ORDER = 0,
+       REV_SORT_BY_COMMIT_DATE,
+       REV_SORT_BY_AUTHOR_DATE
+};
+
 /*
  * Performs an in-place topological sort of list supplied.
  *
  *   invariant of resulting list is:
  *      a reachable from b => ord(b) < ord(a)
- *   in addition, when lifo == 0, commits on parallel tracks are
- *   sorted in the dates order.
+ *   sort_order further specifies:
+ *   REV_SORT_IN_GRAPH_ORDER: try to show a commit on a single-parent
+ *                            chain together.
+ *   REV_SORT_BY_COMMIT_DATE: show eligible commits in committer-date order.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo);
+void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
 
 struct commit_graft {
        unsigned char sha1[20];
@@ -245,4 +267,6 @@ extern void print_commit_list(struct commit_list *list,
  */
 extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
 
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+
 #endif /* COMMIT_H */
index f3462d9c81f12ea07229128f6bef7a4ff3fcfe10..2f433939dc9d9001c7ace83bfeadf567eb90c30c 100644 (file)
@@ -193,7 +193,7 @@ AC_ARG_ENABLE([pthreads],
   [FLAGS is the value to pass to the compiler to enable POSIX Threads.]
   [The default if FLAGS is not specified is to try first -pthread]
   [and then -lpthread.]
-  [--without-pthreads will disable threading.])],
+  [--disable-pthreads will disable threading.])],
 [
 if test "x$enableval" = "xyes"; then
    AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
index 6c3bafeea569601c4b55de165eb7f56ac3be1d31..fa3ae1e8535c786dc724ab7a73cccb6f3bfca8de 100644 (file)
@@ -33,8 +33,6 @@ esac
 # returns location of .git repo
 __gitdir ()
 {
-       # Note: this function is duplicated in git-prompt.sh
-       # When updating it, make sure you update the other one to match.
        if [ -z "${1-}" ]; then
                if [ -n "${__git_dir-}" ]; then
                        echo "$__git_dir"
@@ -2492,9 +2490,10 @@ __git_main ()
                i="${words[c]}"
                case "$i" in
                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
+               --git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
                --bare)      __git_dir="." ;;
                --help) command="help"; break ;;
-               -c) c=$((++c)) ;;
+               -c|--work-tree|--namespace) ((c++)) ;;
                -*) ;;
                *) command="$i"; break ;;
                esac
@@ -2512,6 +2511,7 @@ __git_main ()
                        --exec-path
                        --exec-path=
                        --html-path
+                       --man-path
                        --info-path
                        --work-tree=
                        --namespace=
index 07a6218d10679f5102c3649794bfd98e1883da75..a81ef5a482d48b114a81e242109e2f0dec0839ed 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Distributed under the GNU General Public License, version 2.0.
 #
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
 #
 # To enable:
 #
 #    3a) Change your PS1 to call __git_ps1 as
 #        command-substitution:
 #        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-#        ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
 #        the optional argument will be used as format string.
-#    3b) Alternatively, if you are using bash, __git_ps1 can be
-#        used for PROMPT_COMMAND with two parameters, <pre> and
-#        <post>, which are strings you would put in $PS1 before
-#        and after the status string generated by the git-prompt
-#        machinery.  e.g.
+#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+#        with two parameters, <pre> and <post>, which are strings
+#        you would put in $PS1 before and after the status string
+#        generated by the git-prompt machinery.  e.g.
 #        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+#          will show username, at-sign, host, colon, cwd, then
+#          various status string, followed by dollar and SP, as
+#          your prompt.
 #        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
-#        will show username, at-sign, host, colon, cwd, then
-#        various status string, followed by dollar and SP, as
-#        your prompt.
+#          will show username, pipe, then various status string,
+#          followed by colon, cwd, dollar and SP, as your prompt.
 #        Optionally, you can supply a third argument with a printf
 #        format string to finetune the output of the branch status
 #
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository.  The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
 #
 # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 # unstaged (*) and staged (+) changes will be shown next to the branch
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
-
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
-       # Note: this function is duplicated in git-completion.bash
-       # When updating it, make sure you update the other one to match.
-       if [ -z "${1-}" ]; then
-               if [ -n "${__git_dir-}" ]; then
-                       echo "$__git_dir"
-               elif [ -n "${GIT_DIR-}" ]; then
-                       test -d "${GIT_DIR-}" || return 1
-                       echo "$GIT_DIR"
-               elif [ -d .git ]; then
-                       echo .git
-               else
-                       git rev-parse --git-dir 2>/dev/null
-               fi
-       elif [ -d "$1/.git" ]; then
-               echo "$1/.git"
-       else
-               echo "$1"
-       fi
-}
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
 
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
@@ -225,8 +205,8 @@ __git_ps1_show_upstream ()
 }
 
 # Helper function that is meant to be called from __git_ps1.  It
-# builds up a gitstring injecting color codes into the appropriate
-# places.
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
 __git_ps1_colorize_gitstring ()
 {
        if [[ -n ${ZSH_VERSION-} ]]; then
@@ -234,74 +214,40 @@ __git_ps1_colorize_gitstring ()
                local c_green='%F{green}'
                local c_lblue='%F{blue}'
                local c_clear='%f'
-               local bad_color=$c_red
-               local ok_color=$c_green
-               local branch_color="$c_clear"
-               local flags_color="$c_lblue"
-               local branchstring="$c${b##refs/heads/}"
-
-               if [ $detached = no ]; then
-                       branch_color="$ok_color"
-               else
-                       branch_color="$bad_color"
-               fi
-
-               gitstring="$branch_color$branchstring$c_clear"
-
-               if [ -n "$w$i$s$u$r$p" ]; then
-                       gitstring="$gitstring$z"
-               fi
-               if [ "$w" = "*" ]; then
-                       gitstring="$gitstring$bad_color$w"
-               fi
-               if [ -n "$i" ]; then
-                       gitstring="$gitstring$ok_color$i"
-               fi
-               if [ -n "$s" ]; then
-                       gitstring="$gitstring$flags_color$s"
-               fi
-               if [ -n "$u" ]; then
-                       gitstring="$gitstring$bad_color$u"
-               fi
-               gitstring="$gitstring$c_clear$r$p"
-               return
+       else
+               # Using \[ and \] around colors is necessary to prevent
+               # issues with command line editing/browsing/completion!
+               local c_red='\[\e[31m\]'
+               local c_green='\[\e[32m\]'
+               local c_lblue='\[\e[1;34m\]'
+               local c_clear='\[\e[0m\]'
        fi
-       local c_red='\e[31m'
-       local c_green='\e[32m'
-       local c_lblue='\e[1;34m'
-       local c_clear='\e[0m'
        local bad_color=$c_red
        local ok_color=$c_green
-       local branch_color="$c_clear"
        local flags_color="$c_lblue"
-       local branchstring="$c${b##refs/heads/}"
 
+       local branch_color=""
        if [ $detached = no ]; then
                branch_color="$ok_color"
        else
                branch_color="$bad_color"
        fi
+       c="$branch_color$c"
 
-       # Setting gitstring directly with \[ and \] around colors
-       # is necessary to prevent wrapping issues!
-       gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
-
-       if [ -n "$w$i$s$u$r$p" ]; then
-               gitstring="$gitstring$z"
-       fi
+       z="$c_clear$z"
        if [ "$w" = "*" ]; then
-               gitstring="$gitstring\[$bad_color\]$w"
+               w="$bad_color$w"
        fi
        if [ -n "$i" ]; then
-               gitstring="$gitstring\[$ok_color\]$i"
+               i="$ok_color$i"
        fi
        if [ -n "$s" ]; then
-               gitstring="$gitstring\[$flags_color\]$s"
+               s="$flags_color$s"
        fi
        if [ -n "$u" ]; then
-               gitstring="$gitstring\[$bad_color\]$u"
+               u="$bad_color$u"
        fi
-       gitstring="$gitstring\[$c_clear\]$r$p"
+       r="$c_clear$r"
 }
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
@@ -335,50 +281,83 @@ __git_ps1 ()
                ;;
        esac
 
-       local g="$(__gitdir)"
-       if [ -z "$g" ]; then
+       local repo_info rev_parse_exit_code
+       repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+               --is-bare-repository --is-inside-work-tree \
+               --short HEAD 2>/dev/null)"
+       rev_parse_exit_code="$?"
+
+       if [ -z "$repo_info" ]; then
                if [ $pcmode = yes ]; then
                        #In PC mode PS1 always needs to be set
                        PS1="$ps1pc_start$ps1pc_end"
                fi
+               return
+       fi
+
+       local short_sha
+       if [ "$rev_parse_exit_code" = "0" ]; then
+               short_sha="${repo_info##*$'\n'}"
+               repo_info="${repo_info%$'\n'*}"
+       fi
+       local inside_worktree="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
+       local bare_repo="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
+       local inside_gitdir="${repo_info##*$'\n'}"
+       local g="${repo_info%$'\n'*}"
+
+       local r=""
+       local b=""
+       local step=""
+       local total=""
+       if [ -d "$g/rebase-merge" ]; then
+               read b 2>/dev/null <"$g/rebase-merge/head-name"
+               read step 2>/dev/null <"$g/rebase-merge/msgnum"
+               read total 2>/dev/null <"$g/rebase-merge/end"
+               if [ -f "$g/rebase-merge/interactive" ]; then
+                       r="|REBASE-i"
+               else
+                       r="|REBASE-m"
+               fi
        else
-               local r=""
-               local b=""
-               local step=""
-               local total=""
-               if [ -d "$g/rebase-merge" ]; then
-                       b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)"
-                       step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null)
-                       total=$(cat "$g/rebase-merge/end" 2>/dev/null)
-                       if [ -f "$g/rebase-merge/interactive" ]; then
-                               r="|REBASE-i"
+               if [ -d "$g/rebase-apply" ]; then
+                       read step 2>/dev/null <"$g/rebase-apply/next"
+                       read total 2>/dev/null <"$g/rebase-apply/last"
+                       if [ -f "$g/rebase-apply/rebasing" ]; then
+                               read b 2>/dev/null <"$g/rebase-apply/head-name"
+                               r="|REBASE"
+                       elif [ -f "$g/rebase-apply/applying" ]; then
+                               r="|AM"
                        else
-                               r="|REBASE-m"
+                               r="|AM/REBASE"
                        fi
+               elif [ -f "$g/MERGE_HEAD" ]; then
+                       r="|MERGING"
+               elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+                       r="|CHERRY-PICKING"
+               elif [ -f "$g/REVERT_HEAD" ]; then
+                       r="|REVERTING"
+               elif [ -f "$g/BISECT_LOG" ]; then
+                       r="|BISECTING"
+               fi
+
+               if [ -n "$b" ]; then
+                       :
+               elif [ -h "$g/HEAD" ]; then
+                       # symlink symbolic ref
+                       b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -d "$g/rebase-apply" ]; then
-                               step=$(cat "$g/rebase-apply/next" 2>/dev/null)
-                               total=$(cat "$g/rebase-apply/last" 2>/dev/null)
-                               if [ -f "$g/rebase-apply/rebasing" ]; then
-                                       b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)"
-                                       r="|REBASE"
-                               elif [ -f "$g/rebase-apply/applying" ]; then
-                                       r="|AM"
-                               else
-                                       r="|AM/REBASE"
+                       local head=""
+                       if ! read head 2>/dev/null <"$g/HEAD"; then
+                               if [ $pcmode = yes ]; then
+                                       PS1="$ps1pc_start$ps1pc_end"
                                fi
-                       elif [ -f "$g/MERGE_HEAD" ]; then
-                               r="|MERGING"
-                       elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
-                               r="|CHERRY-PICKING"
-                       elif [ -f "$g/REVERT_HEAD" ]; then
-                               r="|REVERTING"
-                       elif [ -f "$g/BISECT_LOG" ]; then
-                               r="|BISECTING"
+                               return
                        fi
-
-                       test -n "$b" ||
-                       b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+                       # is it a symbolic ref?
+                       b="${head#ref: }"
+                       if [ "$head" = "$b" ]; then
                                detached=yes
                                b="$(
                                case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -392,70 +371,75 @@ __git_ps1 ()
                                        git describe --tags --exact-match HEAD ;;
                                esac 2>/dev/null)" ||
 
-                               b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
-                               b="unknown"
+                               b="$short_sha..."
                                b="($b)"
-                       }
+                       fi
                fi
+       fi
 
-               if [ -n "$step" ] && [ -n "$total" ]; then
-                       r="$r $step/$total"
-               fi
+       if [ -n "$step" ] && [ -n "$total" ]; then
+               r="$r $step/$total"
+       fi
 
-               local w=""
-               local i=""
-               local s=""
-               local u=""
-               local c=""
-               local p=""
+       local w=""
+       local i=""
+       local s=""
+       local u=""
+       local c=""
+       local p=""
 
-               if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-                       if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
-                               c="BARE:"
+       if [ "true" = "$inside_gitdir" ]; then
+               if [ "true" = "$bare_repo" ]; then
+                       c="BARE:"
+               else
+                       b="GIT_DIR!"
+               fi
+       elif [ "true" = "$inside_worktree" ]; then
+               if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
+                  [ "$(git config --bool bash.showDirtyState)" != "false" ]
+               then
+                       git diff --no-ext-diff --quiet --exit-code || w="*"
+                       if [ -n "$short_sha" ]; then
+                               git diff-index --cached --quiet HEAD -- || i="+"
                        else
-                               b="GIT_DIR!"
-                       fi
-               elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
-                       if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
-                          [ "$(git config --bool bash.showDirtyState)" != "false" ]
-                       then
-                               git diff --no-ext-diff --quiet --exit-code || w="*"
-                               if git rev-parse --quiet --verify HEAD >/dev/null; then
-                                       git diff-index --cached --quiet HEAD -- || i="+"
-                               else
-                                       i="#"
-                               fi
-                       fi
-                       if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                               i="#"
                        fi
+               fi
+               if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+                  [ -r "$g/refs/stash" ]; then
+                       s="$"
+               fi
 
-                       if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
-                          [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
-                          [ -n "$(git ls-files --others --exclude-standard)" ]
-                       then
-                               u="%${ZSH_VERSION+%}"
-                       fi
+               if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+                  [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+                  git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
+               then
+                       u="%${ZSH_VERSION+%}"
+               fi
 
-                       if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
-                               __git_ps1_show_upstream
-                       fi
+               if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+                       __git_ps1_show_upstream
                fi
+       fi
 
-               local z="${GIT_PS1_STATESEPARATOR-" "}"
-               local f="$w$i$s$u"
-               if [ $pcmode = yes ]; then
-                       local gitstring=
-                       if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-                               __git_ps1_colorize_gitstring
-                       else
-                               gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
-                       fi
+       local z="${GIT_PS1_STATESEPARATOR-" "}"
+
+       # NO color option unless in PROMPT_COMMAND mode
+       if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+               __git_ps1_colorize_gitstring
+       fi
+
+       local f="$w$i$s$u"
+       local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+
+       if [ $pcmode = yes ]; then
+               if [[ -n ${ZSH_VERSION-} ]]; then
                        gitstring=$(printf -- "$printf_format" "$gitstring")
-                       PS1="$ps1pc_start$gitstring$ps1pc_end"
                else
-                       # NO color option unless in PROMPT_COMMAND mode
-                       printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p"
+                       printf -v gitstring -- "$printf_format" "$gitstring"
                fi
+               PS1="$ps1pc_start$gitstring$ps1pc_end"
+       else
+               printf -- "$printf_format" "$gitstring"
        fi
 }
index e40c0347df1f5f1bf9414c021074a8ef8eae9fb5..f8d7d2ca6c0926912200ddcadf8359c1b9bfef44 100755 (executable)
@@ -576,9 +576,9 @@ sub literal_data_raw {
        my ($content) = @_;
        # Avoid confusion between size in bytes and in characters
        utf8::downgrade($content);
-       binmode {*STDOUT}, ':raw';
+       binmode STDOUT, ':raw';
        print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
-       binmode {*STDOUT}, ':encoding(UTF-8)';
+       binmode STDOUT, ':encoding(UTF-8)';
        return;
 }
 
diff --git a/diff.c b/diff.c
index f0b3e7cfe34a99c0e5fea85fcd00ec54e9b578d8..e53ddad7d485c97ca3f0cf0d5784ac712163a857 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2677,6 +2677,14 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 int diff_populate_filespec(struct diff_filespec *s, int size_only)
 {
        int err = 0;
+       /*
+        * demote FAIL to WARN to allow inspecting the situation
+        * instead of refusing.
+        */
+       enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL
+                                   ? SAFE_CRLF_WARN
+                                   : safe_crlf);
+
        if (!DIFF_FILE_VALID(s))
                die("internal error: asking to populate invalid file.");
        if (S_ISDIR(s->mode))
@@ -2732,7 +2740,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                /*
                 * Convert from working tree format to canonical git format
                 */
-               if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
+               if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
@@ -3593,6 +3601,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+       else if (!strcmp(arg, "--ignore-blank-lines"))
+               DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
index c97ac9b463fc1248b2dfe70d5fdf1dc1679e2bf8..401eb72c619d432c92fc66b470fae778117bcace 100644 (file)
@@ -131,8 +131,7 @@ static void diffcore_pickaxe_grep(struct diff_options *o)
        return;
 }
 
-static unsigned int contains(mmfile_t *mf, struct diff_options *o,
-                            regex_t *regexp, kwset_t kws)
+static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
 {
        unsigned int cnt;
        unsigned long sz;
@@ -176,11 +175,9 @@ static int has_changes(mmfile_t *one, mmfile_t *two,
                       struct diff_options *o,
                       regex_t *regexp, kwset_t kws)
 {
-       if (!one)
-               return contains(two, o, regexp, kws) != 0;
-       if (!two)
-               return contains(one, o, regexp, kws) != 0;
-       return contains(one, o, regexp, kws) != contains(two, o, regexp, kws);
+       unsigned int one_contains = one ? contains(one, regexp, kws) : 0;
+       unsigned int two_contains = two ? contains(two, regexp, kws) : 0;
+       return one_contains != two_contains;
 }
 
 static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
diff --git a/dir.c b/dir.c
index 897c87403e978ab2123ce088cf2d3265e21d7dde..048041954418315d73924b3d73c6f2c794a5edb1 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1036,9 +1036,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
                return path_recurse;
 
        case index_gitdir:
-               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
-                       return path_none;
-               return path_untracked;
+               return path_none;
 
        case index_nonexistent:
                if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
index abe5ffbba55037c34b1bf38a86aa0ec8bd7f3447..6684348c0ec9ea742eb98666b56a0bfcef316560 100644 (file)
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include "transport.h"
 #include "version.h"
+#include "prio-queue.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -37,7 +38,7 @@ static int marked;
  */
 #define MAX_IN_VAIN 256
 
-static struct commit_list *rev_list;
+static struct prio_queue rev_list = { compare_commits_by_commit_date };
 static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
 
 static void rev_list_push(struct commit *commit, int mark)
@@ -49,7 +50,7 @@ static void rev_list_push(struct commit *commit, int mark)
                        if (parse_commit(commit))
                                return;
 
-               commit_list_insert_by_date(commit, &rev_list);
+               prio_queue_put(&rev_list, commit);
 
                if (!(commit->object.flags & COMMON))
                        non_common_revs++;
@@ -122,10 +123,10 @@ static const unsigned char *get_rev(void)
                unsigned int mark;
                struct commit_list *parents;
 
-               if (rev_list == NULL || non_common_revs == 0)
+               if (rev_list.nr == 0 || non_common_revs == 0)
                        return NULL;
 
-               commit = rev_list->item;
+               commit = prio_queue_get(&rev_list);
                if (!commit->object.parsed)
                        parse_commit(commit);
                parents = commit->parents;
@@ -152,8 +153,6 @@ static const unsigned char *get_rev(void)
                                mark_common(parents->item, 1, 0);
                        parents = parents->next;
                }
-
-               rev_list = rev_list->next;
        }
 
        return commit->object.sha1;
@@ -442,7 +441,7 @@ static int find_common(struct fetch_pack_args *args,
                                        in_vain = 0;
                                        got_continue = 1;
                                        if (ack == ACK_ready) {
-                                               rev_list = NULL;
+                                               clear_prio_queue(&rev_list);
                                                got_ready = 1;
                                        }
                                        break;
@@ -505,7 +504,7 @@ static int mark_complete(const char *refname, const unsigned char *sha1, int fla
                struct commit *commit = (struct commit *)o;
                if (!(commit->object.flags & COMPLETE)) {
                        commit->object.flags |= COMPLETE;
-                       commit_list_insert_by_date(commit, &complete);
+                       commit_list_insert(commit, &complete);
                }
        }
        return 0;
@@ -622,6 +621,7 @@ static int everything_local(struct fetch_pack_args *args,
        if (!args->depth) {
                for_each_ref(mark_complete, NULL);
                for_each_alternate_ref(mark_alternate_complete, NULL);
+               commit_list_sort_by_date(&complete);
                if (cutoff)
                        mark_recent_complete_commits(args, cutoff);
        }
diff --git a/fixup-builtins b/fixup-builtins
deleted file mode 100755 (executable)
index 63dfa4c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-while [ "$1" ]
-do
-       if [ "$1" != "git-sh-setup" -a "$1" != "git-parse-remote" -a "$1" != "git-svn" ]; then
-               old="$1"
-               new=$(echo "$1" | sed 's/git-/git /')
-               echo "Converting '$old' to '$new'"
-               sed -i "s/\\<$old\\>/$new/g" $(git ls-files '*.sh')
-       fi
-       shift
-done
-
-sed -i 's/git merge-one-file/git-merge-one-file/g
-s/git rebase-todo/git-rebase-todo/g' $(git ls-files '*.sh')
-git update-index --refresh >& /dev/null
-exit 0
index d2c4ce6e1e88f543bd06cd8347e8949fef11a381..75a991f7ec8a2e4926cb925ad9ff23be2f6aedeb 100755 (executable)
@@ -44,6 +44,8 @@
 
 my $normal_color = $repo->get_color("", "reset");
 
+my $diff_algorithm = $repo->config('diff.algorithm');
+
 my $use_readkey = 0;
 my $use_termcap = 0;
 my %term_escapes;
@@ -731,6 +733,9 @@ sub run_git_apply {
 sub parse_diff {
        my ($path) = @_;
        my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+       if (defined $diff_algorithm) {
+               splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
+       }
        if (defined $patch_mode_revision) {
                push @diff_cmd, $patch_mode_revision;
        }
index 9f4450916cd28578002d04a45b4e28e09cca738c..7ea40fe6449b665713a8272f125936edf632189b 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -6,7 +6,7 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git am [options] [(<mbox>|<Maildir>)...]
-git am [options] (--resolved | --skip | --abort)
+git am [options] (--continue | --skip | --abort)
 --
 i,interactive   run interactively
 b,binary*       (historical option -- no-op)
@@ -102,7 +102,7 @@ stop_here_user_resolve () {
            printf '%s\n' "$resolvemsg"
            stop_here $1
     fi
-    eval_gettextln "When you have resolved this problem, run \"\$cmdline --resolved\".
+    eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\".
 If you prefer to skip this patch, run \"\$cmdline --skip\" instead.
 To restore the original branch and stop patching, run \"\$cmdline --abort\"."
 
@@ -523,7 +523,7 @@ Use \"git am --abort\" to remove it.")"
                esac
        fi
 
-       # Make sure we are not given --skip, --resolved, nor --abort
+       # Make sure we are not given --skip, --continue, nor --abort
        test "$skip$resolved$abort" = "" ||
                die "$(gettext "Resolve operation not in progress, we are not resuming.")"
 
@@ -670,7 +670,7 @@ do
        #  - patch is the patch body.
        #
        # When we are resuming, these files are either already prepared
-       # by the user, or the user can tell us to do so by --resolved flag.
+       # by the user, or the user can tell us to do so by --continue flag.
        case "$resume" in
        '')
                if test -f "$dotest/rebasing"
index 911bbce6c5d4e19243e50d5916005872ac672961..88fcf232e5739b14a60f8b2c2a45add4a48e764b 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -3168,7 +3168,7 @@ def rebase(self):
         if os.system("git update-index --refresh") != 0:
             die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
         if len(read_pipe("git diff-index HEAD --")) > 0:
-            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+            die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
 
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
index 638aabb7b347e2afeb9bf327902de9e3702cd9d4..f0df41c841644208c89230516677db162498be26 100755 (executable)
@@ -5,7 +5,7 @@
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
 USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
-LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
@@ -266,10 +266,17 @@ case "$merge_head" in
        ;;
 esac
 
+# Pulling into unborn branch: a shorthand for branching off
+# FETCH_HEAD, for lazy typers.
 if test -z "$orig_head"
 then
-       git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
-       git read-tree -m -u HEAD || exit 1
+       # Two-way merge: we claim the index is based on an empty tree,
+       # and try to fast-forward to HEAD.  This ensures we will not
+       # lose index/worktree changes that the user already made on
+       # the unborn branch.
+       empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+       git read-tree -m -u $empty_tree $merge_head &&
+       git update-ref -m "initial pull" HEAD $merge_head "$curr_head"
        exit
 fi
 
index f953d8d22499b9e41d3c0ed86183655e39d807af..83d6d4676bc1cadcec5ea5889fcf4a0769ee8d31 100644 (file)
@@ -80,6 +80,18 @@ amend="$state_dir"/amend
 rewritten_list="$state_dir"/rewritten-list
 rewritten_pending="$state_dir"/rewritten-pending
 
+strategy_args=
+if test -n "$do_merge"
+then
+       strategy_args=${strategy:+--strategy=$strategy}
+       eval '
+               for strategy_opt in '"$strategy_opts"'
+               do
+                       strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
+               done
+       '
+fi
+
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
@@ -239,7 +251,7 @@ pick_one () {
 
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
-       output git cherry-pick $empty_args $ff "$@"
+       output eval git cherry-pick "$strategy_args" $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -340,9 +352,8 @@ pick_one_preserving_merges () {
                        msg_content="$(commit_message $sha1)"
                        # No point in merging the first parent, that's HEAD
                        new_parents=${new_parents# $first_parent}
-                       if ! do_with_author output \
-                               git merge --no-ff ${strategy:+-s $strategy} -m \
-                                       "$msg_content" $new_parents
+                       if ! do_with_author output eval \
+                       'git merge --no-ff $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
@@ -350,7 +361,7 @@ pick_one_preserving_merges () {
                        echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
                        ;;
                *)
-                       output git cherry-pick "$@" ||
+                       output eval git cherry-pick "$strategy_args" "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"
                        ;;
                esac
@@ -689,8 +700,22 @@ rearrange_squash () {
                case "$message" in
                "squash! "*|"fixup! "*)
                        action="${message%%!*}"
-                       rest="${message#*! }"
-                       echo "$sha1 $action $rest"
+                       rest=$message
+                       prefix=
+                       # skip all squash! or fixup! (but save for later)
+                       while :
+                       do
+                               case "$rest" in
+                               "squash! "*|"fixup! "*)
+                                       prefix="$prefix${rest%%!*},"
+                                       rest="${rest#*! }"
+                                       ;;
+                               *)
+                                       break
+                                       ;;
+                               esac
+                       done
+                       echo "$sha1 $action $prefix $rest"
                        # if it's a single word, try to resolve to a full sha1 and
                        # emit a second copy. This allows us to match on both message
                        # and on sha1 prefix
@@ -699,7 +724,7 @@ rearrange_squash () {
                                if test -n "$fullsha"; then
                                        # prefix the action to uniquely identify this line as
                                        # intended for full sha1 match
-                                       echo "$sha1 +$action $fullsha"
+                                       echo "$sha1 +$action $prefix $fullsha"
                                fi
                        fi
                esac
@@ -714,7 +739,7 @@ rearrange_squash () {
                esac
                printf '%s\n' "$pick $sha1 $message"
                used="$used$sha1 "
-               while read -r squash action msg_content
+               while read -r squash action msg_prefix msg_content
                do
                        case " $used" in
                        *" $squash "*) continue ;;
@@ -730,7 +755,8 @@ rearrange_squash () {
                                case "$message" in "$msg_content"*) emit=1;; esac ;;
                        esac
                        if test $emit = 1; then
-                               printf '%s\n' "$action $squash $action! $msg_content"
+                               real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g")
+                               printf '%s\n' "$action $squash ${real_prefix}$msg_content"
                                used="$used$squash "
                        fi
                done <"$1.sq"
@@ -838,8 +864,11 @@ comment_for_reflog start
 
 if test ! -z "$switch_to"
 then
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
        output git checkout "$switch_to" -- ||
-               die "Could not checkout $switch_to"
+       die "Could not checkout $switch_to"
+
+       comment_for_reflog start
 fi
 
 orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
@@ -981,6 +1010,7 @@ has_action "$todo" ||
 
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
 output git checkout $onto || die_abort "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 do_rest
index 54015e3eaf5e091a387841d9a6e7cdd3085174f8..0039ecfb407960444c31d48767b65cf773a3243a 100755 (executable)
@@ -155,11 +155,8 @@ finish_rebase () {
                then
                        echo "$(gettext 'Applied autostash.')"
                else
-                       ref_stash=refs/stash &&
-                       >>"$GIT_DIR/logs/$ref_stash" &&
-                       git update-ref -m "autostash" $ref_stash $stash_sha1 ||
-                       die "$(eval_gettext 'Cannot store $stash_sha1')"
-
+                       git stash store -m "autostash" -q $stash_sha1 ||
+                       die "$(eval_gettext "Cannot store \$stash_sha1")"
                        gettext 'Applying autostash resulted in conflicts.
 Your changes are safe in the stash.
 You can run "git stash pop" or "git stash drop" it at any time.
@@ -545,7 +542,9 @@ then
        if test -z "$force_rebase"
        then
                # Lazily switch to the target branch if needed...
-               test -z "$switch_to" || git checkout "$switch_to" --
+               test -z "$switch_to" ||
+               GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
+                       git checkout "$switch_to" --
                say "$(eval_gettext "Current branch \$branch_name is up to date.")"
                finish_rebase
                exit 0
@@ -571,7 +570,9 @@ test "$type" = interactive && run_specific_rebase
 
 # Detach HEAD and reset the tree
 say "$(gettext "First, rewinding head to replay your work on top of it...")"
-git checkout -q "$onto^0" || die "could not detach HEAD"
+
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
+       git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 
 # If the $onto is a proper descendant of the tip of the branch, then
index 671762b93031e66cba2ca837ddf71ed765f7a204..45c386337777460ed77fd71026fcfca678b18037 100755 (executable)
@@ -1188,9 +1188,11 @@ sub send_message {
                else {
                        require Net::SMTP;
                        $smtp_domain ||= maildomain();
-                       $smtp ||= Net::SMTP->new(smtp_host_string(),
+                       $smtp_server_port ||= 25;
+                       $smtp ||= Net::SMTP->new($smtp_server,
                                                 Hello => $smtp_domain,
-                                                Debug => $debug_net_smtp);
+                                                Debug => $debug_net_smtp,
+                                                Port => $smtp_server_port);
                        if ($smtp_encryption eq 'tls' && $smtp) {
                                require Net::SMTP::SSL;
                                $smtp->command('STARTTLS');
@@ -1259,6 +1261,7 @@ sub send_message {
        open my $fh, "<", $t or die "can't open file $t";
 
        my $author = undef;
+       my $sauthor = undef;
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
@@ -1297,7 +1300,7 @@ sub send_message {
                        }
                        elsif (/^From:\s+(.*)$/i) {
                                ($author, $author_encoding) = unquote_rfc2047($1);
-                               my $sauthor = sanitize_address($author);
+                               $sauthor = sanitize_address($author);
                                next if $suppress_cc{'author'};
                                next if $suppress_cc{'self'} and $sauthor eq $sender;
                                printf("(mbox) Adding cc: %s from line '%s'\n",
@@ -1393,7 +1396,7 @@ sub send_message {
                $subject = quote_subject($subject, $auto_8bit_encoding);
        }
 
-       if (defined $author and $author ne $sender) {
+       if (defined $sauthor and $sauthor ne $sender) {
                $message = "From: $author\n\n$message";
                if (defined $author_encoding) {
                        if ($has_content_type) {
index bbefdf64244063f4708f381b8a772466d64e90e5..85c9e2c0817c171d67f7458a0764df8d31fb10ad 100755 (executable)
@@ -156,10 +156,46 @@ create_stash () {
        die "$(gettext "Cannot record working tree state")"
 }
 
+store_stash () {
+       while test $# != 0
+       do
+               case "$1" in
+               -m|--message)
+                       shift
+                       stash_msg="$1"
+                       ;;
+               -q|--quiet)
+                       quiet=t
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+       test $# = 1 ||
+       die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
+
+       w_commit="$1"
+       if test -z "$stash_msg"
+       then
+               stash_msg="Created via \"git stash store\"."
+       fi
+
+       # Make sure the reflog for stash is kept.
+       : >>"$GIT_DIR/logs/$ref_stash"
+       git update-ref -m "$stash_msg" $ref_stash $w_commit
+       ret=$?
+       test $ret != 0 && test -z $quiet &&
+       die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
+       return $ret
+}
+
 save_stash () {
        keep_index=
        patch_mode=
        untracked=
+       force=
        while test $# != 0
        do
                case "$1" in
@@ -180,6 +216,9 @@ save_stash () {
                -u|--include-untracked)
                        untracked=untracked
                        ;;
+               -f|--force)
+                       force=t
+                       ;;
                -a|--all)
                        untracked=all
                        ;;
@@ -223,16 +262,20 @@ save_stash () {
                say "$(gettext "No local changes to save")"
                exit 0
        fi
+       if test -z "$untracked$force" &&
+          test -n "$(git ls-files --killed | head -n 1)"
+       then
+               say "$(gettext "The following untracked files would NOT be saved but need to be removed by stash save:")"
+               test -n "$GIT_QUIET" || git ls-files --killed | sed 's/^/\t/'
+               say "$(gettext "Aborting. Consider using either the --force or --include-untracked option.")" >&2
+               exit 1
+       fi
        test -f "$GIT_DIR/logs/$ref_stash" ||
                clear_stash || die "$(gettext "Cannot initialize stash")"
 
        create_stash "$stash_msg" $untracked
-
-       # Make sure the reflog for stash is kept.
-       : >>"$GIT_DIR/logs/$ref_stash"
-
-       git update-ref -m "$stash_msg" $ref_stash $w_commit ||
-               die "$(gettext "Cannot save the current status")"
+       store_stash -m "$stash_msg" -q $w_commit ||
+       die "$(gettext "Cannot save the current status")"
        say Saved working directory and index state "$stash_msg"
 
        if test -z "$patch_mode"
@@ -546,12 +589,13 @@ clear)
        clear_stash "$@"
        ;;
 create)
-       if test $# -gt 0 && test "$1" = create
-       then
-               shift
-       fi
+       shift
        create_stash "$*" && echo "$w_commit"
        ;;
+store)
+       shift
+       store_stash "$@"
+       ;;
 drop)
        shift
        drop_stash "$@"
index 75feaf17492d27f0832828cee1d37ab91d9cbef6..2979197087f2c6d97e2945008394d50c16a195a5 100755 (executable)
@@ -14,10 +14,13 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
 OPTIONS_SPEC=
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 . git-sh-i18n
 . git-parse-remote
 require_work_tree
+wt_prefix=$(git rev-parse --show-prefix)
+cd_to_toplevel
 
 command=
 branch=
@@ -32,6 +35,7 @@ nofetch=
 update=
 prefix=
 custom_name=
+depth=
 
 # The function takes at most 2 arguments. The first argument is the
 # URL that navigates to the submodule origin repo. When relative, this URL
@@ -106,14 +110,50 @@ resolve_relative_url ()
        echo "${is_relative:+${up_path}}${remoteurl#./}"
 }
 
+# Resolve a path to be relative to another path.  This is intended for
+# converting submodule paths when git-submodule is run in a subdirectory
+# and only handles paths where the directory separator is '/'.
+#
+# The output is the first argument as a path relative to the second argument,
+# which defaults to $wt_prefix if it is omitted.
+relative_path ()
+{
+       local target curdir result
+       target=$1
+       curdir=${2-$wt_prefix}
+       curdir=${curdir%/}
+       result=
+
+       while test -n "$curdir"
+       do
+               case "$target" in
+               "$curdir/"*)
+                       target=${target#"$curdir"/}
+                       break
+                       ;;
+               esac
+
+               result="${result}../"
+               if test "$curdir" = "${curdir%/*}"
+               then
+                       curdir=
+               else
+                       curdir="${curdir%/*}"
+               fi
+       done
+
+       echo "$result$target"
+}
+
 #
 # Get submodule info for registered submodules
 # $@ = path to limit submodule list
 #
 module_list()
 {
+       eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
        (
-               git ls-files --error-unmatch --stage -- "$@" ||
+               git ls-files -z --error-unmatch --stage -- "$@" ||
                echo "unmatched pathspec exists"
        ) |
        perl -e '
@@ -121,6 +161,7 @@ module_list()
        my ($null_sha1) = ("0" x 40);
        my @out = ();
        my $unmatched = 0;
+       $/ = "\0";
        while (<STDIN>) {
                if (/^unmatched pathspec/) {
                        $unmatched = 1;
@@ -211,6 +252,7 @@ module_clone()
        name=$2
        url=$3
        reference="$4"
+       depth="$5"
        quiet=
        if test -n "$GIT_QUIET"
        then
@@ -233,7 +275,7 @@ module_clone()
                mkdir -p "$gitdir_base"
                (
                        clear_local_git_env
-                       git clone $quiet -n ${reference:+"$reference"} \
+                       git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
                                --separate-git-dir "$gitdir" "$url" "$sm_path"
                ) ||
                die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
@@ -282,6 +324,7 @@ isnumber()
 cmd_add()
 {
        # parse $args after "submodule ... add".
+       reference_path=
        while test $# -ne 0
        do
                case "$1" in
@@ -298,17 +341,25 @@ cmd_add()
                        ;;
                --reference)
                        case "$2" in '') usage ;; esac
-                       reference="--reference=$2"
+                       reference_path=$2
                        shift
                        ;;
                --reference=*)
-                       reference="$1"
+                       reference_path="${1#--reference=}"
                        ;;
                --name)
                        case "$2" in '') usage ;; esac
                        custom_name=$2
                        shift
                        ;;
+               --depth)
+                       case "$2" in '') usage ;; esac
+                       depth="--depth=$2"
+                       shift
+                       ;;
+               --depth=*)
+                       depth=$1
+                       ;;
                --)
                        shift
                        break
@@ -323,6 +374,14 @@ cmd_add()
                shift
        done
 
+       if test -n "$reference_path"
+       then
+               is_absolute_path "$reference_path" ||
+               reference_path="$wt_prefix$reference_path"
+
+               reference="--reference=$reference_path"
+       fi
+
        repo=$1
        sm_path=$2
 
@@ -335,9 +394,14 @@ cmd_add()
                usage
        fi
 
+       is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
+
        # assure repo is absolute or relative to parent
        case "$repo" in
        ./*|../*)
+               test -z "$wt_prefix" ||
+               die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
+
                # dereference source url relative to parent's url
                realrepo=$(resolve_relative_url "$repo") || exit
                ;;
@@ -405,7 +469,7 @@ Use -f if you really want to add it." >&2
                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
                        fi
                fi
-               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
+               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
                (
                        clear_local_git_env
                        cd "$sm_path" &&
@@ -471,21 +535,23 @@ cmd_foreach()
                die_if_unmatched "$mode"
                if test -e "$sm_path"/.git
                then
-                       say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+                       displaypath=$(relative_path "$sm_path")
+                       say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
                        name=$(module_name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
                                clear_local_git_env
-                               # we make $path available to scripts ...
-                               path=$sm_path
                                cd "$sm_path" &&
+                               sm_path=$(relative_path "$sm_path") &&
+                               # we make $path available to scripts ...
+                               path=$sm_path &&
                                eval "$@" &&
                                if test -n "$recursive"
                                then
                                        cmd_foreach "--recursive" "$@"
                                fi
                        ) <&3 3<&- ||
-                       die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
+                       die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
                fi
        done
 }
@@ -524,12 +590,14 @@ cmd_init()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
+               displaypath=$(relative_path "$sm_path")
+
                # Copy url setting when it is not set yet
                if test -z "$(git config "submodule.$name.url")"
                then
                        url=$(git config -f .gitmodules submodule."$name".url)
                        test -z "$url" &&
-                       die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
+                       die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
 
                        # Possibly a url relative to parent
                        case "$url" in
@@ -538,9 +606,9 @@ cmd_init()
                                ;;
                        esac
                        git config submodule."$name".url "$url" ||
-                       die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
+                       die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
 
-                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
+                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
                fi
 
                # Copy "update" setting when it is not set yet
@@ -548,7 +616,7 @@ cmd_init()
                test -z "$upd" ||
                test -n "$(git config submodule."$name".update)" ||
                git config submodule."$name".update "$upd" ||
-               die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
+               die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
        done
 }
 
@@ -594,27 +662,29 @@ cmd_deinit()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
+               displaypath=$(relative_path "$sm_path")
+
                # Remove the submodule work tree (unless the user already did it)
                if test -d "$sm_path"
                then
                        # Protect submodules containing a .git directory
                        if test -d "$sm_path/.git"
                        then
-                               echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
+                               echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
                                die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
                        fi
 
                        if test -z "$force"
                        then
                                git rm -qn "$sm_path" ||
-                               die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
+                               die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
                        fi
                        rm -rf "$sm_path" &&
-                       say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
-                       say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+                       say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
+                       say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
                fi
 
-               mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
+               mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
 
                # Remove the .git/config entries (unless the user already did it)
                if test -n "$(git config --get-regexp submodule."$name\.")"
@@ -623,7 +693,7 @@ cmd_deinit()
                        # the user later decides to init this submodule again
                        url=$(git config submodule."$name".url)
                        git config --remove-section submodule."$name" 2>/dev/null &&
-                       say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
+                       say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
                fi
        done
 }
@@ -676,6 +746,14 @@ cmd_update()
                --checkout)
                        update="checkout"
                        ;;
+               --depth)
+                       case "$2" in '') usage ;; esac
+                       depth="--depth=$2"
+                       shift
+                       ;;
+               --depth=*)
+                       depth=$1
+                       ;;
                --)
                        shift
                        break
@@ -717,9 +795,11 @@ cmd_update()
                        update_module=$(git config submodule."$name".update)
                fi
 
+               displaypath=$(relative_path "$prefix$sm_path")
+
                if test "$update_module" = "none"
                then
-                       echo "Skipping submodule '$prefix$sm_path'"
+                       echo "Skipping submodule '$displaypath'"
                        continue
                fi
 
@@ -728,20 +808,20 @@ cmd_update()
                        # Only mention uninitialized submodules when its
                        # path have been specified
                        test "$#" != "0" &&
-                       say "$(eval_gettext "Submodule path '\$prefix\$sm_path' not initialized
+                       say "$(eval_gettext "Submodule path '\$displaypath' not initialized
 Maybe you want to use 'update --init'?")"
                        continue
                fi
 
                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$name" "$url" "$reference" || exit
+                       module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
                        subsha1=$(clear_local_git_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
-                       die "$(eval_gettext "Unable to find current revision in submodule path '\$prefix\$sm_path'")"
+                       die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
                fi
 
                if test -n "$remote"
@@ -774,7 +854,7 @@ Maybe you want to use 'update --init'?")"
                                (clear_local_git_env; cd "$sm_path" &&
                                        ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
                                         test -z "$rev") || git-fetch)) ||
-                               die "$(eval_gettext "Unable to fetch in submodule path '\$prefix\$sm_path'")"
+                               die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
                        fi
 
                        # Is this something we just cloned?
@@ -788,20 +868,26 @@ Maybe you want to use 'update --init'?")"
                        case "$update_module" in
                        rebase)
                                command="git rebase"
-                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': rebased into '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': rebased into '\$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        merge)
                                command="git merge"
-                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': merged in '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
+                               must_die_on_failure=yes
+                               ;;
+                       !*)
+                               command="${update_module#!}"
+                               die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule  path '\$prefix\$sm_path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        *)
                                command="git checkout $subforce -q"
-                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': checked out '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
                                ;;
                        esac
 
@@ -828,7 +914,7 @@ Maybe you want to use 'update --init'?")"
                        res=$?
                        if test $res -gt 0
                        then
-                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$prefix\$sm_path'")"
+                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
                                if test $res -eq 1
                                then
                                        err="${err};$die_msg"
@@ -942,6 +1028,7 @@ cmd_summary() {
        fi
 
        cd_to_toplevel
+       eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
        # Get modified modules cared by user
        modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
                sane_egrep '^:([0-7]* )?160000' |
@@ -991,16 +1078,18 @@ cmd_summary() {
                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
                missing_dst=t
 
+               display_name=$(relative_path "$name")
+
                total_commits=
                case "$missing_src,$missing_dst" in
                t,)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_src")"
                        ;;
                ,t)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_dst")"
                        ;;
                t,t)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
                        ;;
                *)
                        errmsg=
@@ -1029,12 +1118,12 @@ cmd_summary() {
                        submodule="$(gettext "submodule")"
                        if test $mod_dst = 160000
                        then
-                               echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
+                               echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
                        else
-                               echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
+                               echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
                        fi
                else
-                       echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+                       echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
                fi
                if test -n "$errmsg"
                then
@@ -1118,7 +1207,7 @@ cmd_status()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               displaypath="$prefix$sm_path"
+               displaypath=$(relative_path "$prefix$sm_path")
                if test "$stage" = U
                then
                        say "U$sha1 $displaypath"
@@ -1213,7 +1302,8 @@ cmd_sync()
 
                if git config "submodule.$name.url" >/dev/null 2>/dev/null
                then
-                       say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
+                       displaypath=$(relative_path "$prefix$sm_path")
+                       say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
                        git config submodule."$name".url "$super_config_url"
 
                        if test -e "$sm_path"/.git
index 36083c1072d928132bd9d144495f8332cdc7003d..ff1ce3d351c8e4e175321d45b7284070f9ce691a 100755 (executable)
@@ -1246,7 +1246,7 @@ sub cmd_rebase {
                return;
        }
        if (command(qw/diff-index HEAD --/)) {
-               print STDERR "Cannot rebase with uncommited changes:\n";
+               print STDERR "Cannot rebase with uncommitted changes:\n";
                command_noisy('status');
                exit 1;
        }
index 9f446798d473cee7dedc82d5d2c820cb0967b375..1d72ec760e9d69e0fc29a699fa60be945f872a93 100755 (executable)
@@ -32,8 +32,9 @@ valid_custom_tool()
 valid_tool() {
        case "$1" in
        firefox | iceweasel | seamonkey | iceape | \
-       chrome | google-chrome | chromium | chromium-browser |\
-       konqueror | opera | w3m | elinks | links | lynx | dillo | open | start)
+       chrome | google-chrome | chromium | chromium-browser | \
+       konqueror | opera | w3m | elinks | links | lynx | dillo | open | \
+       start | cygstart)
                ;; # happy
        *)
                valid_custom_tool "$1" || return 1
@@ -127,6 +128,10 @@ if test -z "$browser" ; then
        if test -x /bin/start; then
                browser_candidates="start $browser_candidates"
        fi
+       # /usr/bin/cygstart indicates Cygwin
+       if test -x /usr/bin/cygstart; then
+               browser_candidates="cygstart $browser_candidates"
+       fi
 
        for i in $browser_candidates; do
                init_browser_path $i
@@ -174,7 +179,7 @@ konqueror)
                ;;
        esac
        ;;
-w3m|elinks|links|lynx|open)
+w3m|elinks|links|lynx|open|cygstart)
        "$browser_path" "$@"
        ;;
 start)
index 8d69ada04291e308e39e999d49278b7af602f28f..f429f75897ed10684d017aeaeb93f1c9d9478e0b 100755 (executable)
@@ -85,6 +85,9 @@ sub evaluate_uri {
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
 our $site_name = "++GITWEB_SITENAME++"
@@ -3982,7 +3985,9 @@ sub print_nav_breadcrumbs_path {
 sub print_nav_breadcrumbs {
        my %opts = @_;
 
-       print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+       for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+       }
        if (defined $project) {
                my @dirname = split '/', $project;
                my $projectbasename = pop @dirname;
diff --git a/http.c b/http.c
index 92aba59082a4ed9aa0c92d8522793abcd332e279..2d086aedfac9a8ea384c53f3b1963149b880ed31 100644 (file)
--- a/http.c
+++ b/http.c
@@ -228,9 +228,15 @@ static void init_curl_http_auth(CURL *result)
 #else
        {
                static struct strbuf up = STRBUF_INIT;
-               strbuf_reset(&up);
-               strbuf_addf(&up, "%s:%s",
-                           http_auth.username, http_auth.password);
+               /*
+                * Note that we assume we only ever have a single set of
+                * credentials in a given program run, so we do not have
+                * to worry about updating this buffer, only setting its
+                * initial value.
+                */
+               if (!up.len)
+                       strbuf_addf(&up, "%s:%s",
+                               http_auth.username, http_auth.password);
                curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
        }
 #endif
index 4bbb09be59e561b519f63c3685fb790de9e70ded..8cc29a0000f8456331457c0d247c242f3113517e 100644 (file)
@@ -116,7 +116,8 @@ static void sort_and_merge_range_set(struct range_set *rs)
 
        for (i = 1; i < rs->nr; i++) {
                if (rs->ranges[i].start <= rs->ranges[o-1].end) {
-                       rs->ranges[o-1].end = rs->ranges[i].end;
+                       if (rs->ranges[o-1].end < rs->ranges[i].end)
+                               rs->ranges[o-1].end = rs->ranges[i].end;
                } else {
                        rs->ranges[o].start = rs->ranges[i].start;
                        rs->ranges[o].end = rs->ranges[i].end;
index c6fb77b26fd88f4edf85a8920a6d72e49551deed..8fbcb6a98aae85b48c6f8de29c6f95af58823411 100644 (file)
@@ -124,15 +124,17 @@ static char *resolve_symlink(char *p, size_t s)
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       if (strlen(path) >= sizeof(lk->filename))
-               return -1;
-       strcpy(lk->filename, path);
        /*
         * subtract 5 from size to make sure there's room for adding
         * ".lock" for the lock file name
         */
+       static const size_t max_path_len = sizeof(lk->filename) - 5;
+
+       if (strlen(path) >= max_path_len)
+               return -1;
+       strcpy(lk->filename, path);
        if (!(flags & LOCK_NODEREF))
-               resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+               resolve_symlink(lk->filename, max_path_len);
        strcat(lk->filename, ".lock");
        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= lk->fd) {
index 2eb69bcfed096d28bff736deca715458605d902a..a49d8e895d3ad24f00e8504ec3eccfbddead6b4c 100644 (file)
@@ -617,6 +617,9 @@ void show_log(struct rev_info *opt)
        ctx.fmt = opt->commit_format;
        ctx.mailmap = opt->mailmap;
        ctx.color = opt->diffopt.use_color;
+       ctx.output_encoding = get_log_output_encoding();
+       if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
+               ctx.from_ident = &opt->from_ident;
        pretty_print_commit(&ctx, commit, &msgbuf);
 
        if (opt->add_signoff)
index 9e431545d8e6c14401faea5e76296974ca1d6649..74563c92b4cdce8e947a2816a8b56355b11439e6 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -406,7 +406,7 @@ static const char *show_ident_date(const struct ident_split *ident,
        return show_date(date, tz, mode);
 }
 
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
 {
@@ -432,6 +432,23 @@ void pp_user_info(const struct pretty_print_context *pp,
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
        if (pp->fmt == CMIT_FMT_EMAIL) {
+               if (pp->from_ident) {
+                       struct strbuf buf = STRBUF_INIT;
+
+                       strbuf_addstr(&buf, "From: ");
+                       strbuf_add(&buf, namebuf, namelen);
+                       strbuf_addstr(&buf, " <");
+                       strbuf_add(&buf, mailbuf, maillen);
+                       strbuf_addstr(&buf, ">\n");
+                       string_list_append(&pp->in_body_headers,
+                                          strbuf_detach(&buf, NULL));
+
+                       mailbuf = pp->from_ident->mail_begin;
+                       maillen = pp->from_ident->mail_end - mailbuf;
+                       namebuf = pp->from_ident->name_begin;
+                       namelen = pp->from_ident->name_end - namebuf;
+               }
+
                strbuf_addstr(sb, "From: ");
                if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
                        add_rfc2047(sb, namebuf, namelen,
@@ -1514,7 +1531,7 @@ void format_commit_message(const struct commit *commit,
        free(context.signature_check.signer);
 }
 
-static void pp_header(const struct pretty_print_context *pp,
+static void pp_header(struct pretty_print_context *pp,
                      const char *encoding,
                      const struct commit *commit,
                      const char **msg_p,
@@ -1575,7 +1592,7 @@ static void pp_header(const struct pretty_print_context *pp,
        }
 }
 
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
                   const char *encoding,
@@ -1602,6 +1619,16 @@ void pp_title_line(const struct pretty_print_context *pp,
        }
        strbuf_addch(sb, '\n');
 
+       if (need_8bit_cte == 0) {
+               int i;
+               for (i = 0; i < pp->in_body_headers.nr; i++) {
+                       if (has_non_ascii(pp->in_body_headers.items[i].string)) {
+                               need_8bit_cte = 1;
+                               break;
+                       }
+               }
+       }
+
        if (need_8bit_cte > 0) {
                const char *header_fmt =
                        "MIME-Version: 1.0\n"
@@ -1615,10 +1642,21 @@ void pp_title_line(const struct pretty_print_context *pp,
        if (pp->fmt == CMIT_FMT_EMAIL) {
                strbuf_addch(sb, '\n');
        }
+
+       if (pp->in_body_headers.nr) {
+               int i;
+               for (i = 0; i < pp->in_body_headers.nr; i++) {
+                       strbuf_addstr(sb, pp->in_body_headers.items[i].string);
+                       free(pp->in_body_headers.items[i].string);
+               }
+               string_list_clear(&pp->in_body_headers, 0);
+               strbuf_addch(sb, '\n');
+       }
+
        strbuf_release(&title);
 }
 
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent)
@@ -1650,7 +1688,7 @@ void pp_remainder(const struct pretty_print_context *pp,
        }
 }
 
-void pretty_print_commit(const struct pretty_print_context *pp,
+void pretty_print_commit(struct pretty_print_context *pp,
                         const struct commit *commit,
                         struct strbuf *sb)
 {
diff --git a/prio-queue.c b/prio-queue.c
new file mode 100644 (file)
index 0000000..c9f8c6d
--- /dev/null
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "commit.h"
+#include "prio-queue.h"
+
+void prio_queue_reverse(struct prio_queue *queue)
+{
+       int i, j;
+
+       if (queue->compare != NULL)
+               die("BUG: prio_queue_reverse() on non-LIFO queue");
+       for (i = 0; i <= (j = (queue->nr - 1) - i); i++) {
+               struct commit *swap = queue->array[i];
+               queue->array[i] = queue->array[j];
+               queue->array[j] = swap;
+       }
+}
+
+void clear_prio_queue(struct prio_queue *queue)
+{
+       free(queue->array);
+       queue->nr = 0;
+       queue->alloc = 0;
+       queue->array = NULL;
+}
+
+void prio_queue_put(struct prio_queue *queue, void *thing)
+{
+       prio_queue_compare_fn compare = queue->compare;
+       int ix, parent;
+
+       /* Append at the end */
+       ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
+       queue->array[queue->nr++] = thing;
+       if (!compare)
+               return; /* LIFO */
+
+       /* Bubble up the new one */
+       for (ix = queue->nr - 1; ix; ix = parent) {
+               parent = (ix - 1) / 2;
+               if (compare(queue->array[parent], queue->array[ix],
+                           queue->cb_data) <= 0)
+                       break;
+
+               thing = queue->array[parent];
+               queue->array[parent] = queue->array[ix];
+               queue->array[ix] = thing;
+       }
+}
+
+void *prio_queue_get(struct prio_queue *queue)
+{
+       void *result, *swap;
+       int ix, child;
+       prio_queue_compare_fn compare = queue->compare;
+
+       if (!queue->nr)
+               return NULL;
+       if (!compare)
+               return queue->array[--queue->nr]; /* LIFO */
+
+       result = queue->array[0];
+       if (!--queue->nr)
+               return result;
+
+       queue->array[0] = queue->array[queue->nr];
+
+       /* Push down the one at the root */
+       for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
+               child = ix * 2 + 1; /* left */
+               if ((child + 1 < queue->nr) &&
+                   (compare(queue->array[child], queue->array[child + 1],
+                            queue->cb_data) >= 0))
+                       child++; /* use right child */
+
+               if (compare(queue->array[ix], queue->array[child],
+                           queue->cb_data) <= 0)
+                       break;
+
+               swap = queue->array[child];
+               queue->array[child] = queue->array[ix];
+               queue->array[ix] = swap;
+       }
+       return result;
+}
diff --git a/prio-queue.h b/prio-queue.h
new file mode 100644 (file)
index 0000000..9c3cd1f
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef PRIO_QUEUE_H
+#define PRIO_QUEUE_H
+
+/*
+ * A priority queue implementation, primarily for keeping track of
+ * commits in the 'date-order' so that we process them from new to old
+ * as they are discovered, but can be used to hold any pointer to
+ * struct.  The caller is responsible for supplying a function to
+ * compare two "things".
+ *
+ * Alternatively, this data structure can also be used as a LIFO stack
+ * by specifying NULL as the comparison function.
+ */
+
+/*
+ * Compare two "things", one and two; the third parameter is cb_data
+ * in the prio_queue structure.  The result is returned as a sign of
+ * the return value, being the same as the sign of the result of
+ * subtracting "two" from "one" (i.e. negative if "one" sorts earlier
+ * than "two").
+ */
+typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
+
+struct prio_queue {
+       prio_queue_compare_fn compare;
+       void *cb_data;
+       int alloc, nr;
+       void **array;
+};
+
+/*
+ * Add the "thing" to the queue.
+ */
+extern void prio_queue_put(struct prio_queue *, void *thing);
+
+/*
+ * Extract the "thing" that compares the smallest out of the queue,
+ * or NULL.  If compare function is NULL, the queue acts as a LIFO
+ * stack.
+ */
+extern void *prio_queue_get(struct prio_queue *);
+
+extern void clear_prio_queue(struct prio_queue *);
+
+/* Reverse the LIFO elements */
+extern void prio_queue_reverse(struct prio_queue *);
+
+#endif /* PRIO_QUEUE_H */
index b297addb576dec45fa9b82ef4a0ffb350f9cfc6c..d5201f9b06c3f5ac53b8a6f5396fbf3392f1b3b2 100644 (file)
@@ -67,6 +67,61 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
        add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+       sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+       sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+       sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+       sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+       sd->sd_dev = st->st_dev;
+       sd->sd_ino = st->st_ino;
+       sd->sd_uid = st->st_uid;
+       sd->sd_gid = st->st_gid;
+       sd->sd_size = st->st_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+       int changed = 0;
+
+       if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+               changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+       if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+               changed |= CTIME_CHANGED;
+#endif
+
+       if (check_stat) {
+               if (sd->sd_uid != (unsigned int) st->st_uid ||
+                       sd->sd_gid != (unsigned int) st->st_gid)
+                       changed |= OWNER_CHANGED;
+               if (sd->sd_ino != (unsigned int) st->st_ino)
+                       changed |= INODE_CHANGED;
+       }
+
+#ifdef USE_STDEV
+       /*
+        * st_dev breaks on network filesystems where different
+        * clients will have different views of what "device"
+        * the filesystem is on
+        */
+       if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+                       changed |= INODE_CHANGED;
+#endif
+
+       if (sd->sd_size != (unsigned int) st->st_size)
+               changed |= DATA_CHANGED;
+
+       return changed;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -74,15 +129,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-       ce->ce_ctime.sec = (unsigned int)st->st_ctime;
-       ce->ce_mtime.sec = (unsigned int)st->st_mtime;
-       ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
-       ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
-       ce->ce_dev = st->st_dev;
-       ce->ce_ino = st->st_ino;
-       ce->ce_uid = st->st_uid;
-       ce->ce_gid = st->st_gid;
-       ce->ce_size = st->st_size;
+       fill_stat_data(&ce->ce_stat_data, st);
 
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
@@ -195,43 +242,11 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
        default:
                die("internal error: ce_mode is %o", ce->ce_mode);
        }
-       if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           ce->ce_ctime.sec != (unsigned int)st->st_ctime)
-               changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
-       if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
-               changed |= CTIME_CHANGED;
-#endif
-
-       if (check_stat) {
-               if (ce->ce_uid != (unsigned int) st->st_uid ||
-                       ce->ce_gid != (unsigned int) st->st_gid)
-                       changed |= OWNER_CHANGED;
-               if (ce->ce_ino != (unsigned int) st->st_ino)
-                       changed |= INODE_CHANGED;
-       }
 
-#ifdef USE_STDEV
-       /*
-        * st_dev breaks on network filesystems where different
-        * clients will have different views of what "device"
-        * the filesystem is on
-        */
-       if (check_stat && ce->ce_dev != (unsigned int) st->st_dev)
-                       changed |= INODE_CHANGED;
-#endif
-
-       if (ce->ce_size != (unsigned int) st->st_size)
-               changed |= DATA_CHANGED;
+       changed |= match_stat_data(&ce->ce_stat_data, st);
 
        /* Racily smudged entry? */
-       if (!ce->ce_size) {
+       if (!ce->ce_stat_data.sd_size) {
                if (!is_empty_blob_sha1(ce->sha1))
                        changed |= DATA_CHANGED;
        }
@@ -246,11 +261,11 @@ static int is_racy_timestamp(const struct index_state *istate,
                istate->timestamp.sec &&
 #ifdef USE_NSEC
                 /* nanosecond timestamped files can also be racy! */
-               (istate->timestamp.sec < ce->ce_mtime.sec ||
-                (istate->timestamp.sec == ce->ce_mtime.sec &&
-                 istate->timestamp.nsec <= ce->ce_mtime.nsec))
+               (istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
+                (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
+                 istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
 #else
-               istate->timestamp.sec <= ce->ce_mtime.sec
+               istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
 #endif
                 );
 }
@@ -342,7 +357,7 @@ int ie_modified(const struct index_state *istate,
         * then we know it is.
         */
        if ((changed & DATA_CHANGED) &&
-           (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0))
+           (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
 
        changed_fs = ce_modified_check_fs(ce, st);
@@ -1324,16 +1339,16 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
 {
        struct cache_entry *ce = xmalloc(cache_entry_size(len));
 
-       ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
-       ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
-       ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
-       ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
-       ce->ce_dev   = ntoh_l(ondisk->dev);
-       ce->ce_ino   = ntoh_l(ondisk->ino);
+       ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
+       ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
+       ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
+       ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+       ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
+       ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
        ce->ce_mode  = ntoh_l(ondisk->mode);
-       ce->ce_uid   = ntoh_l(ondisk->uid);
-       ce->ce_gid   = ntoh_l(ondisk->gid);
-       ce->ce_size  = ntoh_l(ondisk->size);
+       ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
+       ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
+       ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
@@ -1611,7 +1626,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
         * The only thing we care about in this function is to smudge the
         * falsely clean entry due to touch-update-touch race, so we leave
         * everything else as they are.  We are called for entries whose
-        * ce_mtime match the index file mtime.
+        * ce_stat_data.sd_mtime match the index file mtime.
         *
         * Note that this actually does not do much for gitlinks, for
         * which ce_match_stat_basic() always goes to the actual
@@ -1650,7 +1665,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
                 * file, and never calls us, so the cached size information
                 * for "frotz" stays 6 which does not match the filesystem.
                 */
-               ce->ce_size = 0;
+               ce->ce_stat_data.sd_size = 0;
        }
 }
 
@@ -1660,16 +1675,16 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
 {
        short flags;
 
-       ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
-       ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
-       ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
-       ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
-       ondisk->dev  = htonl(ce->ce_dev);
-       ondisk->ino  = htonl(ce->ce_ino);
+       ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
+       ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
+       ondisk->ctime.nsec = htonl(ce->ce_stat_data.sd_ctime.nsec);
+       ondisk->mtime.nsec = htonl(ce->ce_stat_data.sd_mtime.nsec);
+       ondisk->dev  = htonl(ce->ce_stat_data.sd_dev);
+       ondisk->ino  = htonl(ce->ce_stat_data.sd_ino);
        ondisk->mode = htonl(ce->ce_mode);
-       ondisk->uid  = htonl(ce->ce_uid);
-       ondisk->gid  = htonl(ce->ce_gid);
-       ondisk->size = htonl(ce->ce_size);
+       ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
+       ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
+       ondisk->size = htonl(ce->ce_stat_data.sd_size);
        hashcpy(ondisk->sha1, ce->sha1);
 
        flags = ce->ce_flags;
@@ -1936,3 +1951,33 @@ void *read_blob_data_from_index(struct index_state *istate, const char *path, un
                *size = sz;
        return data;
 }
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+       free(sv->sd);
+       sv->sd = NULL;
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+       struct stat st;
+
+       if (stat(path, &st) < 0)
+               return sv->sd == NULL;
+       if (!sv->sd)
+               return 0;
+       return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+       struct stat st;
+
+       if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+               stat_validity_clear(sv);
+       else {
+               if (!sv->sd)
+                       sv->sd = xcalloc(1, sizeof(struct stat_data));
+               fill_stat_data(sv->sd, &st);
+       }
+}
diff --git a/refs.c b/refs.c
index 42a7e17f6bdf6f03f56a012c87c137b80452ea17..43022066499ffaf828ac31e82b77f53539bc232d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -749,6 +749,21 @@ static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
        }
 }
 
+/*
+ * Load all of the refs from the dir into our in-memory cache. The hard work
+ * of loading loose refs is done by get_ref_dir(), so we just need to recurse
+ * through all of the sub-directories. We do not even need to care about
+ * sorting, as traversal order does not matter to us.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+       int i;
+       for (i = 0; i < dir->nr; i++) {
+               struct ref_entry *entry = dir->entries[i];
+               if (entry->flag & REF_DIR)
+                       prime_ref_dir(get_ref_dir(entry));
+       }
+}
 /*
  * Return true iff refname1 and refname2 conflict with each other.
  * Two reference names conflict if one of them exactly matches the
@@ -806,6 +821,30 @@ static int is_refname_available(const char *refname, const char *oldrefname,
        return 1;
 }
 
+struct packed_ref_cache {
+       struct ref_entry *root;
+
+       /*
+        * Count of references to the data structure in this instance,
+        * including the pointer from ref_cache::packed if any.  The
+        * data will not be freed as long as the reference count is
+        * nonzero.
+        */
+       unsigned int referrers;
+
+       /*
+        * Iff the packed-refs file associated with this instance is
+        * currently locked for writing, this points at the associated
+        * lock (which is owned by somebody else).  The referrer count
+        * is also incremented when the file is locked and decremented
+        * when it is unlocked.
+        */
+       struct lock_file *lock;
+
+       /* The metadata from when this packed-refs cache was read */
+       struct stat_validity validity;
+};
+
 /*
  * Future: need to be in "struct repository"
  * when doing a full libification.
@@ -813,7 +852,7 @@ static int is_refname_available(const char *refname, const char *oldrefname,
 static struct ref_cache {
        struct ref_cache *next;
        struct ref_entry *loose;
-       struct ref_entry *packed;
+       struct packed_ref_cache *packed;
        /*
         * The submodule name, or "" for the main repo.  We allocate
         * length 1 rather than FLEX_ARRAY so that the main ref_cache
@@ -822,11 +861,42 @@ static struct ref_cache {
        char name[1];
 } ref_cache, *submodule_ref_caches;
 
+/* Lock used for the main packed-refs file: */
+static struct lock_file packlock;
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs.  If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       if (!--packed_refs->referrers) {
+               free_ref_entry(packed_refs->root);
+               stat_validity_clear(&packed_refs->validity);
+               free(packed_refs);
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
        if (refs->packed) {
-               free_ref_entry(refs->packed);
+               struct packed_ref_cache *packed_refs = refs->packed;
+
+               if (packed_refs->lock)
+                       die("internal error: packed-ref cache cleared while locked");
                refs->packed = NULL;
+               release_packed_ref_cache(packed_refs);
        }
 }
 
@@ -996,29 +1066,57 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
        }
 }
 
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+/*
+ * Get the packed_ref_cache for the specified ref_cache, creating it
+ * if necessary.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
 {
+       const char *packed_refs_file;
+
+       if (*refs->name)
+               packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+       else
+               packed_refs_file = git_path("packed-refs");
+
+       if (refs->packed &&
+           !stat_validity_check(&refs->packed->validity, packed_refs_file))
+               clear_packed_ref_cache(refs);
+
        if (!refs->packed) {
-               const char *packed_refs_file;
                FILE *f;
 
-               refs->packed = create_dir_entry(refs, "", 0, 0);
-               if (*refs->name)
-                       packed_refs_file = git_path_submodule(refs->name, "packed-refs");
-               else
-                       packed_refs_file = git_path("packed-refs");
+               refs->packed = xcalloc(1, sizeof(*refs->packed));
+               acquire_packed_ref_cache(refs->packed);
+               refs->packed->root = create_dir_entry(refs, "", 0, 0);
                f = fopen(packed_refs_file, "r");
                if (f) {
-                       read_packed_refs(f, get_ref_dir(refs->packed));
+                       stat_validity_update(&refs->packed->validity, fileno(f));
+                       read_packed_refs(f, get_ref_dir(refs->packed->root));
                        fclose(f);
                }
        }
-       return get_ref_dir(refs->packed);
+       return refs->packed;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+       return get_ref_dir(packed_ref_cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+{
+       return get_packed_ref_dir(get_packed_ref_cache(refs));
 }
 
 void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
-       add_ref(get_packed_refs(&ref_cache),
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed refs not locked");
+       add_ref(get_packed_ref_dir(packed_ref_cache),
                create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 }
 
@@ -1197,6 +1295,37 @@ static struct ref_entry *get_packed_ref(const char *refname)
        return find_ref(get_packed_refs(&ref_cache), refname);
 }
 
+/*
+ * A loose ref file doesn't exist; check for a packed ref.  The
+ * options are forwarded from resolve_safe_unsafe().
+ */
+static const char *handle_missing_loose_ref(const char *refname,
+                                           unsigned char *sha1,
+                                           int reading,
+                                           int *flag)
+{
+       struct ref_entry *entry;
+
+       /*
+        * The loose reference file does not exist; check for a packed
+        * reference.
+        */
+       entry = get_packed_ref(refname);
+       if (entry) {
+               hashcpy(sha1, entry->u.value.sha1);
+               if (flag)
+                       *flag |= REF_ISPACKED;
+               return refname;
+       }
+       /* The reference is not a packed reference, either. */
+       if (reading) {
+               return NULL;
+       } else {
+               hashclr(sha1);
+               return refname;
+       }
+}
+
 const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH;
@@ -1221,36 +1350,34 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
 
                git_snpath(path, sizeof(path), "%s", refname);
 
+               /*
+                * We might have to loop back here to avoid a race
+                * condition: first we lstat() the file, then we try
+                * to read it as a link or as a file.  But if somebody
+                * changes the type of the file (file <-> directory
+                * <-> symlink) between the lstat() and reading, then
+                * we don't want to report that as an error but rather
+                * try again starting with the lstat().
+                */
+       stat_ref:
                if (lstat(path, &st) < 0) {
-                       struct ref_entry *entry;
-
-                       if (errno != ENOENT)
+                       if (errno == ENOENT)
+                               return handle_missing_loose_ref(refname, sha1,
+                                                               reading, flag);
+                       else
                                return NULL;
-                       /*
-                        * The loose reference file does not exist;
-                        * check for a packed reference.
-                        */
-                       entry = get_packed_ref(refname);
-                       if (entry) {
-                               hashcpy(sha1, entry->u.value.sha1);
-                               if (flag)
-                                       *flag |= REF_ISPACKED;
-                               return refname;
-                       }
-                       /* The reference is not a packed reference, either. */
-                       if (reading) {
-                               return NULL;
-                       } else {
-                               hashclr(sha1);
-                               return refname;
-                       }
                }
 
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
-                       if (len < 0)
-                               return NULL;
+                       if (len < 0) {
+                               if (errno == ENOENT || errno == EINVAL)
+                                       /* inconsistent with lstat; retry */
+                                       goto stat_ref;
+                               else
+                                       return NULL;
+                       }
                        buffer[len] = 0;
                        if (!prefixcmp(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
@@ -1273,8 +1400,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                 * a ref
                 */
                fd = open(path, O_RDONLY);
-               if (fd < 0)
-                       return NULL;
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               /* inconsistent with lstat; retry */
+                               goto stat_ref;
+                       else
+                               return NULL;
+               }
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
                if (len < 0)
@@ -1286,8 +1418,19 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                /*
                 * Is it a symbolic ref?
                 */
-               if (prefixcmp(buffer, "ref:"))
-                       break;
+               if (prefixcmp(buffer, "ref:")) {
+                       /*
+                        * Please note that FETCH_HEAD has a second
+                        * line containing other data.
+                        */
+                       if (get_sha1_hex(buffer, sha1) ||
+                           (buffer[40] != '\0' && !isspace(buffer[40]))) {
+                               if (flag)
+                                       *flag |= REF_ISBROKEN;
+                               return NULL;
+                       }
+                       return refname;
+               }
                if (flag)
                        *flag |= REF_ISSYMREF;
                buf = buffer + 4;
@@ -1300,13 +1443,6 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                }
                refname = strcpy(refname_buffer, buf);
        }
-       /* Please note that FETCH_HEAD has a second line containing other data. */
-       if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
-               if (flag)
-                       *flag |= REF_ISBROKEN;
-               return NULL;
-       }
-       return refname;
 }
 
 char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -1520,14 +1656,32 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 static int do_for_each_entry(struct ref_cache *refs, const char *base,
                             each_ref_entry_fn fn, void *cb_data)
 {
-       struct ref_dir *packed_dir = get_packed_refs(refs);
-       struct ref_dir *loose_dir = get_loose_refs(refs);
+       struct packed_ref_cache *packed_ref_cache;
+       struct ref_dir *loose_dir;
+       struct ref_dir *packed_dir;
        int retval = 0;
 
+       /*
+        * We must make sure that all loose refs are read before accessing the
+        * packed-refs file; this avoids a race condition in which loose refs
+        * are migrated to the packed-refs file by a simultaneous process, but
+        * our in-memory view is from before the migration. get_packed_ref_cache()
+        * takes care of making sure our view is up to date with what is on
+        * disk.
+        */
+       loose_dir = get_loose_refs(refs);
        if (base && *base) {
-               packed_dir = find_containing_dir(packed_dir, base, 0);
                loose_dir = find_containing_dir(loose_dir, base, 0);
        }
+       if (loose_dir)
+               prime_ref_dir(loose_dir);
+
+       packed_ref_cache = get_packed_ref_cache(refs);
+       acquire_packed_ref_cache(packed_ref_cache);
+       packed_dir = get_packed_ref_dir(packed_ref_cache);
+       if (base && *base) {
+               packed_dir = find_containing_dir(packed_dir, base, 0);
+       }
 
        if (packed_dir && loose_dir) {
                sort_ref_dir(packed_dir);
@@ -1544,6 +1698,7 @@ static int do_for_each_entry(struct ref_cache *refs, const char *base,
                                loose_dir, 0, fn, cb_data);
        }
 
+       release_packed_ref_cache(packed_ref_cache);
        return retval;
 }
 
@@ -1998,6 +2153,73 @@ static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
        }
 }
 
+/*
+ * An each_ref_entry_fn that writes the entry to a packed-refs file.
+ */
+static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
+{
+       int *fd = cb_data;
+       enum peel_status peel_status = peel_entry(entry, 0);
+
+       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+               error("internal error: %s is not a valid packed reference!",
+                     entry->name);
+       write_packed_entry(*fd, entry->name, entry->u.value.sha1,
+                          peel_status == PEEL_PEELED ?
+                          entry->u.value.peeled : NULL);
+       return 0;
+}
+
+int lock_packed_refs(int flags)
+{
+       struct packed_ref_cache *packed_ref_cache;
+
+       /* Discard the old cache because it might be invalid: */
+       clear_packed_ref_cache(&ref_cache);
+       if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+               return -1;
+       /* Read the current packed-refs while holding the lock: */
+       packed_ref_cache = get_packed_ref_cache(&ref_cache);
+       packed_ref_cache->lock = &packlock;
+       /* Increment the reference count to prevent it from being freed: */
+       acquire_packed_ref_cache(packed_ref_cache);
+       return 0;
+}
+
+int commit_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+       int error = 0;
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+       write_or_die(packed_ref_cache->lock->fd,
+                    PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+
+       do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
+                                0, write_packed_entry_fn,
+                                &packed_ref_cache->lock->fd);
+       if (commit_lock_file(packed_ref_cache->lock))
+               error = -1;
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       return error;
+}
+
+void rollback_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+       rollback_lock_file(packed_ref_cache->lock);
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       clear_packed_ref_cache(&ref_cache);
+}
+
 struct ref_to_prune {
        struct ref_to_prune *next;
        unsigned char sha1[20];
@@ -2006,35 +2228,50 @@ struct ref_to_prune {
 
 struct pack_refs_cb_data {
        unsigned int flags;
+       struct ref_dir *packed_refs;
        struct ref_to_prune *ref_to_prune;
-       int fd;
 };
 
-static int pack_one_ref(struct ref_entry *entry, void *cb_data)
+/*
+ * An each_ref_entry_fn that is run over loose references only.  If
+ * the loose reference can be packed, add an entry in the packed ref
+ * cache.  If the reference should be pruned, also add it to
+ * ref_to_prune in the pack_refs_cb_data.
+ */
+static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
 {
        struct pack_refs_cb_data *cb = cb_data;
        enum peel_status peel_status;
+       struct ref_entry *packed_entry;
        int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
 
-       /* ALWAYS pack refs that were already packed or are tags */
-       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref &&
-           !(entry->flag & REF_ISPACKED))
+       /* ALWAYS pack tags */
+       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
                return 0;
 
        /* Do not pack symbolic or broken refs: */
        if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
                return 0;
 
+       /* Add a packed ref cache entry equivalent to the loose entry. */
        peel_status = peel_entry(entry, 1);
        if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
                die("internal error peeling reference %s (%s)",
                    entry->name, sha1_to_hex(entry->u.value.sha1));
-       write_packed_entry(cb->fd, entry->name, entry->u.value.sha1,
-                          peel_status == PEEL_PEELED ?
-                          entry->u.value.peeled : NULL);
+       packed_entry = find_ref(cb->packed_refs, entry->name);
+       if (packed_entry) {
+               /* Overwrite existing packed entry with info from loose entry */
+               packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
+               hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+       } else {
+               packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+                                               REF_ISPACKED | REF_KNOWS_PEELED, 0);
+               add_ref(cb->packed_refs, packed_entry);
+       }
+       hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
 
-       /* If the ref was already packed, there is no need to prune it. */
-       if ((cb->flags & PACK_REFS_PRUNE) && !(entry->flag & REF_ISPACKED)) {
+       /* Schedule the loose reference for pruning if requested. */
+       if ((cb->flags & PACK_REFS_PRUNE)) {
                int namelen = strlen(entry->name) + 1;
                struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
                hashcpy(n->sha1, entry->u.value.sha1);
@@ -2096,8 +2333,6 @@ static void prune_refs(struct ref_to_prune *r)
        }
 }
 
-static struct lock_file packlock;
-
 int pack_refs(unsigned int flags)
 {
        struct pack_refs_cb_data cbdata;
@@ -2105,26 +2340,38 @@ int pack_refs(unsigned int flags)
        memset(&cbdata, 0, sizeof(cbdata));
        cbdata.flags = flags;
 
-       cbdata.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"),
-                                             LOCK_DIE_ON_ERROR);
+       lock_packed_refs(LOCK_DIE_ON_ERROR);
+       cbdata.packed_refs = get_packed_refs(&ref_cache);
 
-       write_or_die(cbdata.fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+       do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+                                pack_if_possible_fn, &cbdata);
 
-       do_for_each_entry(&ref_cache, "", pack_one_ref, &cbdata);
-       if (commit_lock_file(&packlock) < 0)
+       if (commit_packed_refs())
                die_errno("unable to overwrite old ref-pack file");
+
        prune_refs(cbdata.ref_to_prune);
        return 0;
 }
 
-static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
+/*
+ * If entry is no longer needed in packed-refs, add it to the string
+ * list pointed to by cb_data.  Reasons for deleting entries:
+ *
+ * - Entry is broken.
+ * - Entry is overridden by a loose ref.
+ * - Entry does not point at a valid object.
+ *
+ * In the first and third cases, also emit an error message because these
+ * are indications of repository corruption.
+ */
+static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
 {
-       int *fd = cb_data;
-       enum peel_status peel_status;
+       struct string_list *refs_to_delete = cb_data;
 
        if (entry->flag & REF_ISBROKEN) {
                /* This shouldn't happen to packed refs. */
                error("%s is broken!", entry->name);
+               string_list_append(refs_to_delete, entry->name);
                return 0;
        }
        if (!has_sha1_file(entry->u.value.sha1)) {
@@ -2134,7 +2381,7 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
                if (read_ref_full(entry->name, sha1, 0, &flags))
                        /* We should at least have found the packed ref. */
                        die("Internal error");
-               if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED))
+               if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
                        /*
                         * This packed reference is overridden by a
                         * loose reference, so it is OK that its value
@@ -2143,9 +2390,11 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
                         * collected.  For this purpose we don't even
                         * care whether the loose reference itself is
                         * invalid, broken, symbolic, etc.  Silently
-                        * omit the packed reference from the output.
+                        * remove the packed reference.
                         */
+                       string_list_append(refs_to_delete, entry->name);
                        return 0;
+               }
                /*
                 * There is no overriding loose reference, so the fact
                 * that this reference doesn't refer to a valid object
@@ -2154,44 +2403,47 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
                 * the output.
                 */
                error("%s does not point to a valid object!", entry->name);
+               string_list_append(refs_to_delete, entry->name);
                return 0;
        }
 
-       peel_status = peel_entry(entry, 0);
-       write_packed_entry(*fd, entry->name, entry->u.value.sha1,
-                          peel_status == PEEL_PEELED ?
-                          entry->u.value.peeled : NULL);
-
        return 0;
 }
 
 static int repack_without_ref(const char *refname)
 {
-       int fd;
        struct ref_dir *packed;
+       struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+       struct string_list_item *ref_to_delete;
 
        if (!get_packed_ref(refname))
                return 0; /* refname does not exist in packed refs */
 
-       fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
-       if (fd < 0) {
+       if (lock_packed_refs(0)) {
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
        }
-       clear_packed_ref_cache(&ref_cache);
        packed = get_packed_refs(&ref_cache);
-       /* Remove refname from the cache. */
+
+       /* Remove refname from the cache: */
        if (remove_entry(packed, refname) == -1) {
                /*
                 * The packed entry disappeared while we were
                 * acquiring the lock.
                 */
-               rollback_lock_file(&packlock);
+               rollback_packed_refs();
                return 0;
        }
-       write_or_die(fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
-       do_for_each_entry_in_dir(packed, 0, repack_ref_fn, &fd);
-       return commit_lock_file(&packlock);
+
+       /* Remove any other accumulated cruft: */
+       do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
+       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+               if (remove_entry(packed, ref_to_delete->string) == -1)
+                       die("internal error");
+       }
+
+       /* Write what remains: */
+       return commit_packed_refs();
 }
 
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
diff --git a/refs.h b/refs.h
index 246bf6096d222a0d822d621de519ca5c49b0f641..9e5db3ae26ec7898d8ee9083c37562d3af05c0ab 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -77,11 +77,33 @@ extern int for_each_rawref(each_ref_fn, void *);
 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
- * Add a reference to the in-memory packed reference cache.  To actually
- * write the reference to the packed-refs file, call pack_refs().
+ * Lock the packed-refs file for writing.  Flags is passed to
+ * hold_lock_file_for_update().  Return 0 on success.
+ */
+extern int lock_packed_refs(int flags);
+
+/*
+ * Add a reference to the in-memory packed reference cache.  This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()).  To actually write the packed-refs file, call
+ * commit_packed_refs().
  */
 extern void add_packed_ref(const char *refname, const unsigned char *sha1);
 
+/*
+ * Write the current version of the packed refs cache from memory to
+ * disk.  The packed-refs file must already be locked for writing (see
+ * lock_packed_refs()).  Return zero on success.
+ */
+extern int commit_packed_refs(void);
+
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache.  (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+extern void rollback_packed_refs(void);
+
 /*
  * Flags for controlling behaviour of pack_refs()
  * PACK_REFS_PRUNE: Prune loose refs after packing
index 60eda6308197ad3d0b5957b9ae34e2dc15dfe5cc..5b3ce9eed299e312c132b590ea4926cfb019a271 100644 (file)
@@ -7,6 +7,7 @@
 #include "run-command.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "argv-array.h"
 
 static struct remote *remote;
 static const char *url; /* always ends with a trailing slash */
@@ -787,36 +788,35 @@ static int push_dav(int nr_spec, char **specs)
 static int push_git(struct discovery *heads, int nr_spec, char **specs)
 {
        struct rpc_state rpc;
-       const char **argv;
-       int argc = 0, i, err;
+       int i, err;
+       struct argv_array args;
+
+       argv_array_init(&args);
+       argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
+                        NULL);
 
-       argv = xmalloc((10 + nr_spec) * sizeof(char*));
-       argv[argc++] = "send-pack";
-       argv[argc++] = "--stateless-rpc";
-       argv[argc++] = "--helper-status";
        if (options.thin)
-               argv[argc++] = "--thin";
+               argv_array_push(&args, "--thin");
        if (options.dry_run)
-               argv[argc++] = "--dry-run";
+               argv_array_push(&args, "--dry-run");
        if (options.verbosity == 0)
-               argv[argc++] = "--quiet";
+               argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
-               argv[argc++] = "--verbose";
-       argv[argc++] = options.progress ? "--progress" : "--no-progress";
-       argv[argc++] = url;
+               argv_array_push(&args, "--verbose");
+       argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+       argv_array_push(&args, url);
        for (i = 0; i < nr_spec; i++)
-               argv[argc++] = specs[i];
-       argv[argc++] = NULL;
+               argv_array_push(&args, specs[i]);
 
        memset(&rpc, 0, sizeof(rpc));
        rpc.service_name = "git-receive-pack",
-       rpc.argv = argv;
+       rpc.argv = args.argv;
 
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
                write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
-       free(argv);
+       argv_array_clear(&args);
        return err;
 }
 
index e71f66de25b6031bbe1ee94ab3770aacd15b46e0..efcba931eca963bd6a5fd13f01a4859e0ae9e14d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -276,10 +276,9 @@ static void read_remotes_file(struct remote *remote)
 
 static void read_branches_file(struct remote *remote)
 {
-       const char *slash = strchr(remote->name, '/');
        char *frag;
        struct strbuf branch = STRBUF_INIT;
-       int n = slash ? slash - remote->name : 1000;
+       int n = 1000;
        FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
        char *s, *p;
        int len;
@@ -299,21 +298,11 @@ static void read_branches_file(struct remote *remote)
        while (isspace(p[-1]))
                *--p = 0;
        len = p - s;
-       if (slash)
-               len += strlen(slash);
        p = xmalloc(len + 1);
        strcpy(p, s);
-       if (slash)
-               strcat(p, slash);
 
        /*
-        * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
-        * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
-        * the partial URL obtained from the branches file plus
-        * "/netdev-2.6" and does not store it in any tracking ref.
-        * #branch specifier in the file is ignored.
-        *
-        * Otherwise, the branches file would have URL and optionally
+        * The branches file would have URL and optionally
         * #branch specified.  The "master" (or specified) branch is
         * fetched and stored in the local branch of the same name.
         */
@@ -323,12 +312,8 @@ static void read_branches_file(struct remote *remote)
                strbuf_addf(&branch, "refs/heads/%s", frag);
        } else
                strbuf_addstr(&branch, "refs/heads/master");
-       if (!slash) {
-               strbuf_addf(&branch, ":refs/heads/%s", remote->name);
-       } else {
-               strbuf_reset(&branch);
-               strbuf_addstr(&branch, "HEAD:");
-       }
+
+       strbuf_addf(&branch, ":refs/heads/%s", remote->name);
        add_url_alias(remote, p);
        add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
@@ -1317,6 +1302,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        free(sent_tips.tip);
 }
 
+static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
+{
+       for ( ; ref; ref = ref->next)
+               string_list_append_nodup(ref_index, ref->name)->util = ref;
+
+       sort_string_list(ref_index);
+}
+
 /*
  * Given the set of refs the local repository has, the set of refs the
  * remote repository has, and the refspec used for push, determine
@@ -1335,6 +1328,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
        int errs;
        static const char *default_refspec[] = { ":", NULL };
        struct ref *ref, **dst_tail = tail_ref(dst);
+       struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
 
        if (!nr_refspec) {
                nr_refspec = 1;
@@ -1345,6 +1339,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
        /* pick the remainder */
        for (ref = src; ref; ref = ref->next) {
+               struct string_list_item *dst_item;
                struct ref *dst_peer;
                const struct refspec *pat = NULL;
                char *dst_name;
@@ -1353,7 +1348,11 @@ int match_push_refs(struct ref *src, struct ref **dst,
                if (!dst_name)
                        continue;
 
-               dst_peer = find_ref_by_name(*dst, dst_name);
+               if (!dst_ref_index.nr)
+                       prepare_ref_index(&dst_ref_index, *dst);
+
+               dst_item = string_list_lookup(&dst_ref_index, dst_name);
+               dst_peer = dst_item ? dst_item->util : NULL;
                if (dst_peer) {
                        if (dst_peer->peer_ref)
                                /* We're already sending something to this ref. */
@@ -1370,6 +1369,8 @@ int match_push_refs(struct ref *src, struct ref **dst,
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, &dst_tail);
                        hashcpy(dst_peer->new_sha1, ref->new_sha1);
+                       string_list_insert(&dst_ref_index,
+                               dst_peer->name)->util = dst_peer;
                }
                dst_peer->peer_ref = copy_ref(ref);
                dst_peer->force = pat->force;
@@ -1377,10 +1378,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
                free(dst_name);
        }
 
+       string_list_clear(&dst_ref_index, 0);
+
        if (flags & MATCH_REFS_FOLLOW_TAGS)
                add_missing_tags(src, dst, &dst_tail);
 
        if (send_prune) {
+               struct string_list src_ref_index = STRING_LIST_INIT_NODUP;
                /* check for missing refs on the remote */
                for (ref = *dst; ref; ref = ref->next) {
                        char *src_name;
@@ -1391,11 +1395,15 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
                        src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
                        if (src_name) {
-                               if (!find_ref_by_name(src, src_name))
+                               if (!src_ref_index.nr)
+                                       prepare_ref_index(&src_ref_index, src);
+                               if (!string_list_has_string(&src_ref_index,
+                                           src_name))
                                        ref->peer_ref = alloc_delete_ref();
                                free(src_name);
                        }
                }
+               string_list_clear(&src_ref_index, 0);
        }
        if (errs)
                return -1;
index f1bb731fd71eb874344faa5376e7c375ac2fcf7f..2f0142f22e21d7cfb1c74ca7035ee962e16dbd9e 100644 (file)
@@ -1296,7 +1296,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        DIFF_OPT_SET(&revs->pruning, QUICK);
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
-       revs->lifo = 1;
+       revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
        revs->max_age = -1;
@@ -1638,7 +1638,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--merge")) {
                revs->show_merge = 1;
        } else if (!strcmp(arg, "--topo-order")) {
-               revs->lifo = 1;
+               revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--simplify-merges")) {
                revs->simplify_merges = 1;
@@ -1656,7 +1656,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->prune = 1;
                load_ref_decorations(DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
-               revs->lifo = 0;
+               revs->sort_order = REV_SORT_BY_COMMIT_DATE;
+               revs->topo_order = 1;
+       } else if (!strcmp(arg, "--author-date-order")) {
+               revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
        } else if (!prefixcmp(arg, "--early-output")) {
                int count = 100;
@@ -2606,7 +2609,7 @@ int prepare_revision_walk(struct rev_info *revs)
                if (limit_list(revs) < 0)
                        return -1;
        if (revs->topo_order)
-               sort_in_topological_order(&revs->commits, revs->lifo);
+               sort_in_topological_order(&revs->commits, revs->sort_order);
        if (revs->line_level_traverse)
                line_log_filter(revs);
        if (revs->simplify_merges)
@@ -2924,7 +2927,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
         * If revs->topo_order is set, sort the boundary commits
         * in topological order
         */
-       sort_in_topological_order(&revs->commits, revs->lifo);
+       sort_in_topological_order(&revs->commits, revs->sort_order);
 }
 
 static struct commit *get_revision_internal(struct rev_info *revs)
index eeea6fba3c5408d5567eec238d0a331bf64ddc04..95859ba119033ba17346c701493df1474eec5df2 100644 (file)
@@ -4,6 +4,7 @@
 #include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
+#include "commit.h"
 
 #define SEEN           (1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -62,6 +63,10 @@ struct rev_info {
        const char *prefix;
        const char *def;
        struct pathspec prune_data;
+
+       /* topo-sort */
+       enum rev_sort_order sort_order;
+
        unsigned int    early_output:1,
                        ignore_missing:1;
 
@@ -72,7 +77,6 @@ struct rev_info {
                        show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
-                       lifo:1,
                        topo_order:1,
                        simplify_merges:1,
                        simplify_by_decoration:1,
@@ -140,6 +144,7 @@ struct rev_info {
        int             numbered_files;
        int             reroll_count;
        char            *message_id;
+       struct ident_split from_ident;
        struct string_list *ref_message_ids;
        int             add_signoff;
        const char      *extra_headers;
index f7be7d8be66992567fe2140c6aecdd3cc6140d38..425207ad5f1068b17daa50199b6880ef6009db79 100644 (file)
@@ -270,15 +270,20 @@ static int error_dirty_index(struct replay_opts *opts)
 }
 
 static int fast_forward_to(const unsigned char *to, const unsigned char *from,
-                          int unborn)
+                       int unborn, struct replay_opts *opts)
 {
        struct ref_lock *ref_lock;
+       struct strbuf sb = STRBUF_INIT;
+       int ret;
 
        read_cache();
        if (checkout_fast_forward(from, to, 1))
                exit(1); /* the callee should have complained already */
        ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
-       return write_ref_sha1(ref_lock, to, "cherry-pick");
+       strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
+       ret = write_ref_sha1(ref_lock, to, sb.buf);
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int do_recursive_merge(struct commit *base, struct commit *next,
@@ -523,7 +528,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        if (opts->allow_ff &&
            ((parent && !hashcmp(parent->object.sha1, head)) ||
             (!parent && unborn)))
-            return fast_forward_to(commit->object.sha1, head, unborn);
+               return fast_forward_to(commit->object.sha1, head, unborn, opts);
 
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
index 90419efe1052406381af3e35b70dc996fe8533ce..1a75fac05feaefa3cc0c57896aa6ee70813a324a 100644 (file)
@@ -241,7 +241,7 @@ static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(sha1), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
index 86854248983be78b3702f5045a659dfe83bf4446..85415d0057402d40c4c9e98e3c94c41eb8dd940f 100644 (file)
@@ -226,6 +226,7 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
        while ((commit = get_revision(rev))) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = rev->date_mode;
+               ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
                strbuf_addstr(&sb, line_prefix);
                if (commit->object.flags & SYMMETRIC_LEFT) {
index ec5246886132f919ec4b6b05bfedd65eb2011690..2167125008db5e3e1c7d13310c8da3a4243e8718 100644 (file)
--- a/t/README
+++ b/t/README
@@ -76,6 +76,11 @@ appropriately before running "make".
        command being run and their output if any are also
        output.
 
+--verbose-only=<pattern>::
+       Like --verbose, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
+
 --debug::
        This may help the person who is developing a new test.
        It causes the command defined with test_debug to run.
@@ -121,6 +126,11 @@ appropriately before running "make".
        the 't/valgrind/' directory and use the commands under
        't/valgrind/bin/'.
 
+--valgrind-only=<pattern>::
+       Like --valgrind, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
+
 --tee::
        In addition to printing the test output to the terminal,
        write it to files named 't/test-results/$TEST_NAME.out'.
index 8b5a71dc05216dc0acfb7761a4b9ca789c716ce0..45971f43b77142fe1bb90262974c83c786cfe43d 100755 (executable)
@@ -21,6 +21,7 @@ sub err {
        /^\s*declare\s+/ and err 'arrays/declare not portable';
        /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
        /test\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+       /^\s*export\s+[^=]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
        # this resets our $. for each file
        close ARGV if eof;
 }
index 4b74ae460b8943c176b6563d374e1663f89a1d7b..8ff87fb3f9bee1c6955f84b66027037100ebbbd3 100644 (file)
 #       ("squash", "fixup", "edit", or "reword") and the SHA1 taken
 #       from the specified line.
 #
+#   "exec_cmd_with_args" -- add an "exec cmd with args" line.
+#
 #   "#" -- Add a comment line.
 #
 #   ">" -- Add a blank line.
 
 set_fake_editor () {
-       echo "#!$SHELL_PATH" >fake-editor.sh
-       cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
-       test -z "$EXPECT_HEADER_COUNT" ||
-               test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+       write_script fake-editor.sh <<-\EOF
+       case "$1" in
+       */COMMIT_EDITMSG)
+               test -z "$EXPECT_HEADER_COUNT" ||
+                       test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+                       exit
+               test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+               test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
                exit
-       test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
-       test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
-       exit
-       ;;
-esac
-test -z "$EXPECT_COUNT" ||
-       test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
-       exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-echo 'rebase -i script before editing:'
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
-       case $line in
-       squash|fixup|edit|reword)
-               action="$line";;
-       exec*)
-               echo "$line" | sed 's/_/ /g' >> "$1";;
-       "#")
-               echo '# comment' >> "$1";;
-       ">")
-               echo >> "$1";;
-       *)
-               sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
-               action=pick;;
+               ;;
        esac
-done
-echo 'rebase -i script after editing:'
-cat "$1"
-EOF
+       test -z "$EXPECT_COUNT" ||
+               test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+               exit
+       test -z "$FAKE_LINES" && exit
+       grep -v '^#' < "$1" > "$1".tmp
+       rm -f "$1"
+       echo 'rebase -i script before editing:'
+       cat "$1".tmp
+       action=pick
+       for line in $FAKE_LINES; do
+               case $line in
+               squash|fixup|edit|reword)
+                       action="$line";;
+               exec*)
+                       echo "$line" | sed 's/_/ /g' >> "$1";;
+               "#")
+                       echo '# comment' >> "$1";;
+               ">")
+                       echo >> "$1";;
+               *)
+                       sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+                       action=pick;;
+               esac
+       done
+       echo 'rebase -i script after editing:'
+       cat "$1"
+       EOF
+
+       test_set_editor "$(pwd)/fake-editor.sh"
+}
+
+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
+# blank lines and comments) to stdout, and exit failure (so you should run
+# it with test_must_fail).  This can be used to verify the expected user
+# experience, for todo list changes that do not affect the outcome of
+# rebase; or as an extra check in addition to checking the outcome.
 
+set_cat_todo_editor () {
+       write_script fake-editor.sh <<-\EOF
+       grep "^[^#]" "$1"
+       exit 1
+       EOF
        test_set_editor "$(pwd)/fake-editor.sh"
-       chmod a+x fake-editor.sh
 }
 
 # checks that the revisions in "$2" represent a linear range with the
index ea25dd89e505525507c16c62d91fb07c092b064a..4ffd90127edb360142e492f6562c3c15c002d286 100644 (file)
@@ -1,55 +1,50 @@
 : included from 6002 and others
 
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+mkdir -p .git/refs/tags
 
-:> sed.script
+>sed.script
 
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
+# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags
+tag () {
        _tag=$1
-       [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
-       cat .git/refs/tags/$_tag
+       test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist"
+       cat ".git/refs/tags/$_tag"
 }
 
 # Generate a commit using the text specified to make it unique and the tree
 # named by the tag specified.
-unique_commit()
-{
+unique_commit () {
        _text=$1
-        _tree=$2
+       _tree=$2
        shift 2
-       echo $_text | git commit-tree $(tag $_tree) "$@"
+       echo "$_text" | git commit-tree $(tag "$_tree") "$@"
 }
 
 # Save the output of a command into the tag specified. Prepend
 # a substitution script for the tag onto the front of sed.script
-save_tag()
-{
+save_tag () {
        _tag=$1
-       [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+       test -n "$_tag" || error "usage: save_tag tag commit-args ..."
        shift 1
-       "$@" >.git/refs/tags/$_tag
+       "$@" >".git/refs/tags/$_tag"
 
-        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
-       cat sed.script >> sed.script.tmp
+       echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
+       cat sed.script >>sed.script.tmp
        rm sed.script
        mv sed.script.tmp sed.script
 }
 
 # Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
+entag () {
        sed -f sed.script
 }
 
 # Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
 # tag to a specified value. Restore the original value on return.
-as_author()
-{
+as_author () {
        _author=$1
        shift 1
-        _save=$GIT_AUTHOR_EMAIL
+       _save=$GIT_AUTHOR_EMAIL
 
        GIT_AUTHOR_EMAIL="$_author"
        export GIT_AUTHOR_EMAIL
@@ -63,45 +58,58 @@ as_author()
        fi
 }
 
-commit_date()
-{
-        _commit=$1
-       git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+commit_date () {
+       _commit=$1
+       git cat-file commit $_commit |
+       sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
 }
 
-on_committer_date()
-{
-    _date=$1
-    shift 1
-    GIT_COMMITTER_DATE="$_date"
-    export GIT_COMMITTER_DATE
-    "$@"
-    unset GIT_COMMITTER_DATE
+# Assign the value of fake date to a variable, but
+# allow fairly common "1971-08-16 00:00" to be omittd
+assign_fake_date () {
+       case "$2" in
+       ??:??:??)       eval "$1='1971-08-16 $2'" ;;
+       ??:??)          eval "$1='1971-08-16 00:$2'" ;;
+       ??)             eval "$1='1971-08-16 00:00:$2'" ;;
+       *)              eval "$1='$2'" ;;
+       esac
+}
+
+on_committer_date () {
+       assign_fake_date GIT_COMMITTER_DATE "$1"
+       export GIT_COMMITTER_DATE
+       shift 1
+       "$@"
+}
+
+on_dates () {
+       assign_fake_date GIT_COMMITTER_DATE "$1"
+       assign_fake_date GIT_AUTHOR_DATE "$2"
+       export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+       shift 2
+       "$@"
 }
 
 # Execute a command and suppress any error output.
-hide_error()
-{
+hide_error () {
        "$@" 2>/dev/null
 }
 
-check_output()
-{
+check_output () {
        _name=$1
        shift 1
-       if eval "$*" | entag > $_name.actual
+       if eval "$*" | entag >"$_name.actual"
        then
-               test_cmp $_name.expected $_name.actual
+               test_cmp "$_name.expected" "$_name.actual"
        else
-               return 1;
+               return 1
        fi
 }
 
 # Turn a reasonable test description into a reasonable test name.
 # All alphanums translated into -'s which are then compressed and stripped
 # from front and back.
-name_from_description()
-{
+name_from_description () {
        perl -pe '
                s/[^A-Za-z0-9.]/-/g;
                s/-+/-/g;
@@ -119,9 +127,11 @@ name_from_description()
 test_output_expect_success()
 {
        _description=$1
-        _test=$2
-        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
-        _name=$(echo $_description | name_from_description)
-       cat > $_name.expected
+       _test=$2
+       test $# -eq 2 ||
+       error "usage: test_output_expect_success description test <<EOF ... EOF"
+
+       _name=$(echo $_description | name_from_description)
+       cat >"$_name.expected"
        test_expect_success "$_description" "check_output $_name \"$_test\""
 }
index c552f561bffd9800d53064772ea640677b20a3ae..8848c1461909199a5c0bd587df8d9ef652c650ac 100644 (file)
@@ -66,7 +66,7 @@ You can set the following variables (also in your config.mak):
     GIT_PERF_LARGE_REPO
        Repositories to copy for the performance tests.  The normal
        repo should be at least git.git size.  The large repo should
-       probably be about linux-2.6.git size for optimal results.
+       probably be about linux.git size for optimal results.
        Both default to the git.git you are running from.
 
 You can also pass the options taken by ordinary git tests; the most
index a816fbcb7618bb8cdbf1f74a4fd2295923c02ea3..c61d5351e1ff0b1b4431eb28553d0d882ebe7451 100644 (file)
@@ -150,6 +150,7 @@ exit $ret' >&3 2>&4
 
 
 test_perf () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -187,7 +188,7 @@ test_perf () {
                base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
                "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 # We extend test_done to print timings at the end (./run disables this
index 0f1318056cd06d8bcae615be5e573149ac5971d1..10be52beedb4058318555e096d51517f1c30ad9f 100755 (executable)
@@ -47,8 +47,14 @@ test_expect_failure 'pretend we have a known breakage' '
 
 run_sub_test_lib_test () {
        name="$1" descr="$2" # stdin is the body of the test code
+       shift 2
        mkdir "$name" &&
        (
+               # Pretend we're a test harness.  This prevents
+               # test-lib from writing the counts to a file that will
+               # later be summarized, showing spurious "failed" tests
+               HARNESS_ACTIVE=t &&
+               export HARNESS_ACTIVE &&
                cd "$name" &&
                cat >"$name.sh" <<-EOF &&
                #!$SHELL_PATH
@@ -65,7 +71,7 @@ run_sub_test_lib_test () {
                cat >>"$name.sh" &&
                chmod +x "$name.sh" &&
                export TEST_DIRECTORY &&
-               ./"$name.sh" >out 2>err
+               ./"$name.sh" "$@" >out 2>err
        )
 }
 
@@ -215,6 +221,60 @@ test_expect_success 'pretend we have a mix of all possible results' "
        EOF
 "
 
+test_expect_success 'test --verbose' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose "test verbose" --verbose <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       mv test-verbose/out test-verbose/out+
+       grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+       check_sub_test_lib_test test-verbose <<-\EOF
+       > expecting success: true
+       > Z
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > expecting success: false
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > Z
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+'
+
+test_expect_success 'test --verbose-only' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose-only-2 "test verbose-only=2" \
+               --verbose-only=2 <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+'
+
 test_set_prereq HAVEIT
 haveit=no
 test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
new file mode 100755 (executable)
index 0000000..94045c3
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+1
+2
+3
+4
+5
+5
+6
+7
+8
+9
+10
+EOF
+test_expect_success 'basic ordering' '
+       test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+3
+4
+1
+5
+6
+EOF
+test_expect_success 'mixed put and get' '
+       test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+1
+2
+NULL
+1
+2
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+       test-prio-queue 1 2 get get get 1 2 get get get >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 1a8f44c44ca3cce70990ec56fec61894be9b3086..e526184a0bfa3c8ad2171d0c41a7bee22cf9a04e 100755 (executable)
@@ -81,6 +81,14 @@ test_expect_success 'safecrlf: print warning only once' '
        test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
 '
 
+
+test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
+       git config core.autocrlf input &&
+       git config core.safecrlf true &&
+       git diff HEAD
+'
+
+
 test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
        git config core.autocrlf false &&
        git config core.safecrlf false &&
index db228086d3977d304a3debd7c3ae6dc54c40ad17..4a155c8d095fb8ca2c2f88802d3501e01d07ce28 100755 (executable)
@@ -77,6 +77,7 @@ test_expect_success 'disambiguate blob' '
 
 test_expect_success 'disambiguate tree' '
        commit=$(echo "d7xm" | git commit-tree 000000000) &&
+       # this commit is fffff2e and not ambiguous with the 00000* objects
        test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
 '
 
@@ -99,10 +100,14 @@ test_expect_success 'disambiguate commit-ish' '
 
 test_expect_success 'disambiguate commit' '
        commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+       # this commit is ffffffd8 and not ambiguous with the 00000* objects
        test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
 '
 
 test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+       # These are underspecified from the prefix-length point of view
+       # to disambiguate the commit with other objects, but there is only
+       # one commit that has 00000* prefix at this point.
        git log 000000000..000000000 &&
        git log ..000000000 &&
        git log 000000000.. &&
@@ -112,16 +117,19 @@ test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
 '
 
 test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+       # Likewise.
        git rev-parse 000000000..000000000 &&
        git rev-parse ..000000000 &&
        git rev-parse 000000000..
 '
 
 test_expect_success 'git log takes only commit-ish' '
+       # Likewise.
        git log 000000000
 '
 
 test_expect_success 'git reset takes only commit-ish' '
+       # Likewise.
        git reset 000000000
 '
 
@@ -131,26 +139,30 @@ test_expect_success 'first tag' '
 '
 
 test_expect_failure 'two semi-ambiguous commit-ish' '
+       # At this point, we have a tag 0000000000f8f that points
+       # at a commit 0000000000e4f, and a tree and a blob that
+       # share 0000000000 prefix with these tag and commit.
+       #
        # Once the parser becomes ultra-smart, it could notice that
-       # 110282 before ^{commit} name many different objects, but
+       # 0000000000 before ^{commit} name many different objects, but
        # that only two (HEAD and v1.0.0 tag) can be peeled to commit,
        # and that peeling them down to commit yield the same commit
        # without ambiguity.
-       git rev-parse --verify 110282^{commit} &&
+       git rev-parse --verify 0000000000^{commit} &&
 
        # likewise
-       git log 000000000..000000000 &&
-       git log ..000000000 &&
-       git log 000000000.. &&
-       git log 000000000...000000000 &&
-       git log ...000000000 &&
-       git log 000000000...
+       git log 0000000000..0000000000 &&
+       git log ..0000000000 &&
+       git log 0000000000.. &&
+       git log 0000000000...0000000000 &&
+       git log ...0000000000 &&
+       git log 0000000000...
 '
 
 test_expect_failure 'three semi-ambiguous tree-ish' '
        # Likewise for tree-ish.  HEAD, v1.0.0 and HEAD^{tree} share
        # the prefix but peeling them to tree yields the same thing
-       git rev-parse --verify 000000000^{tree}
+       git rev-parse --verify 0000000000^{tree}
 '
 
 test_expect_success 'parse describe name' '
@@ -241,7 +253,7 @@ test_expect_success 'ambiguous commit-ish' '
        # Now there are many commits that begin with the
        # common prefix, none of these should pick one at
        # random.  They all should result in ambiguity errors.
-       test_must_fail git rev-parse --verify 110282^{commit} &&
+       test_must_fail git rev-parse --verify 00000000^{commit} &&
 
        # likewise
        test_must_fail git log 000000000..000000000 &&
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
new file mode 100755 (executable)
index 0000000..87ec3ae
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='Tests for rev-parse --prefix'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir -p sub1/sub2 &&
+       echo top >top &&
+       echo file1 >sub1/file1 &&
+       echo file2 >sub1/sub2/file2 &&
+       git add top sub1/file1 sub1/sub2/file2 &&
+       git commit -m commit
+'
+
+test_expect_success 'empty prefix -- file' '
+       git rev-parse --prefix "" -- top sub1/file1 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       top
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- file' '
+       git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/file1
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- ../file' '
+       git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/../top
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'empty prefix HEAD:./path' '
+       git rev-parse --prefix "" HEAD:./top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:./path' '
+       git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
+       git rev-parse HEAD:sub1/file1 >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:../path' '
+       git rev-parse --prefix sub1/ HEAD:../top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'prefix ignored with HEAD:top' '
+       git rev-parse --prefix sub1/ HEAD:top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'disambiguate path with valid prefix' '
+       git rev-parse --prefix sub1/ file1 >actual &&
+       cat <<-\EOF >expected &&
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'file and refs with prefix' '
+       git rev-parse --prefix sub1/ master file1 >actual &&
+       cat <<-EOF >expected &&
+       $(git rev-parse master)
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'two-levels deep' '
+       git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_done
index b44de9dc623fc923941b61719f445170602e21be..e7ba8c505f579ab5cd53ee713eaf6ca894e471a1 100755 (executable)
@@ -116,4 +116,38 @@ test_expect_success 'master...' '
        test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
 '
 
+test_expect_success '"checkout -" works after a rebase A' '
+       git checkout master &&
+       git checkout other &&
+       git rebase master &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase A B' '
+       git branch moodle master~1 &&
+       git checkout master &&
+       git checkout other &&
+       git rebase master moodle &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A' '
+       git checkout master &&
+       git checkout other &&
+       git rebase -i master &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A B' '
+       git branch foodle master~1 &&
+       git checkout master &&
+       git checkout other &&
+       git rebase master foodle &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
 test_done
index 262e61744563b2ce4eb14df0e54f71d56c157007..f611d799b690457e306571adf16813b238a27dc9 100755 (executable)
@@ -11,6 +11,8 @@ This test prepares the following in the cache:
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    submod1/   - a submodule
+    submod2/   - another submodule
 
 and the following on the filesystem:
 
@@ -21,9 +23,11 @@ and the following on the filesystem:
     path4      - a file
     path5      - a symlink
     path6/file6 - a file in a directory
+    submod1/   - a submodule (modified from the cache)
+    submod2/   - a submodule (matches the cache)
 
-git ls-files -k should report that existing filesystem
-objects except path4, path5 and path6/file6 to be killed.
+git ls-files -k should report that existing filesystem objects
+path0/*, path1/*, path2 and path3 to be killed.
 
 Also for modification test, the cache and working tree have:
 
@@ -33,7 +37,7 @@ Also for modification test, the cache and working tree have:
     path10     - a non-empty file, cache dirtied.
 
 We should report path0, path1, path2/file2, path3/file3, path7 and path8
-modified without reporting path9 and path10.
+modified without reporting path9 and path10.  submod1 is also modified.
 '
 . ./test-lib.sh
 
@@ -48,6 +52,18 @@ test_expect_success 'git update-index --add to add various paths.' '
        : >path9 &&
        date >path10 &&
        git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+       for i in 1 2
+       do
+               git init submod$i &&
+               (
+                       cd submod$i && git commit --allow-empty -m "empty $i"
+               ) || break
+       done &&
+       git update-index --add submod[12]
+       (
+               cd submod1 &&
+               git commit --allow-empty -m "empty 1 (updated)"
+       ) &&
        rm -fr path?    # leave path10 alone
 '
 
@@ -94,6 +110,7 @@ test_expect_success 'validate git ls-files -m output.' '
        path3/file3
        path7
        path8
+       submod1
        EOF
        test_cmp .expected .output
 '
index d6b414377306de88e8e294dd5eaadfcb3a234ec2..49ccb38f8856e6e5fa00961c6e9117f94c6ae233 100755 (executable)
@@ -926,6 +926,21 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
        test L = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i produces readable reflog' '
+       git reset --hard &&
+       git branch -f branch-reflog-test H &&
+       git rebase -i --onto I F branch-reflog-test &&
+       cat >expect <<-\EOF &&
+       rebase -i (start): checkout I
+       rebase -i (pick): G
+       rebase -i (pick): H
+       rebase -i (finish): returning to refs/heads/branch-reflog-test
+       EOF
+       tail -n 4 .git/logs/HEAD |
+       sed -e "s/.*    //" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'rebase -i respects core.commentchar' '
        git reset --hard &&
        git checkout E^0 &&
@@ -950,4 +965,15 @@ test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxer
        git checkout branch1
 '
 
+test_expect_success 'rebase -i with --strategy and -X' '
+       git checkout -b conflict-merge-use-theirs conflict-branch &&
+       git reset --hard HEAD^ &&
+       echo five >conflict &&
+       echo Z >file1 &&
+       git commit -a -m "one file conflict" &&
+       EDITOR=true git rebase -i --strategy=recursive -Xours conflict-branch &&
+       test $(git show conflict-branch:conflict) = $(cat conflict) &&
+       test $(cat file1) = Z
+'
+
 test_done
index a1e86c4097ba096245b11a991c39e79886ca2b91..41370ab998c72e14f269a257304b3e0cc4aebc8c 100755 (executable)
@@ -4,6 +4,8 @@ test_description='auto squash'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success setup '
        echo 0 >file0 &&
        git add . &&
@@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '
        test_auto_commit_flags squash 2
 '
 
+test_auto_fixup_fixup () {
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "$1! first" &&
+       echo 2 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "$1! $2! first" &&
+       git tag "final-$1-$2" &&
+       test_tick &&
+       (
+               set_cat_todo_editor &&
+               test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
+               cat >expected <<-EOF &&
+               pick $(git rev-parse --short HEAD^^^) first commit
+               $1 $(git rev-parse --short HEAD^) $1! first
+               $1 $(git rev-parse --short HEAD) $1! $2! first
+               pick $(git rev-parse --short HEAD^^) second commit
+               EOF
+               test_cmp expected actual
+       ) &&
+       git rebase --autosquash -i HEAD^^^^ &&
+       git log --oneline >actual &&
+       test_line_count = 3 actual
+       git diff --exit-code "final-$1-$2" &&
+       test 2 = "$(git cat-file blob HEAD^:file1)" &&
+       if test "$1" = "fixup"
+       then
+               test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+       elif test "$1" = "squash"
+       then
+               test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
+       else
+               false
+       fi
+}
+
+test_expect_success 'fixup! fixup!' '
+       test_auto_fixup_fixup fixup fixup
+'
+
+test_expect_success 'fixup! squash!' '
+       test_auto_fixup_fixup fixup squash
+'
+
+test_expect_success 'squash! squash!' '
+       test_auto_fixup_fixup squash squash
+'
+
+test_expect_success 'squash! fixup!' '
+       test_auto_fixup_fixup squash fixup
+'
+
 test_done
index 37ddabba2d60956a4d8da63585bec5a622e12a5d..38b00c37b0606a5442f2dce02647a019f0c80c9e 100755 (executable)
@@ -39,6 +39,42 @@ test_expect_failure 'UTF-16 refused because of NULs' '
        git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
 '
 
+test_expect_success 'UTF-8 invalid characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 characters" >F &&
+       printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 overlong sequences rejected' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       rm -f "$HOME/stderr" "$HOME/invalid" &&
+       echo "UTF-8 overlong" >F &&
+       printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 non-character 1" >F &&
+       printf "Commit message\n\nNon-character:\364\217\277\276\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 non-character 2." >F &&
+       printf "Commit message\n\nNon-character:\357\267\220\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
 
 for H in ISO8859-1 eucJP ISO-2022-JP
 do
index 634b2b74f49558781f612e3044d99cbc6fa3e657..5d22f17ca2d3469c7542805e365246a70a5fba64 100755 (executable)
@@ -654,4 +654,41 @@ test_expect_success 'stash where working directory contains "HEAD" file' '
        test_cmp output expect
 '
 
+test_expect_success 'store called with invalid commit' '
+       test_must_fail git stash store foo
+'
+
+test_expect_success 'store updates stash ref and reflog' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       ! grep quux bazzy &&
+       git stash store -m quuxery $STASH_ID &&
+       test $(cat .git/refs/stash) = $STASH_ID &&
+       grep $STASH_ID .git/logs/refs/stash &&
+       git stash pop &&
+       grep quux bazzy
+'
+
+test_expect_success 'stash a change to turn a non-directory to a directory' '
+       git reset --hard &&
+       >testfile &&
+       git add testfile &&
+       git commit -m "add testfile as a regular file" &&
+       rm testfile &&
+       mkdir testfile &&
+       >testfile/file &&
+       test_must_fail git stash save "recover regular file" &&
+       test -f testfile/file
+'
+
+test_expect_success 'stash a change to turn a non-directory to a directory (forced)' '
+       git stash save --force "recover regular file (forced)" &&
+       ! test -f testfile/file &&
+       test -f testfile
+'
+
 test_done
index 58d418098d793b284bb4a7222cf9709c45b26c90..668933bfb2c906ce9f8c74ab498f1986b56aced0 100755 (executable)
@@ -972,6 +972,49 @@ test_expect_success 'empty subject prefix does not have extra space' '
        test_cmp expect actual
 '
 
+test_expect_success '--from=ident notices bogus ident' '
+       test_must_fail git format-patch -1 --stdout --from=foo >patch
+'
+
+test_expect_success '--from=ident replaces author' '
+       git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
+       cat >expect <<-\EOF &&
+       From: Me <me@example.com>
+
+       From: A U Thor <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
+test_expect_success '--from uses committer ident' '
+       git format-patch -1 --stdout --from >patch &&
+       cat >expect <<-\EOF &&
+       From: C O Mitter <committer@example.com>
+
+       From: A U Thor <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
+test_expect_success 'in-body headers trigger content encoding' '
+       GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git format-patch -1 --stdout --from >patch &&
+       cat >expect <<-\EOF &&
+       From: C O Mitter <committer@example.com>
+       Content-Type: text/plain; charset=UTF-8
+
+       From: Ã©xötìc <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
 append_signoff()
 {
        C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
index cc3db1304ef202a376e84360986568bb7408c701..3fb4b976a277e69cdbf73172caf335748be609a3 100755 (executable)
@@ -142,6 +142,351 @@ EOF
 git diff --ignore-space-at-eol > out
 test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
 
+test_expect_success 'ignore-blank-lines: only new lines' '
+       test_seq 5 >x &&
+       git update-index x &&
+       test_seq 5 | sed "/3/i \\
+" >x &&
+       git diff --ignore-blank-lines >out &&
+       >expect &&
+       test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+       test_seq 5 >x &&
+       git update-index x &&
+       test_seq 5 | sed "/3/i \ " >x &&
+       git diff -w --ignore-blank-lines >out &&
+       >expect &&
+       test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: after change' '
+       cat <<-\EOF >x &&
+       1
+       2
+
+       3
+       4
+       5
+
+       6
+       7
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+
+       1
+       2
+       3
+       4
+       5
+       6
+
+       7
+       EOF
+       git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,6 +1,7 @@
+       +change
+       +
+        1
+        2
+       -
+        3
+        4
+        5
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: before change' '
+       cat <<-\EOF >x &&
+       1
+       2
+
+       3
+       4
+       5
+       6
+       7
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+
+       1
+       2
+       3
+       4
+       5
+
+       6
+       7
+       change
+       EOF
+       git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -4,5 +4,7 @@
+        3
+        4
+        5
+       +
+        6
+        7
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes' '
+       cat <<-\EOF >x &&
+       1
+       2
+       3
+       4
+       5
+
+
+       6
+       7
+       8
+       9
+       10
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+       6
+       7
+       8
+
+       9
+       10
+       change
+       EOF
+       git diff --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,5 +1,7 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       @@ -8,5 +8,7 @@
+        6
+        7
+        8
+       +
+        9
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
+       test_seq 10 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+
+       6
+       7
+       8
+       9
+
+       10
+       change
+       EOF
+       git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,10 +1,15 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       +
+        6
+        7
+        8
+        9
+       +
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: scattered spaces' '
+       test_seq 10 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+       3
+
+       4
+
+       5
+
+       6
+
+
+       7
+
+       8
+       9
+       10
+       change
+       EOF
+       git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,3 +1,4 @@
+       +change
+        1
+        2
+        3
+       @@ -8,3 +15,4 @@
+        8
+        9
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: spaces coalesce' '
+       test_seq 6 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+       3
+
+       4
+
+       5
+
+       6
+       change
+       EOF
+       git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,6 +1,11 @@
+       +change
+        1
+        2
+        3
+       +
+        4
+       +
+        5
+       +
+        6
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
+       test_seq 16 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+       change
+       6
+       7
+       8
+
+       9
+       10
+       11
+       change
+       12
+       13
+       14
+
+       15
+       16
+       change
+       EOF
+       git diff --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,8 +1,11 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       +change
+        6
+        7
+        8
+       @@ -9,8 +13,11 @@
+        9
+        10
+        11
+       +change
+        12
+        13
+        14
+       +
+        15
+        16
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
 test_expect_success 'check mixed spaces and tabs in indent' '
 
        # This is indented with SP HT SP.
index 32d4a604250d55e9221a416638c8c8c4fdd6ff87..1751c8330709c56a4b7019513e30434741847b95 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Support for verbose submodule differences in git diff
@@ -10,6 +11,9 @@ This test tries to verify the sanity of the --submodule option of git diff.
 
 . ./test-lib.sh
 
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
+# used in sample commit log messages in add_file() function below.
+added=$(printf "hinzugef\303\274gt")
 add_file () {
        (
                cd "$1" &&
@@ -19,7 +23,8 @@ add_file () {
                        echo "$name" >"$name" &&
                        git add "$name" &&
                        test_tick &&
-                       git commit -m "Add $name" || exit
+                       msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t iso8859-1) &&
+                       git -c 'i18n.commitEncoding=iso8859-1' commit -m "$msg_added_iso88591"
                done >/dev/null &&
                git rev-parse --short --verify HEAD
        )
@@ -93,7 +98,7 @@ test_expect_success 'modified submodule(forward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -102,7 +107,7 @@ test_expect_success 'modified submodule(forward)' '
        git diff --submodule=log >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -111,7 +116,7 @@ test_expect_success 'modified submodule(forward) --submodule' '
        git diff --submodule >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -142,8 +147,8 @@ test_expect_success 'modified submodule(backward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head2..$head3 (rewind):
-         < Add foo3
-         < Add foo2
+         < Add foo3 ($added foo3)
+         < Add foo2 ($added foo2)
        EOF
        test_cmp expected actual
 '
@@ -153,10 +158,10 @@ test_expect_success 'modified submodule(backward and forward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head2...$head4:
-         > Add foo5
-         > Add foo4
-         < Add foo3
-         < Add foo2
+         > Add foo5 ($added foo5)
+         > Add foo4 ($added foo4)
+         < Add foo3 ($added foo3)
+         < Add foo2 ($added foo2)
        EOF
        test_cmp expected actual
 '
index 7c398432bad761c1b38c6d15548bed0c389221fd..1618a6dbc7c718677cf669b5f6fc96f2682992fb 100755 (executable)
@@ -86,6 +86,20 @@ test_expect_success 'apply --index from subdir of toplevel' '
        test_cmp expected sub/dir/file
 '
 
+test_expect_success 'apply half-broken patch from subdir of toplevel' '
+       (
+               cd sub/dir &&
+               test_must_fail git apply <<-EOF
+               --- sub/dir/file
+               +++ sub/dir/file
+               @@ -1,0 +1,0 @@
+               --- file_in_root
+               +++ file_in_root
+               @@ -1,0 +1,0 @@
+               EOF
+       )
+'
+
 test_expect_success 'apply from .git dir' '
        cp postimage expected &&
        cp preimage .git/file &&
index 26fbfde4a36689a51c181c13f9d5680fd2780219..fb000411395d50639334c3c0395b2b975aa4bc12 100755 (executable)
@@ -1,20 +1,38 @@
 #!/bin/sh
 #
 # Copyright (c) 2010, Will Palmer
+# Copyright (c) 2011, Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Test pretty formats'
 . ./test-lib.sh
 
+sample_utf8_part=$(printf "f\303\244ng")
+
+commit_msg () {
+       # String "initial. initial" partly in German
+       # (translated with Google Translate),
+       # encoded in UTF-8, used as a commit log message below.
+       msg="initial. an${sample_utf8_part}lich\n"
+       if test -n "$1"
+       then
+               printf "$msg" | iconv -f utf-8 -t "$1"
+       else
+               printf "$msg"
+       fi
+}
+
 test_expect_success 'set up basic repos' '
        >foo &&
        >bar &&
        git add foo &&
        test_tick &&
-       git commit -m initial &&
+       git config i18n.commitEncoding iso8859-1 &&
+       git commit -m "$(commit_msg iso8859-1)" &&
        git add bar &&
        test_tick &&
-       git commit -m "add bar"
+       git commit -m "add bar" &&
+       git config --unset i18n.commitEncoding
 '
 
 test_expect_success 'alias builtin format' '
@@ -38,6 +56,20 @@ test_expect_success 'alias user-defined format' '
        test_cmp expected actual
 '
 
+test_expect_success 'alias user-defined tformat with %s (iso8859-1 encoding)' '
+       git config i18n.logOutputEncoding iso8859-1 &&
+       git log --oneline >expected-s &&
+       git log --pretty="tformat:%h %s" >actual-s &&
+       git config --unset i18n.logOutputEncoding &&
+       test_cmp expected-s actual-s
+'
+
+test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
+       git log --oneline >expected-s &&
+       git log --pretty="tformat:%h %s" >actual-s &&
+       test_cmp expected-s actual-s
+'
+
 test_expect_success 'alias user-defined tformat' '
        git log --pretty="tformat:%h" >expected &&
        git config pretty.test-alias "tformat:%h" &&
@@ -72,13 +104,13 @@ test_expect_success 'alias loop' '
 '
 
 test_expect_success 'NUL separation' '
-       printf "add bar\0initial" >expected &&
+       printf "add bar\0$(commit_msg)" >expected &&
        git log -z --pretty="format:%s" >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'NUL termination' '
-       printf "add bar\0initial\0" >expected &&
+       printf "add bar\0$(commit_msg)\0" >expected &&
        git log -z --pretty="tformat:%s" >actual &&
        test_cmp expected actual
 '
@@ -86,7 +118,7 @@ test_expect_success 'NUL termination' '
 test_expect_success 'NUL separation with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected &&
+       printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
        git log -z --stat --pretty="format:%s" >actual &&
        test_i18ncmp expected actual
 '
@@ -94,25 +126,29 @@ test_expect_success 'NUL separation with --stat' '
 test_expect_failure 'NUL termination with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected &&
+       printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
        git log -z --stat --pretty="tformat:%s" >actual &&
        test_i18ncmp expected actual
 '
 
 test_expect_success 'setup more commits' '
        test_commit "message one" one one message-one &&
-       test_commit "message two" two two message-two
+       test_commit "message two" two two message-two &&
+       head1=$(git rev-parse --verify --short HEAD~0) &&
+       head2=$(git rev-parse --verify --short HEAD~1) &&
+       head3=$(git rev-parse --verify --short HEAD~2) &&
+       head4=$(git rev-parse --verify --short HEAD~3)
 '
 
 test_expect_success 'left alignment formatting' '
        git log --pretty="format:%<(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 message two                            Z
 message one                            Z
 add bar                                Z
-initial                                Z
+$(commit_msg)                    Z
 EOF
        test_cmp expected actual
 '
@@ -121,11 +157,11 @@ test_expect_success 'left alignment formatting at the nth column' '
        git log --pretty="format:%h %<|(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
-fa33ab1 message two                    Z
-7cd6c63 message one                    Z
-1711bf9 add bar                        Z
-af20c06 initial                        Z
+       qz_to_tab_space <<EOF >expected &&
+$head1 message two                    Z
+$head2 message one                    Z
+$head3 add bar                        Z
+$head4 $(commit_msg)            Z
 EOF
        test_cmp expected actual
 '
@@ -134,11 +170,11 @@ test_expect_success 'left alignment formatting with no padding' '
        git log --pretty="format:%<(1)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       cat <<\EOF >expected &&
+       cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
        test_cmp expected actual
 '
@@ -147,11 +183,11 @@ test_expect_success 'left alignment formatting with trunc' '
        git log --pretty="format:%<(10,trunc)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 message ..
 message ..
 add bar  Z
-initial  Z
+initial...
 EOF
        test_cmp expected actual
 '
@@ -160,11 +196,11 @@ test_expect_success 'left alignment formatting with ltrunc' '
        git log --pretty="format:%<(10,ltrunc)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 ..sage two
 ..sage one
 add bar  Z
-initial  Z
+..${sample_utf8_part}lich
 EOF
        test_cmp expected actual
 '
@@ -173,11 +209,11 @@ test_expect_success 'left alignment formatting with mtrunc' '
        git log --pretty="format:%<(10,mtrunc)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 mess.. two
 mess.. one
 add bar  Z
-initial  Z
+init..lich
 EOF
        test_cmp expected actual
 '
@@ -186,11 +222,11 @@ test_expect_success 'right alignment formatting' '
        git log --pretty="format:%>(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 Z                            message two
 Z                            message one
 Z                                add bar
-Z                                initial
+Z                    $(commit_msg)
 EOF
        test_cmp expected actual
 '
@@ -199,11 +235,11 @@ test_expect_success 'right alignment formatting at the nth column' '
        git log --pretty="format:%h %>|(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
-fa33ab1                      message two
-7cd6c63                      message one
-1711bf9                          add bar
-af20c06                          initial
+       qz_to_tab_space <<EOF >expected &&
+$head1                      message two
+$head2                      message one
+$head3                          add bar
+$head4              $(commit_msg)
 EOF
        test_cmp expected actual
 '
@@ -212,11 +248,11 @@ test_expect_success 'right alignment formatting with no padding' '
        git log --pretty="format:%>(1)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       cat <<\EOF >expected &&
+       cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
        test_cmp expected actual
 '
@@ -225,11 +261,11 @@ test_expect_success 'center alignment formatting' '
        git log --pretty="format:%><(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
+       qz_to_tab_space <<EOF >expected &&
 Z             message two              Z
 Z             message one              Z
 Z               add bar                Z
-Z               initial                Z
+Z         $(commit_msg)          Z
 EOF
        test_cmp expected actual
 '
@@ -238,11 +274,11 @@ test_expect_success 'center alignment formatting at the nth column' '
        git log --pretty="format:%h %><|(40)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       qz_to_tab_space <<\EOF >expected &&
-fa33ab1           message two          Z
-7cd6c63           message one          Z
-1711bf9             add bar            Z
-af20c06             initial            Z
+       qz_to_tab_space <<EOF >expected &&
+$head1           message two          Z
+$head2           message one          Z
+$head3             add bar            Z
+$head4       $(commit_msg)      Z
 EOF
        test_cmp expected actual
 '
@@ -251,11 +287,11 @@ test_expect_success 'center alignment formatting with no padding' '
        git log --pretty="format:%><(1)%s" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       cat <<\EOF >expected &&
+       cat <<EOF >expected &&
 message two
 message one
 add bar
-initial
+$(commit_msg)
 EOF
        test_cmp expected actual
 '
@@ -265,11 +301,11 @@ test_expect_success 'left/right alignment formatting with stealing' '
        git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
        # complete the incomplete line at the end
        echo >>actual &&
-       cat <<\EOF >expected &&
+       cat <<EOF >expected &&
 short long  long long
 message ..   A U Thor
 add bar      A U Thor
-initial      A U Thor
+initial...   A U Thor
 EOF
        test_cmp expected actual
 '
index a1f5bc49c879e427f132f86c98134d0564005709..d930b6eec4c46930c0190912f33be5b32eb6ff36 100644 (file)
@@ -1,3 +1,100 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+ }
+
 commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
 Author: Thomas Rast <trast@student.ethz.ch>
 Date:   Thu Feb 28 10:45:16 2013 +0100
@@ -7,7 +104,7 @@ Date:   Thu Feb 28 10:45:16 2013 +0100
 diff --git a/a.c b/a.c
 --- a/a.c
 +++ b/a.c
-@@ -3,9 +3,9 @@
+@@ -3,19 +3,19 @@
 -int f(int x)
 +long f(long x)
  {
@@ -18,6 +115,17 @@ diff --git a/a.c b/a.c
        }
        return s;
  }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
 
 commit f04fb20f2c77850996cba739709acc6faecc58f7
 Author: Thomas Rast <trast@student.ethz.ch>
@@ -28,7 +136,7 @@ Date:   Thu Feb 28 10:44:55 2013 +0100
 diff --git a/a.c b/a.c
 --- a/a.c
 +++ b/a.c
-@@ -3,8 +3,9 @@
+@@ -3,18 +3,19 @@
  int f(int x)
  {
        int s = 0;
@@ -38,6 +146,16 @@ diff --git a/a.c b/a.c
        }
 +      return s;
  }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+       printf("%d\n", f(15));
+       return 0;
+ }
 
 commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
 Author: Thomas Rast <trast@student.ethz.ch>
@@ -48,7 +166,7 @@ Date:   Thu Feb 28 10:44:48 2013 +0100
 diff --git a/a.c b/a.c
 --- /dev/null
 +++ b/a.c
-@@ -0,0 +3,8 @@
+@@ -0,0 +3,18 @@
 +int f(int x)
 +{
 +      int s = 0;
@@ -57,3 +175,13 @@ diff --git a/a.c b/a.c
 +              s++;
 +      }
 +}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++      printf("%d\n", f(15));
++      return 0;
++}
index dd10ff053ce28ff2ef092e7b0740c809ba64f5b9..8f6e3922dce2b676221610d83a87c5b36d3bc5b6 100755 (executable)
@@ -42,107 +42,104 @@ check_tracking_branch () {
 }
 
 test_expect_success setup '
-
        setup_repository one &&
        setup_repository two &&
        (
-               cd two && git branch another
+               cd two &&
+               git branch another
        ) &&
        git clone one test
-
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
-(
-       cd test &&
-       tokens_match origin "$(git remote)" &&
-       check_remote_track origin master side &&
-       check_tracking_branch origin HEAD master side
-)
+       (
+               cd test &&
+               tokens_match origin "$(git remote)" &&
+               check_remote_track origin master side &&
+               check_tracking_branch origin HEAD master side
+       )
 '
 
 test_expect_success 'add another remote' '
-(
-       cd test &&
-       git remote add -f second ../two &&
-       tokens_match "origin second" "$(git remote)" &&
-       check_tracking_branch second master side another &&
-       git for-each-ref "--format=%(refname)" refs/remotes |
-       sed -e "/^refs\/remotes\/origin\//d" \
-           -e "/^refs\/remotes\/second\//d" >actual &&
-       >expect &&
-       test_cmp expect actual
-)
+       (
+               cd test &&
+               git remote add -f second ../two &&
+               tokens_match "origin second" "$(git remote)" &&
+               check_tracking_branch second master side another &&
+               git for-each-ref "--format=%(refname)" refs/remotes |
+               sed -e "/^refs\/remotes\/origin\//d" \
+                   -e "/^refs\/remotes\/second\//d" >actual &&
+               >expect &&
+               test_cmp expect actual
+       )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'check remote tracking' '
-(
-       cd test &&
-       check_remote_track origin master side &&
-       check_remote_track second master side another
-)
+test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+       (
+               cd test &&
+               check_remote_track origin master side &&
+               check_remote_track second master side another
+       )
 '
 
 test_expect_success 'remote forces tracking branches' '
-(
-       cd test &&
-       case `git config remote.second.fetch` in
-       +*) true ;;
-        *) false ;;
-       esac
-)
+       (
+               cd test &&
+               case `git config remote.second.fetch` in
+               +*) true ;;
+                *) false ;;
+               esac
+       )
 '
 
 test_expect_success 'remove remote' '
-(
-       cd test &&
-       git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
-       git remote rm second
-)
+       (
+               cd test &&
+               git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+               git remote rm second
+       )
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remove remote' '
-(
-       cd test &&
-       tokens_match origin "$(git remote)" &&
-       check_remote_track origin master side &&
-       git for-each-ref "--format=%(refname)" refs/remotes |
-       sed -e "/^refs\/remotes\/origin\//d" >actual &&
-       >expect &&
-       test_cmp expect actual
-)
+       (
+               cd test &&
+               tokens_match origin "$(git remote)" &&
+               check_remote_track origin master side &&
+               git for-each-ref "--format=%(refname)" refs/remotes |
+               sed -e "/^refs\/remotes\/origin\//d" >actual &&
+               >expect &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'remove remote protects local branches' '
-(
-       cd test &&
-       { cat >expect1 <<EOF
-Note: A branch outside the refs/remotes/ hierarchy was not removed;
-to delete it, use:
-  git branch -d master
-EOF
-       } &&
-       { cat >expect2 <<EOF
-Note: Some branches outside the refs/remotes/ hierarchy were not removed;
-to delete them, use:
-  git branch -d foobranch
-  git branch -d master
-EOF
-       } &&
-       git tag footag &&
-       git config --add remote.oops.fetch "+refs/*:refs/*" &&
-       git remote remove oops 2>actual1 &&
-       git branch foobranch &&
-       git config --add remote.oops.fetch "+refs/*:refs/*" &&
-       git remote rm oops 2>actual2 &&
-       git branch -d foobranch &&
-       git tag -d footag &&
-       test_i18ncmp expect1 actual1 &&
-       test_i18ncmp expect2 actual2
-)
-'
-
-cat > test/expect << EOF
+       (
+               cd test &&
+               cat >expect1 <<-\EOF &&
+               Note: A branch outside the refs/remotes/ hierarchy was not removed;
+               to delete it, use:
+                 git branch -d master
+               EOF
+               cat >expect2 <<-\EOF &&
+               Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+               to delete them, use:
+                 git branch -d foobranch
+                 git branch -d master
+               EOF
+               git tag footag &&
+               git config --add remote.oops.fetch "+refs/*:refs/*" &&
+               git remote remove oops 2>actual1 &&
+               git branch foobranch &&
+               git config --add remote.oops.fetch "+refs/*:refs/*" &&
+               git remote rm oops 2>actual2 &&
+               git branch -d foobranch &&
+               git tag -d footag &&
+               test_i18ncmp expect1 actual1 &&
+               test_i18ncmp expect2 actual2
+       )
+'
+
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -172,36 +169,40 @@ cat > test/expect << EOF
 EOF
 
 test_expect_success 'show' '
-       (cd test &&
-        git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
-        git fetch &&
-        git checkout -b ahead origin/master &&
-        echo 1 >> file &&
-        test_tick &&
-        git commit -m update file &&
-        git checkout master &&
-        git branch --track octopus origin/master &&
-        git branch --track rebase origin/master &&
-        git branch -d -r origin/master &&
-        git config --add remote.two.url ../two &&
-        git config --add remote.two.pushurl ../three &&
-        git config branch.rebase.rebase true &&
-        git config branch.octopus.merge "topic-a topic-b topic-c" &&
-        (cd ../one &&
-         echo 1 > file &&
-         test_tick &&
-         git commit -m update file) &&
-        git config --add remote.origin.push : &&
-        git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
-        git config --add remote.origin.push +refs/tags/lastbackup &&
-        git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
-        git config --add remote.two.push refs/heads/master:refs/heads/another &&
-        git remote show origin two > output &&
-        git branch -d rebase octopus &&
-        test_i18ncmp expect output)
-'
-
-cat > test/expect << EOF
+       (
+               cd test &&
+               git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
+               git fetch &&
+               git checkout -b ahead origin/master &&
+               echo 1 >>file &&
+               test_tick &&
+               git commit -m update file &&
+               git checkout master &&
+               git branch --track octopus origin/master &&
+               git branch --track rebase origin/master &&
+               git branch -d -r origin/master &&
+               git config --add remote.two.url ../two &&
+               git config --add remote.two.pushurl ../three &&
+               git config branch.rebase.rebase true &&
+               git config branch.octopus.merge "topic-a topic-b topic-c" &&
+               (
+                       cd ../one &&
+                       echo 1 >file &&
+                       test_tick &&
+                       git commit -m update file
+               ) &&
+               git config --add remote.origin.push : &&
+               git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+               git config --add remote.origin.push +refs/tags/lastbackup &&
+               git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+               git config --add remote.two.push refs/heads/master:refs/heads/another &&
+               git remote show origin two >output &&
+               git branch -d rebase octopus &&
+               test_i18ncmp expect output
+       )
+'
+
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -219,152 +220,187 @@ cat > test/expect << EOF
 EOF
 
 test_expect_success 'show -n' '
-       (mv one one.unreachable &&
-        cd test &&
-        git remote show -n origin > output &&
-        mv ../one.unreachable ../one &&
-        test_i18ncmp expect output)
+       mv one one.unreachable &&
+       (
+               cd test &&
+               git remote show -n origin >output &&
+               mv ../one.unreachable ../one &&
+               test_i18ncmp expect output
+       )
 '
 
 test_expect_success 'prune' '
-       (cd one &&
-        git branch -m side side2) &&
-       (cd test &&
-        git fetch origin &&
-        git remote prune origin &&
-        git rev-parse refs/remotes/origin/side2 &&
-        test_must_fail git rev-parse refs/remotes/origin/side)
+       (
+               cd one &&
+               git branch -m side side2
+       ) &&
+       (
+               cd test &&
+               git fetch origin &&
+               git remote prune origin &&
+               git rev-parse refs/remotes/origin/side2 &&
+               test_must_fail git rev-parse refs/remotes/origin/side
+       )
 '
 
 test_expect_success 'set-head --delete' '
-       (cd test &&
-        git symbolic-ref refs/remotes/origin/HEAD &&
-        git remote set-head --delete origin &&
-        test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+       (
+               cd test &&
+               git symbolic-ref refs/remotes/origin/HEAD &&
+               git remote set-head --delete origin &&
+               test_must_fail git symbolic-ref refs/remotes/origin/HEAD
+       )
 '
 
 test_expect_success 'set-head --auto' '
-       (cd test &&
-        git remote set-head --auto origin &&
-        echo refs/remotes/origin/master >expect &&
-        git symbolic-ref refs/remotes/origin/HEAD >output &&
-        test_cmp expect output
+       (
+               cd test &&
+               git remote set-head --auto origin &&
+               echo refs/remotes/origin/master >expect &&
+               git symbolic-ref refs/remotes/origin/HEAD >output &&
+               test_cmp expect output
        )
 '
 
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
 error: Multiple remote HEAD branches. Please choose one explicitly with:
   git remote set-head two another
   git remote set-head two master
 EOF
 
 test_expect_success 'set-head --auto fails w/multiple HEADs' '
-       (cd test &&
-        test_must_fail git remote set-head --auto two >output 2>&1 &&
-       test_i18ncmp expect output)
+       (
+               cd test &&
+               test_must_fail git remote set-head --auto two >output 2>&1 &&
+               test_i18ncmp expect output
+       )
 '
 
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
 refs/remotes/origin/side2
 EOF
 
 test_expect_success 'set-head explicit' '
-       (cd test &&
-        git remote set-head origin side2 &&
-        git symbolic-ref refs/remotes/origin/HEAD >output &&
-        git remote set-head origin master &&
-        test_cmp expect output)
+       (
+               cd test &&
+               git remote set-head origin side2 &&
+               git symbolic-ref refs/remotes/origin/HEAD >output &&
+               git remote set-head origin master &&
+               test_cmp expect output
+       )
 '
 
-cat > test/expect << EOF
+cat >test/expect <<EOF
 Pruning origin
 URL: $(pwd)/one
  * [would prune] origin/side2
 EOF
 
 test_expect_success 'prune --dry-run' '
-       (cd one &&
-        git branch -m side2 side) &&
-       (cd test &&
-        git remote prune --dry-run origin > output &&
-        git rev-parse refs/remotes/origin/side2 &&
-        test_must_fail git rev-parse refs/remotes/origin/side &&
-       (cd ../one &&
-        git branch -m side side2) &&
-        test_i18ncmp expect output)
+       (
+               cd one &&
+               git branch -m side2 side) &&
+       (
+               cd test &&
+               git remote prune --dry-run origin >output &&
+               git rev-parse refs/remotes/origin/side2 &&
+               test_must_fail git rev-parse refs/remotes/origin/side &&
+       (
+               cd ../one &&
+               git branch -m side side2) &&
+               test_i18ncmp expect output
+       )
 '
 
 test_expect_success 'add --mirror && prune' '
-       (mkdir mirror &&
-        cd mirror &&
-        git init --bare &&
-        git remote add --mirror -f origin ../one) &&
-       (cd one &&
-        git branch -m side2 side) &&
-       (cd mirror &&
-        git rev-parse --verify refs/heads/side2 &&
-        test_must_fail git rev-parse --verify refs/heads/side &&
-        git fetch origin &&
-        git remote prune origin &&
-        test_must_fail git rev-parse --verify refs/heads/side2 &&
-        git rev-parse --verify refs/heads/side)
+       mkdir mirror &&
+       (
+               cd mirror &&
+               git init --bare &&
+               git remote add --mirror -f origin ../one
+       ) &&
+       (
+               cd one &&
+               git branch -m side2 side
+       ) &&
+       (
+               cd mirror &&
+               git rev-parse --verify refs/heads/side2 &&
+               test_must_fail git rev-parse --verify refs/heads/side &&
+               git fetch origin &&
+               git remote prune origin &&
+               test_must_fail git rev-parse --verify refs/heads/side2 &&
+               git rev-parse --verify refs/heads/side
+       )
 '
 
 test_expect_success 'add --mirror=fetch' '
        mkdir mirror-fetch &&
        git init mirror-fetch/parent &&
-       (cd mirror-fetch/parent &&
-        test_commit one) &&
+       (
+               cd mirror-fetch/parent &&
+               test_commit one
+       ) &&
        git init --bare mirror-fetch/child &&
-       (cd mirror-fetch/child &&
-        git remote add --mirror=fetch -f parent ../parent)
+       (
+               cd mirror-fetch/child &&
+               git remote add --mirror=fetch -f parent ../parent
+       )
 '
 
 test_expect_success 'fetch mirrors act as mirrors during fetch' '
-       (cd mirror-fetch/parent &&
-        git branch new &&
-        git branch -m master renamed
+       (
+               cd mirror-fetch/parent &&
+               git branch new &&
+               git branch -m master renamed
        ) &&
-       (cd mirror-fetch/child &&
-        git fetch parent &&
-        git rev-parse --verify refs/heads/new &&
-        git rev-parse --verify refs/heads/renamed
+       (
+               cd mirror-fetch/child &&
+               git fetch parent &&
+               git rev-parse --verify refs/heads/new &&
+               git rev-parse --verify refs/heads/renamed
        )
 '
 
 test_expect_success 'fetch mirrors can prune' '
-       (cd mirror-fetch/child &&
-        git remote prune parent &&
-        test_must_fail git rev-parse --verify refs/heads/master
+       (
+               cd mirror-fetch/child &&
+               git remote prune parent &&
+               test_must_fail git rev-parse --verify refs/heads/master
        )
 '
 
 test_expect_success 'fetch mirrors do not act as mirrors during push' '
-       (cd mirror-fetch/parent &&
-        git checkout HEAD^0
+       (
+               cd mirror-fetch/parent &&
+               git checkout HEAD^0
        ) &&
-       (cd mirror-fetch/child &&
-        git branch -m renamed renamed2 &&
-        git push parent :
+       (
+               cd mirror-fetch/child &&
+               git branch -m renamed renamed2 &&
+               git push parent :
        ) &&
-       (cd mirror-fetch/parent &&
-        git rev-parse --verify renamed &&
-        test_must_fail git rev-parse --verify refs/heads/renamed2
+       (
+               cd mirror-fetch/parent &&
+               git rev-parse --verify renamed &&
+               test_must_fail git rev-parse --verify refs/heads/renamed2
        )
 '
 
 test_expect_success 'add fetch mirror with specific branches' '
        git init --bare mirror-fetch/track &&
-       (cd mirror-fetch/track &&
-        git remote add --mirror=fetch -t heads/new parent ../parent
+       (
+               cd mirror-fetch/track &&
+               git remote add --mirror=fetch -t heads/new parent ../parent
        )
 '
 
 test_expect_success 'fetch mirror respects specific branches' '
-       (cd mirror-fetch/track &&
-        git fetch parent &&
-        git rev-parse --verify refs/heads/new &&
-        test_must_fail git rev-parse --verify refs/heads/renamed
+       (
+               cd mirror-fetch/track &&
+               git fetch parent &&
+               git rev-parse --verify refs/heads/new &&
+               test_must_fail git rev-parse --verify refs/heads/renamed
        )
 '
 
@@ -372,60 +408,72 @@ test_expect_success 'add --mirror=push' '
        mkdir mirror-push &&
        git init --bare mirror-push/public &&
        git init mirror-push/private &&
-       (cd mirror-push/private &&
-        test_commit one &&
-        git remote add --mirror=push public ../public
+       (
+               cd mirror-push/private &&
+               test_commit one &&
+               git remote add --mirror=push public ../public
        )
 '
 
 test_expect_success 'push mirrors act as mirrors during push' '
-       (cd mirror-push/private &&
-        git branch new &&
-        git branch -m master renamed &&
-        git push public
+       (
+               cd mirror-push/private &&
+               git branch new &&
+               git branch -m master renamed &&
+               git push public
        ) &&
-       (cd mirror-push/private &&
-        git rev-parse --verify refs/heads/new &&
-        git rev-parse --verify refs/heads/renamed &&
-        test_must_fail git rev-parse --verify refs/heads/master
+       (
+               cd mirror-push/private &&
+               git rev-parse --verify refs/heads/new &&
+               git rev-parse --verify refs/heads/renamed &&
+               test_must_fail git rev-parse --verify refs/heads/master
        )
 '
 
 test_expect_success 'push mirrors do not act as mirrors during fetch' '
-       (cd mirror-push/public &&
-        git branch -m renamed renamed2 &&
-        git symbolic-ref HEAD refs/heads/renamed2
+       (
+               cd mirror-push/public &&
+               git branch -m renamed renamed2 &&
+               git symbolic-ref HEAD refs/heads/renamed2
        ) &&
-       (cd mirror-push/private &&
-        git fetch public &&
-        git rev-parse --verify refs/heads/renamed &&
-        test_must_fail git rev-parse --verify refs/heads/renamed2
+       (
+               cd mirror-push/private &&
+               git fetch public &&
+               git rev-parse --verify refs/heads/renamed &&
+               test_must_fail git rev-parse --verify refs/heads/renamed2
        )
 '
 
 test_expect_success 'push mirrors do not allow you to specify refs' '
        git init mirror-push/track &&
-       (cd mirror-push/track &&
-        test_must_fail git remote add --mirror=push -t new public ../public
+       (
+               cd mirror-push/track &&
+               test_must_fail git remote add --mirror=push -t new public ../public
        )
 '
 
 test_expect_success 'add alt && prune' '
-       (mkdir alttst &&
-        cd alttst &&
-        git init &&
-        git remote add -f origin ../one &&
-        git config remote.alt.url ../one &&
-        git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
-       (cd one &&
-        git branch -m side side2) &&
-       (cd alttst &&
-        git rev-parse --verify refs/remotes/origin/side &&
-        test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
-        git fetch alt &&
-        git remote prune alt &&
-        test_must_fail git rev-parse --verify refs/remotes/origin/side &&
-        git rev-parse --verify refs/remotes/origin/side2)
+       mkdir alttst &&
+       (
+               cd alttst &&
+               git init &&
+               git remote add -f origin ../one &&
+               git config remote.alt.url ../one &&
+               git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
+       ) &&
+       (
+               cd one &&
+               git branch -m side side2
+       ) &&
+       (
+               cd alttst &&
+               git rev-parse --verify refs/remotes/origin/side &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+               git fetch alt &&
+               git remote prune alt &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+               git rev-parse --verify refs/remotes/origin/side2
+       )
 '
 
 cat >test/expect <<\EOF
@@ -433,20 +481,24 @@ some-tag
 EOF
 
 test_expect_success 'add with reachable tags (default)' '
-       (cd one &&
-        >foobar &&
-        git add foobar &&
-        git commit -m "Foobar" &&
-        git tag -a -m "Foobar tag" foobar-tag &&
-        git reset --hard HEAD~1 &&
-        git tag -a -m "Some tag" some-tag) &&
-       (mkdir add-tags &&
-        cd add-tags &&
-        git init &&
-        git remote add -f origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >>../test/output &&
-        test_must_fail git config remote.origin.tagopt) &&
+       (
+               cd one &&
+               >foobar &&
+               git add foobar &&
+               git commit -m "Foobar" &&
+               git tag -a -m "Foobar tag" foobar-tag &&
+               git reset --hard HEAD~1 &&
+               git tag -a -m "Some tag" some-tag
+       ) &&
+       mkdir add-tags &&
+       (
+               cd add-tags &&
+               git init &&
+               git remote add -f origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >>../test/output &&
+               test_must_fail git config remote.origin.tagopt
+       ) &&
        test_cmp test/expect test/output
 '
 
@@ -457,14 +509,16 @@ foobar-tag
 EOF
 
 test_expect_success 'add --tags' '
-       (rm -rf add-tags &&
-        mkdir add-tags &&
-        cd add-tags &&
-        git init &&
-        git remote add -f --tags origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >>../test/output &&
-        git config remote.origin.tagopt >>../test/output) &&
+       rm -rf add-tags &&
+       (
+               mkdir add-tags &&
+               cd add-tags &&
+               git init &&
+               git remote add -f --tags origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >>../test/output &&
+               git config remote.origin.tagopt >>../test/output
+       ) &&
        test_cmp test/expect test/output
 '
 
@@ -473,25 +527,31 @@ cat >test/expect <<\EOF
 EOF
 
 test_expect_success 'add --no-tags' '
-       (rm -rf add-tags &&
-        mkdir add-no-tags &&
-        cd add-no-tags &&
-        git init &&
-        git remote add -f --no-tags origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >../test/output &&
-        git config remote.origin.tagopt >>../test/output) &&
-       (cd one &&
-        git tag -d some-tag foobar-tag) &&
+       rm -rf add-tags &&
+       (
+               mkdir add-no-tags &&
+               cd add-no-tags &&
+               git init &&
+               git remote add -f --no-tags origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >../test/output &&
+               git config remote.origin.tagopt >>../test/output
+       ) &&
+       (
+               cd one &&
+               git tag -d some-tag foobar-tag
+       ) &&
        test_cmp test/expect test/output
 '
 
 test_expect_success 'reject --no-no-tags' '
-       (cd add-no-tags &&
-        test_must_fail git remote add -f --no-no-tags neworigin ../one)
+       (
+               cd add-no-tags &&
+               test_must_fail git remote add -f --no-no-tags neworigin ../one
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   apis/master
   apis/side
   drosophila/another
@@ -500,17 +560,17 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update' '
-
-       (cd one &&
-        git remote add drosophila ../two &&
-        git remote add apis ../mirror &&
-        git remote update &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+       (
+               cd one &&
+               git remote add drosophila ../two &&
+               git remote add apis ../mirror &&
+               git remote update &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
@@ -521,34 +581,40 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update with arguments' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git remote add manduca ../mirror &&
-        git remote add megaloprepus ../mirror &&
-        git config remotes.phobaeticus "drosophila megaloprepus" &&
-        git config remotes.titanus manduca &&
-        git remote update phobaeticus titanus &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git remote add manduca ../mirror &&
+               git remote add megaloprepus ../mirror &&
+               git config remotes.phobaeticus "drosophila megaloprepus" &&
+               git config remotes.titanus manduca &&
+               git remote update phobaeticus titanus &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success 'update --prune' '
-
-       (cd one &&
-        git branch -m side2 side3) &&
-       (cd test &&
-        git remote update --prune &&
-        (cd ../one && git branch -m side3 side2) &&
-        git rev-parse refs/remotes/origin/side3 &&
-        test_must_fail git rev-parse refs/remotes/origin/side2)
+       (
+               cd one &&
+               git branch -m side2 side3
+       ) &&
+       (
+               cd test &&
+               git remote update --prune &&
+               (
+                       cd ../one &&
+                       git branch -m side3 side2
+               ) &&
+               git rev-parse refs/remotes/origin/side3 &&
+               test_must_fail git rev-parse refs/remotes/origin/side2
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<-\EOF
   apis/master
   apis/side
   manduca/master
@@ -558,176 +624,204 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update default' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remote.drosophila.skipDefaultUpdate true &&
-        git remote update default &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remote.drosophila.skipDefaultUpdate true &&
+               git remote update default &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
 EOF
 
 test_expect_success 'update default (overridden, with funny whitespace)' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remotes.default "$(printf "\t drosophila  \n")" &&
-        git remote update default &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remotes.default "$(printf "\t drosophila  \n")" &&
+               git remote update default &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success 'update (with remotes.default defined)' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remotes.default "drosophila" &&
-        git remote update &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remotes.default "drosophila" &&
+               git remote update &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success '"remote show" does not show symbolic refs' '
-
        git clone one three &&
-       (cd three &&
-        git remote show origin > output &&
-        ! grep "^ *HEAD$" < output &&
-        ! grep -i stale < output)
-
+       (
+               cd three &&
+               git remote show origin >output &&
+               ! grep "^ *HEAD$" < output &&
+               ! grep -i stale < output
+       )
 '
 
 test_expect_success 'reject adding remote with an invalid name' '
-
        test_must_fail git remote add some:url desired-name
-
 '
 
 # The first three test if the tracking branches are properly renamed,
 # the last two ones check if the config is updated.
 
 test_expect_success 'rename a remote' '
-
        git clone one four &&
-       (cd four &&
-        git remote rename origin upstream &&
-        rmdir .git/refs/remotes/origin &&
-        test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
-        test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
-        test "$(git config branch.master.remote)" = "upstream")
-
+       (
+               cd four &&
+               git remote rename origin upstream &&
+               rmdir .git/refs/remotes/origin &&
+               test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+               test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+               test "$(git config branch.master.remote)" = "upstream"
+       )
 '
 
 test_expect_success 'rename does not update a non-default fetch refspec' '
-
        git clone one four.one &&
-       (cd four.one &&
-        git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
-        git remote rename origin upstream &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
-        git rev-parse -q origin/master)
-
+       (
+               cd four.one &&
+               git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+               git remote rename origin upstream &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+               git rev-parse -q origin/master
+       )
 '
 
 test_expect_success 'rename a remote with name part of fetch spec' '
-
        git clone one four.two &&
-       (cd four.two &&
-        git remote rename origin remote &&
-        git remote rename remote upstream &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*")
-
+       (
+               cd four.two &&
+               git remote rename origin remote &&
+               git remote rename remote upstream &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
+       )
 '
 
 test_expect_success 'rename a remote with name prefix of other remote' '
-
        git clone one four.three &&
-       (cd four.three &&
-        git remote add o git://example.com/repo.git &&
-        git remote rename o upstream &&
-        test "$(git rev-parse origin/master)" = "$(git rev-parse master)")
-
+       (
+               cd four.three &&
+               git remote add o git://example.com/repo.git &&
+               git remote rename o upstream &&
+               test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+       )
 '
 
-cat > remotes_origin << EOF
+cat >remotes_origin <<EOF
 URL: $(pwd)/one
 Push: refs/heads/master:refs/heads/upstream
+Push: refs/heads/next:refs/heads/upstream2
 Pull: refs/heads/master:refs/heads/origin
+Pull: refs/heads/next:refs/heads/origin2
 EOF
 
 test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
        git clone one five &&
        origin_url=$(pwd)/one &&
-       (cd five &&
-        git remote remove origin &&
-        mkdir -p .git/remotes &&
-        cat ../remotes_origin > .git/remotes/origin &&
-        git remote rename origin origin &&
-        ! test -f .git/remotes/origin &&
-        test "$(git config remote.origin.url)" = "$origin_url" &&
-        test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
-        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+       (
+               cd five &&
+               git remote remove origin &&
+               mkdir -p .git/remotes &&
+               cat ../remotes_origin >.git/remotes/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/remotes/origin &&
+               test "$(git config remote.origin.url)" = "$origin_url" &&
+               cat >push_expected <<-\EOF &&
+               refs/heads/master:refs/heads/upstream
+               refs/heads/next:refs/heads/upstream2
+               EOF
+               cat >fetch_expected <<-\EOF &&
+               refs/heads/master:refs/heads/origin
+               refs/heads/next:refs/heads/origin2
+               EOF
+               git config --get-all remote.origin.push >push_actual &&
+               git config --get-all remote.origin.fetch >fetch_actual &&
+               test_cmp push_expected push_actual &&
+               test_cmp fetch_expected fetch_actual
+       )
 '
 
 test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
        git clone one six &&
        origin_url=$(pwd)/one &&
-       (cd six &&
-        git remote rm origin &&
-        echo "$origin_url" > .git/branches/origin &&
-        git remote rename origin origin &&
-        ! test -f .git/branches/origin &&
-        test "$(git config remote.origin.url)" = "$origin_url" &&
-        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+       (
+               cd six &&
+               git remote rm origin &&
+               echo "$origin_url" >.git/branches/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/branches/origin &&
+               test "$(git config remote.origin.url)" = "$origin_url" &&
+               test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" &&
+               test "$(git config remote.origin.push)" = "HEAD:refs/heads/master"
+       )
 '
 
-test_expect_success 'remote prune to cause a dangling symref' '
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
        git clone one seven &&
+       (
+               cd seven &&
+               git remote rm origin &&
+               echo "quux#foom" > .git/branches/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/branches/origin &&
+               test "$(git config remote.origin.url)" = "quux" &&
+               test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+               test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
+       )
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+       git clone one eight &&
        (
                cd one &&
                git checkout side2 &&
                git branch -D master
        ) &&
        (
-               cd seven &&
+               cd eight &&
                git remote prune origin
        ) >err 2>&1 &&
        test_i18ngrep "has become dangling" err &&
 
        : And the dangling symref will not cause other annoying errors &&
        (
-               cd seven &&
+               cd eight &&
                git branch -a
        ) 2>err &&
        ! grep "points nowhere" err &&
        (
-               cd seven &&
+               cd eight &&
                test_must_fail git branch nomore origin
        ) 2>err &&
        grep "dangling symref" err
 '
 
 test_expect_success 'show empty remote' '
-
        test_create_repo empty &&
        git clone empty empty-clone &&
        (
index 6af6c63350383e049082e61d3c8133af79d5a749..ed4d9c83181bc45456074f053ed5a0f137fc7e27 100755 (executable)
@@ -57,6 +57,35 @@ test_expect_success 'pulling into void does not overwrite untracked files' '
        )
 '
 
+test_expect_success 'pulling into void does not overwrite staged files' '
+       git init cloned-staged-colliding &&
+       (
+               cd cloned-staged-colliding &&
+               echo "alternate content" >file &&
+               git add file &&
+               test_must_fail git pull .. master &&
+               echo "alternate content" >expect &&
+               test_cmp expect file &&
+               git cat-file blob :file >file.index &&
+               test_cmp expect file.index
+       )
+'
+
+
+test_expect_success 'pulling into void does not remove new staged files' '
+       git init cloned-staged-new &&
+       (
+               cd cloned-staged-new &&
+               echo "new tracked file" >newfile &&
+               git add newfile &&
+               git pull .. master &&
+               echo "new tracked file" >expect &&
+               test_cmp expect newfile &&
+               git cat-file blob :newfile >newfile.index &&
+               test_cmp expect newfile.index
+       )
+'
+
 test_expect_success 'test . as a remote' '
 
        git branch copy master &&
index 4736da8f36fd4e69fea0008829f4304020ea338d..6a5ac3add47399918737168e60f75d2d7cec1557 100755 (executable)
@@ -15,17 +15,19 @@ test_expect_success 'setup bare remotes' '
 
 # $1 = local revision
 # $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
 check_pushed_commit () {
        git log -1 --format='%h %s' "$1" >expect &&
-       git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+       git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
        test_cmp expect actual
 }
 
 # $1 = push.default value
 # $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
        git -c push.default="$1" push &&
-       check_pushed_commit HEAD "$2"
+       check_pushed_commit HEAD "$2" "$3"
 }
 
 # $1 = push.default value
@@ -37,6 +39,26 @@ test_push_failure () {
        test_cmp expect actual
 }
 
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+       workflow=central
+       pushdefault=parent1
+       if test -n "${4-}"; then
+               workflow=triangular
+               pushdefault=parent2
+       fi
+       test_expect_success "push.default = $2 $1 in $workflow workflows" "
+               test_config branch.master.remote parent1 &&
+               test_config branch.master.merge refs/heads/foo &&
+               test_config remote.pushdefault $pushdefault &&
+               test_commit commit-for-$2${4+-triangular} &&
+               test_push_$1 $2 $3 ${4+repo2}
+       "
+}
+
 test_expect_success '"upstream" pushes to configured upstream' '
        git checkout master &&
        test_config branch.master.remote parent1 &&
@@ -48,7 +70,6 @@ test_expect_success '"upstream" pushes to configured upstream' '
 test_expect_success '"upstream" does not push on unconfigured remote' '
        git checkout master &&
        test_unconfig branch.master.remote &&
-       test_config push.default upstream &&
        test_commit three &&
        test_push_failure upstream
 '
@@ -57,7 +78,6 @@ test_expect_success '"upstream" does not push on unconfigured branch' '
        git checkout master &&
        test_config branch.master.remote parent1 &&
        test_unconfig branch.master.merge &&
-       test_config push.default upstream
        test_commit four &&
        test_push_failure upstream
 '
@@ -115,4 +135,41 @@ test_expect_success 'push to existing branch, upstream configured with different
        test_cmp expect-other-name actual-other-name
 '
 
+# We are on 'master', which integrates with 'foo' from parent1
+# remote (set in test_pushdefault_workflow helper).  Push to
+# parent1 in centralized, and push to parent2 in triangular workflow.
+# The parent1 repository has 'master' and 'foo' branches, while
+# the parent2 repository has only 'master' branch.
+#
+# test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+# update parent1's master (which is not our upstream)
+test_pushdefault_workflow success current master
+
+# update parent1's foo (which is our upstream)
+test_pushdefault_workflow success upstream foo
+
+# upsream is foo which is not the name of the current branch
+test_pushdefault_workflow failure simple master
+
+# master and foo are updated
+test_pushdefault_workflow success matching master
+
+# master is updated
+test_pushdefault_workflow success current master triangular
+
+# upstream mode cannot be used in triangular
+test_pushdefault_workflow failure upstream foo triangular
+
+# in triangular, 'simple' works as 'current' and update the branch
+# with the same name.
+test_pushdefault_workflow success simple master triangular
+
+# master is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching master triangular
+
 test_done
index 8956c21617410863660bff0bc22ec8e81903e81a..5a6e49d18d6f9e6e06409093d9b9f426d70de475 100755 (executable)
@@ -58,7 +58,13 @@ test_expect_success 'creating too deep nesting' \
 git clone -l -s D E &&
 git clone -l -s E F &&
 git clone -l -s F G &&
-test_must_fail git clone --bare -l -s G H'
+git clone --bare -l -s G H'
+
+test_expect_success 'invalidity of deepest repository' \
+'cd H && {
+       test_valid_repo
+       test $? -ne 0
+}'
 
 cd "$base_dir"
 
index fb07536a0f5b7e1000cf4945f55be3305df88e90..43ad7724845cd0e1523aadd60845f048a6b92ce5 100755 (executable)
@@ -39,25 +39,25 @@ test_bisection_diff()
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4
 git update-ref HEAD $(tag l5)
 
 
@@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5)
 #     F
 
 
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
-on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
-on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
-on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
-on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
-on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
-on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
-on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
-on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
-on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
-on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
-on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
-on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
-on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
-
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
-on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
-on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
-on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
-on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
-on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
-on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
-on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+on_committer_date "00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
 
 test_sequence()
 {
index e4c52b0214b5028e8c3db035dc96ca3285e61506..24d1836f417d67a224f0d9bef619952a39ed28eb 100755 (executable)
@@ -16,39 +16,34 @@ list_duplicates()
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
-on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
-on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
-on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
-on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
-on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
-on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree
+on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root
+on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2
+on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1
+on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2
+on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4
+on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0
+on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
 
 
-#
-# note: as of 20/6, it isn't possible to create duplicate parents, so this
-# can't be tested.
-#
-#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
 hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
 save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
 save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
@@ -105,6 +100,50 @@ l0
 root
 EOF
 
+test_output_expect_success 'simple date order' 'git rev-list --date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+b2
+c1
+b1
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+c3
+c2
+b3
+b2
+c1
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
 test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order  g4' <<EOF
 g4
 h2
index 0393c9fd0b1c155817855857d7476d1638d2296f..98744038eccb396c752e6a4b6ced702733c0d396 100755 (executable)
@@ -1,20 +1,45 @@
 #!/bin/sh
 
+# Copyright (c) 2009 Jens Lehmann
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
+
 test_description='git rev-list --pretty=format test'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_tick
+# String "added" in German
+# (translated with Google Translate),
+# encoded in UTF-8, used as a commit log message below.
+added=$(printf "added (hinzugef\303\274gt) foo")
+added_iso88591=$(echo "$added" | iconv -f utf-8 -t iso8859-1)
+# same but "changed"
+changed=$(printf "changed (ge\303\244ndert) foo")
+changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1)
+
 test_expect_success 'setup' '
-touch foo && git add foo && git commit -m "added foo" &&
-  echo changed >foo && git commit -a -m "changed foo"
+       : >foo &&
+       git add foo &&
+       git config i18n.commitEncoding iso8859-1 &&
+       git commit -m "$added_iso88591" &&
+       head1=$(git rev-parse --verify HEAD) &&
+       head1_short=$(git rev-parse --verify --short $head1) &&
+       tree1=$(git rev-parse --verify HEAD:) &&
+       tree1_short=$(git rev-parse --verify --short $tree1) &&
+       echo "$changed" > foo &&
+       git commit -a -m "$changed_iso88591" &&
+       head2=$(git rev-parse --verify HEAD) &&
+       head2_short=$(git rev-parse --verify --short $head2) &&
+       tree2=$(git rev-parse --verify HEAD:) &&
+       tree2_short=$(git rev-parse --verify --short $tree2)
+       git config --unset i18n.commitEncoding
 '
 
-# usage: test_format name format_string <expected_output
+# usage: test_format name format_string [failure] <expected_output
 test_format () {
        cat >expect.$1
-       test_expect_success "format $1" "
+       test_expect_${3:-success} "format $1" "
                git rev-list --pretty=format:'$2' master >output.$1 &&
                test_cmp expect.$1 output.$1
        "
@@ -32,49 +57,49 @@ has_no_color () {
        test_cmp expect "$1"
 }
 
-test_format percent %%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format percent %%h <<EOF
+commit $head2
 %h
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 %h
 EOF
 
-test_format hash %H%n%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-131a310eb913d107dd3c09a65d1651175898735d
-131a310
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
+test_format hash %H%n%h <<EOF
+commit $head2
+$head2
+$head2_short
+commit $head1
+$head1
+$head1_short
 EOF
 
-test_format tree %T%n%t <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-fe722612f26da5064c32ca3843aa154bdb0b08a0
-fe72261
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-4d5fcadc293a348e88f777dc0920f11e7d71441c
-4d5fcad
+test_format tree %T%n%t <<EOF
+commit $head2
+$tree2
+$tree2_short
+commit $head1
+$tree1
+$tree1_short
 EOF
 
-test_format parents %P%n%p <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format parents %P%n%p <<EOF
+commit $head2
+$head1
+$head1_short
+commit $head1
 
 
 EOF
 
 # we don't test relative here
-test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF
+commit $head2
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -82,14 +107,14 @@ Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 EOF
 
-test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF
+commit $head2
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -97,43 +122,45 @@ Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 EOF
 
-test_format encoding %e <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format encoding %e <<EOF
+commit $head2
+iso8859-1
+commit $head1
+iso8859-1
 EOF
 
-test_format subject %s <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+test_format subject %s <<EOF
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
-test_format body %b <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format body %b <<EOF
+commit $head2
+commit $head1
 EOF
 
-test_format raw-body %B <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
+test_format raw-body %B <<EOF
+commit $head2
+$changed
 
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head1
+$added
 
 EOF
 
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
+commit $head2
 \e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 \e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
 EOF
 
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
+commit $head2
 \e[1;31;43mfoo\e[m
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 \e[1;31;43mfoo\e[m
 EOF
 
@@ -179,46 +206,71 @@ test_expect_success '%C(auto) respects --color=auto (stdout not tty)' '
        )
 '
 
-cat >commit-msg <<'EOF'
+iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF
 Test printing of complex bodies
 
 This commit message is much longer than the others,
 and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: Â¡bueno!
+include an iso8859 character: Ã‚¡bueno!
 EOF
+
 test_expect_success 'setup complex body' '
-git config i18n.commitencoding iso8859-1 &&
-  echo change2 >foo && git commit -a -F commit-msg
+       git config i18n.commitencoding iso8859-1 &&
+       echo change2 >foo && git commit -a -F commit-msg &&
+       head3=$(git rev-parse --verify HEAD) &&
+       head3_short=$(git rev-parse --short $head3)
 '
 
-test_format complex-encoding %e <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
+test_format complex-encoding %e <<EOF
+commit $head3
+iso8859-1
+commit $head2
+iso8859-1
+commit $head1
 iso8859-1
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
-test_format complex-subject %s <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
+test_format complex-subject %s <<EOF
+commit $head3
 Test printing of complex bodies
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head2
+$changed_iso88591
+commit $head1
+$added_iso88591
 EOF
 
-test_format complex-body %b <<'EOF'
-commit 1ed88da4a5b5ed8c449114ac131efc62178734c3
-This commit message is much longer than the others,
-and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: Â¡bueno!
+test_expect_success 'prepare expected messages (for test %b)' '
+       cat <<-EOF >expected.utf-8 &&
+       commit $head3
+       This commit message is much longer than the others,
+       and it will be encoded in iso8859-1. We should therefore
+       include an iso8859 character: Â¡bueno!
+
+       commit $head2
+       commit $head1
+       EOF
+       iconv -f utf-8 -t iso8859-1 expected.utf-8 >expected.iso8859-1
+'
+
+test_format complex-body %b <expected.iso8859-1
 
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+# Git uses i18n.commitEncoding if no i18n.logOutputEncoding set
+# so unset i18n.commitEncoding to test encoding conversion
+git config --unset i18n.commitEncoding
+
+test_format complex-subject-commitencoding-unset %s <<EOF
+commit $head3
+Test printing of complex bodies
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
+test_format complex-body-commitencoding-unset %b <expected.utf-8
+
 test_expect_success '%x00 shows NUL' '
-       echo  >expect commit 1ed88da4a5b5ed8c449114ac131efc62178734c3 &&
+       echo  >expect commit $head3 &&
        echo >>expect fooQbar &&
        git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
        nul_to_q <actual.nul >actual &&
@@ -265,12 +317,12 @@ test_expect_success 'add LF before non-empty (2)' '
 
 test_expect_success 'add SP before non-empty (1)' '
        git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
-       test $(wc -w <actual) = 2
+       test $(wc -w <actual) = 3
 '
 
 test_expect_success 'add SP before non-empty (2)' '
        git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
-       test $(wc -w <actual) = 4
+       test $(wc -w <actual) = 6
 '
 
 test_expect_success '--abbrev' '
index 8f3b54d826f5a9c8e7b08df7f6a58398b5a62480..88d60c1ce2933141c0800a76fdebca9037803258 100755 (executable)
@@ -91,12 +91,12 @@ test_expect_success 'update-index --remove' '
        test_cmp expected 1
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
        setup_absent &&
        test -z "$(git ls-files -d)"
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
        setup_dirty &&
        test -z "$(git ls-files -d)"
 '
index 300be86c385a93588e987f591357c57421b9cbae..8d4b50d1b5816d69ab165119773a7ffc0c360c0a 100755 (executable)
@@ -9,6 +9,19 @@ Documented tests for git reset'
 
 . ./test-lib.sh
 
+commit_msg () {
+       # String "modify 2nd file (changed)" partly in German
+       # (translated with Google Translate),
+       # encoded in UTF-8, used as a commit log message below.
+       msg="modify 2nd file (ge\303\244ndert)\n"
+       if test -n "$1"
+       then
+               printf "$msg" | iconv -f utf-8 -t "$1"
+       else
+               printf "$msg"
+       fi
+}
+
 test_expect_success 'creating initial files and commits' '
        test_tick &&
        echo "1st file" >first &&
@@ -28,7 +41,7 @@ test_expect_success 'creating initial files and commits' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git commit -a -m "modify 2nd file" &&
+       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
        head5=$(git rev-parse --verify HEAD)
 '
 # git log --pretty=oneline # to see those SHA1 involved
@@ -44,6 +57,20 @@ check_changes () {
        done | test_cmp .cat_expect -
 }
 
+test_expect_success 'reset --hard message' '
+       hex=$(git log -1 --format="%h") &&
+       git reset --hard > .actual &&
+       echo HEAD is now at $hex $(commit_msg) > .expected &&
+       test_cmp .expected .actual
+'
+
+test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' '
+       hex=$(git log -1 --format="%h") &&
+       git -c "i18n.logOutputEncoding=iso8859-1" reset --hard > .actual &&
+       echo HEAD is now at $hex $(commit_msg iso8859-1) > .expected &&
+       test_cmp .expected .actual
+'
+
 >.diff_expect
 >.cached_expect
 cat >.cat_expect <<EOF
@@ -192,7 +219,8 @@ test_expect_success \
        'changing files and redo the last commit should succeed' '
        echo "3rd line 2nd file" >>secondfile &&
        git commit -a -C ORIG_HEAD &&
-       check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+       head4=$(git rev-parse --verify HEAD) &&
+       check_changes $head4 &&
        test "$(git rev-parse ORIG_HEAD)" = \
                        $head5
 '
@@ -211,7 +239,7 @@ test_expect_success \
        git reset --hard HEAD~2 &&
        check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
        test "$(git rev-parse ORIG_HEAD)" = \
-                       3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+                       $head4
 '
 
 >.diff_expect
@@ -303,7 +331,7 @@ test_expect_success 'redoing the last two commits should succeed' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git commit -a -m "modify 2nd file" &&
+       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
        check_changes $head5
 '
 
@@ -326,10 +354,11 @@ test_expect_success '--hard reset to HEAD should clear a failed merge' '
        git checkout branch2 &&
        echo "3rd line in branch2" >>secondfile &&
        git commit -a -m "change in branch2" &&
+       head3=$(git rev-parse --verify HEAD) &&
 
        test_must_fail git pull . branch1 &&
        git reset --hard &&
-       check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+       check_changes $head3
 '
 
 >.diff_expect
index f47cc7b6044ba5f745f7b80ace0fcba0c255f4e5..5ee97b003a879a0b6a558265e94c18582aa836c0 100755 (executable)
@@ -212,6 +212,32 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
        test_cmp empty untracked
 '
 
+test_expect_success 'submodule add in subdirectory' '
+       echo "refs/heads/master" >expect &&
+       >empty &&
+
+       mkdir addtest/sub &&
+       (
+               cd addtest/sub &&
+               git submodule add "$submodurl" ../realsubmod3 &&
+               git submodule init
+       ) &&
+
+       rm -f heads head untracked &&
+       inspect addtest/realsubmod3 ../.. &&
+       test_cmp expect heads &&
+       test_cmp expect head &&
+       test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+       (
+               cd addtest/sub &&
+               test_must_fail git submodule add ../../ submod3 2>../../output.err
+       ) &&
+       test_i18ngrep toplevel output.err
+'
+
 test_expect_success 'setup - add an example entry to .gitmodules' '
        GIT_CONFIG=.gitmodules \
        git config submodule.example.url git://example.com/init.git
@@ -319,6 +345,26 @@ test_expect_success 'status should be "up-to-date" after update' '
        grep "^ $rev1" list
 '
 
+test_expect_success 'status "up-to-date" from subdirectory' '
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule status >../list
+       ) &&
+       grep "^ $rev1" list &&
+       grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule status ../init >../list
+       ) &&
+       grep "^ $rev1" list &&
+       grep "\\.\\./init" list
+'
+
 test_expect_success 'status should be "modified" after submodule commit' '
        (
                cd init &&
@@ -399,6 +445,25 @@ test_expect_success 'update --init' '
        git rev-parse --resolve-git-dir init/.git
 '
 
+test_expect_success 'update --init from subdirectory' '
+       mv init init2 &&
+       git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+       git config --remove-section submodule.example &&
+       test_must_fail git config submodule.example.url &&
+
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule update ../init >update.out &&
+               cat update.out &&
+               test_i18ngrep "not initialized" update.out &&
+               test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+               git submodule update --init ../init
+       ) &&
+       git rev-parse --resolve-git-dir init/.git
+'
+
 test_expect_success 'do not add files from a submodule' '
 
        git reset --hard &&
@@ -772,6 +837,21 @@ test_expect_success 'submodule deinit should remove the whole submodule section
        rmdir init
 '
 
+test_expect_success 'submodule deinit from subdirectory' '
+       git submodule update --init &&
+       git config submodule.example.foo bar &&
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule deinit ../init >../output
+       ) &&
+       grep "\\.\\./init" output &&
+       test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+       test -f example2/.git &&
+       rmdir init
+'
+
 test_expect_success 'submodule deinit . deinits all initialized submodules' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
@@ -868,4 +948,35 @@ test_expect_success 'submodule deinit fails when submodule has a .git directory
        test -n "$(git config --get-regexp "submodule\.example\.")"
 '
 
+test_expect_success 'submodule with UTF-8 name' '
+       svname=$(printf "\303\245 \303\244\303\266") &&
+       mkdir "$svname" &&
+       (
+               cd "$svname" &&
+               git init &&
+               >sub &&
+               git add sub &&
+               git commit -m "init sub"
+       ) &&
+       test_config core.precomposeunicode true &&
+       git submodule add ./"$svname" &&
+       git submodule >&2 &&
+       test -n "$(git submodule | grep "$svname")"
+'
+
+test_expect_success 'submodule add clone shallow submodule' '
+       mkdir super &&
+       pwd=$(pwd)
+       (
+               cd super &&
+               git init &&
+               git submodule add --depth=1 file://"$pwd"/example2 submodule &&
+               (
+                       cd submodule &&
+                       test 1 = $(git log --oneline | wc -l)
+               )
+       )
+'
+
+
 test_done
index 30b429e7dcbcd56af21ded67128aa18901133275..ac2434c0dbdaf837d8dd7f896e680fbb7b6ab954 100755 (executable)
@@ -45,6 +45,42 @@ EOF
        test_cmp expected actual
 "
 
+test_expect_success 'added submodule (subdirectory)' "
+       mkdir sub &&
+       (
+               cd sub &&
+               git submodule summary >../actual
+       ) &&
+       cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+       test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+       (
+               cd sub &&
+               git submodule summary . >../actual
+       ) &&
+       >expected &&
+       test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+       (
+               cd sub &&
+               git submodule summary ../sm1 >../actual
+       ) &&
+       cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+       test_cmp expected actual
+"
+
 commit_file sm1 &&
 head2=$(add_file sm1 foo3)
 
@@ -76,8 +112,8 @@ head3=$(
 )
 
 test_expect_success 'modified submodule(backward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
@@ -89,8 +125,8 @@ EOF
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -102,15 +138,15 @@ EOF
 "
 
 test_expect_success '--summary-limit' "
-    git submodule summary -n 3 >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary -n 3 >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -122,8 +158,8 @@ rm -f sm1 &&
 mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
-    git submodule summary --cached >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary --cached >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
@@ -132,59 +168,59 @@ EOF
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
-    git submodule summary --files >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary --files >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head4(submodule) (3):
   > Add foo5
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 rm -f sm1 &&
 test_create_repo sm1 &&
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
-    test_i18ncmp expected actual
+       test_i18ncmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -192,43 +228,43 @@ head7=$(add_file sm2 foo8 foo9) &&
 git add sm2
 
 test_expect_success 'multiple submodules' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
-    git submodule summary sm2 >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary sm2 >actual &&
+       cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
-    git submodule summary HEAD^ >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary HEAD^ >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
-    git submodule summary --for-status HEAD^ >actual &&
-    test_i18ncmp actual - <<EOF
+       git submodule summary --for-status HEAD^ >actual &&
+       test_i18ncmp actual - <<EOF
 # Submodule changes to be committed:
 #
 # * sm1 $head6...0000000:
@@ -240,14 +276,14 @@ EOF
 "
 
 test_expect_success 'fail when using --files together with --cached' "
-    test_must_fail git submodule summary --files --cached
+       test_must_fail git submodule summary --files --cached
 "
 
 test_expect_success 'should not fail in an empty repo' "
-    git init xyzzy &&
-    cd xyzzy &&
-    git submodule summary >output 2>&1 &&
-    test_cmp output /dev/null
+       git init xyzzy &&
+       cd xyzzy &&
+       git submodule summary >output 2>&1 &&
+       test_cmp output /dev/null
 "
 
 test_done
index 94e26c47eae37062ca0d7d24d30104fb46d06743..79bc135bf69ea5813f0a372db6bbefa0f9a92753 100755 (executable)
@@ -11,216 +11,338 @@ These tests exercise the "git submodule sync" subcommand.
 . ./test-lib.sh
 
 test_expect_success setup '
-       echo file > file &&
+       echo file >file &&
        git add file &&
        test_tick &&
        git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
-       (cd submodule &&
-        git submodule add ../submodule sub-submodule &&
-        test_tick &&
-        git commit -m "sub-submodule"
+       (
+               cd submodule &&
+               git submodule add ../submodule sub-submodule &&
+               test_tick &&
+               git commit -m "sub-submodule"
        ) &&
-       (cd super &&
-        git submodule add ../submodule submodule &&
-        test_tick &&
-        git commit -m "submodule"
+       (
+               cd super &&
+               git submodule add ../submodule submodule &&
+               test_tick &&
+               git commit -m "submodule"
        ) &&
        git clone super super-clone &&
-       (cd super-clone && git submodule update --init --recursive) &&
+       (
+               cd super-clone &&
+               git submodule update --init --recursive
+       ) &&
        git clone super empty-clone &&
-       (cd empty-clone && git submodule init) &&
+       (
+               cd empty-clone &&
+               git submodule init
+       ) &&
        git clone super top-only-clone &&
        git clone super relative-clone &&
-       (cd relative-clone && git submodule update --init --recursive) &&
+       (
+               cd relative-clone &&
+               git submodule update --init --recursive
+       ) &&
        git clone super recursive-clone &&
-       (cd recursive-clone && git submodule update --init --recursive)
+       (
+               cd recursive-clone &&
+               git submodule update --init --recursive
+       )
 '
 
 test_expect_success 'change submodule' '
-       (cd submodule &&
-        echo second line >> file &&
-        test_tick &&
-        git commit -a -m "change submodule"
+       (
+               cd submodule &&
+               echo second line >>file &&
+               test_tick &&
+               git commit -a -m "change submodule"
        )
 '
 
+reset_submodule_urls () {
+       local root
+       root=$(pwd) &&
+       (
+               cd super-clone/submodule &&
+               git config remote.origin.url "$root/submodule"
+       ) &&
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url "$root/submodule"
+       )
+}
+
 test_expect_success 'change submodule url' '
-       (cd super &&
-        cd submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super &&
+               cd submodule &&
+               git checkout master &&
+               git pull
        ) &&
        mv submodule moved-submodule &&
-       (cd moved-submodule &&
-        git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
-        test_tick &&
-        git commit -a -m moved-sub-submodule
+       (
+               cd moved-submodule &&
+               git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
+               test_tick &&
+               git commit -a -m moved-sub-submodule
        ) &&
-       (cd super &&
-        git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
-        test_tick &&
-        git commit -a -m moved-submodule
+       (
+               cd super &&
+               git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
+               test_tick &&
+               git commit -a -m moved-submodule
        )
 '
 
 test_expect_success '"git submodule sync" should update submodule URLs' '
-       (cd super-clone &&
-        git pull --no-recurse-submodules &&
-        git submodule sync
+       (
+               cd super-clone &&
+               git pull --no-recurse-submodules &&
+               git submodule sync
        ) &&
-       test -d "$(cd super-clone/submodule &&
-        git config remote.origin.url
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
        )" &&
-       test ! -d "$(cd super-clone/submodule/sub-submodule &&
-        git config remote.origin.url
+       test ! -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
        )" &&
-       (cd super-clone/submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super-clone/submodule &&
+               git checkout master &&
+               git pull
        ) &&
-       (cd super-clone &&
-        test -d "$(git config submodule.submodule.url)"
+       (
+               cd super-clone &&
+               test -d "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync --recursive" should update all submodule URLs' '
-       (cd super-clone &&
-        (cd submodule &&
-         git pull --no-recurse-submodules
-        ) &&
-        git submodule sync --recursive
+       (
+               cd super-clone &&
+               (
+                       cd submodule &&
+                       git pull --no-recurse-submodules
+               ) &&
+               git submodule sync --recursive
+       ) &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
+       )" &&
+       test -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
+       )" &&
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git checkout master &&
+               git pull
+       )
+'
+
+test_expect_success 'reset submodule URLs' '
+       reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+       (
+               cd super-clone &&
+               git pull --no-recurse-submodules &&
+               mkdir -p sub &&
+               cd sub &&
+               git submodule sync >../../output
+       ) &&
+       grep "\\.\\./submodule" output &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
+       )" &&
+       test ! -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
+       )" &&
+       (
+               cd super-clone/submodule &&
+               git checkout master &&
+               git pull
+       ) &&
+       (
+               cd super-clone &&
+               test -d "$(git config submodule.submodule.url)"
+       )
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+       (
+               cd super-clone &&
+               (
+                       cd submodule &&
+                       git pull --no-recurse-submodules
+               ) &&
+               mkdir -p sub &&
+               cd sub &&
+               git submodule sync --recursive >../../output
        ) &&
-       test -d "$(cd super-clone/submodule &&
-        git config remote.origin.url
+       grep "\\.\\./submodule/sub-submodule" output &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
        )" &&
-       test -d "$(cd super-clone/submodule/sub-submodule &&
-        git config remote.origin.url
+       test -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
        )" &&
-       (cd super-clone/submodule/sub-submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git checkout master &&
+               git pull
        )
 '
 
 test_expect_success '"git submodule sync" should update known submodule URLs' '
-       (cd empty-clone &&
-        git pull &&
-        git submodule sync &&
-        test -d "$(git config submodule.submodule.url)"
+       (
+               cd empty-clone &&
+               git pull &&
+               git submodule sync &&
+               test -d "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
-       (cd top-only-clone &&
-        git pull &&
-        git submodule sync &&
-        test -z "$(git config submodule.submodule.url)" &&
-        git submodule sync submodule &&
-        test -z "$(git config submodule.submodule.url)"
+       (
+               cd top-only-clone &&
+               git pull &&
+               git submodule sync &&
+               test -z "$(git config submodule.submodule.url)" &&
+               git submodule sync submodule &&
+               test -z "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo' '
-       (cd relative-clone &&
-        git remote set-url origin foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual fails with: "cannot strip off url foo
-        test "$(git config remote.origin.url)" = "../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual fails with: "cannot strip off url foo
+                       test "$(git config remote.origin.url)" = "../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
-       (cd submodule/sub-submodule &&
-        test "$(git config remote.origin.url)" != "../../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               ) &&
+               (
+                       cd submodule/sub-submodule &&
+                       test "$(git config remote.origin.url)" != "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync --recursive" propagates changes in origin' '
-       (cd recursive-clone &&
-        git remote set-url origin foo/bar &&
-        git submodule sync --recursive &&
-       (cd submodule &&
-        #actual foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
-       (cd submodule/sub-submodule &&
-        test "$(git config remote.origin.url)" = "../../foo/submodule"
-       )
+       (
+               cd recursive-clone &&
+               git remote set-url origin foo/bar &&
+               git submodule sync --recursive &&
+               (
+                       cd submodule &&
+                       #actual foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               ) &&
+               (
+                       cd submodule/sub-submodule &&
+                       test "$(git config remote.origin.url)" = "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
-       (cd relative-clone &&
-        git remote set-url origin ./foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ./submodule
-        test "$(git config remote.origin.url)" = "../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ./foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ./submodule
+                       test "$(git config remote.origin.url)" = "../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin ./foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ./foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ./foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ./foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ../submodule
-        test "$(git config remote.origin.url)" = "../../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ../submodule
+                       test "$(git config remote.origin.url)" = "../../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ../foo/submodule
-        test "$(git config remote.origin.url)" = "../../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ../foo/submodule
+                       test "$(git config remote.origin.url)" = "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo/bar &&
-        mkdir -p a/b/c &&
-        ( cd a/b/c &&
-          git init &&
-          :> .gitignore &&
-          git add .gitignore &&
-          test_tick &&
-          git commit -m "initial commit" ) &&
-        git submodule add ../bar/a/b/c ./a/b/c &&
-        git submodule sync &&
-       (cd a/b/c &&
-        #actual ../foo/bar/a/b/c
-        test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo/bar &&
+               mkdir -p a/b/c &&
+               (
+                       cd a/b/c &&
+                       git init &&
+                       >.gitignore &&
+                       git add .gitignore &&
+                       test_tick &&
+                       git commit -m "initial commit"
+               ) &&
+               git submodule add ../bar/a/b/c ./a/b/c &&
+               git submodule sync &&
+               (
+                       cd a/b/c &&
+                       #actual ../foo/bar/a/b/c
+                       test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+               )
        )
 '
 
index a4ffea0dbe743a1bbe4c5512037a6b1ffde1c313..b192f936bcbe794f0691550d1bf38714b1359d01 100755 (executable)
@@ -80,6 +80,21 @@ test_expect_success 'submodule update detaching the HEAD ' '
        )
 '
 
+test_expect_success 'submodule update from subdirectory' '
+       (cd super/submodule &&
+        git reset --hard HEAD~1
+       ) &&
+       mkdir super/sub &&
+       (cd super/sub &&
+        (cd ../submodule &&
+         compare_head
+        ) &&
+        git submodule update ../submodule &&
+        cd ../submodule &&
+        ! compare_head
+       )
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
        (cd submodule &&
@@ -279,6 +294,35 @@ test_expect_success 'submodule update - checkout in .git/config' '
        )
 '
 
+test_expect_success 'submodule update - command in .git/config' '
+       (cd super &&
+        git config submodule.submodule.update "!git checkout"
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule update - command in .git/config catches failure' '
+       (cd super &&
+        git config submodule.submodule.update "!false"
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        test_must_fail git submodule update submodule
+       )
+'
+
 test_expect_success 'submodule init picks up rebase' '
        (cd super &&
         git config -f .gitmodules submodule.rebasing.update rebase &&
@@ -685,14 +729,24 @@ test_expect_success 'submodule update properly revives a moved submodule' '
 test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
        mkdir -p linked/dir &&
        ln -s linked/dir linkto &&
-       (
-               cd linkto &&
-               git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
-               (
-                       cd super &&
-                       git submodule update --init --recursive
-               )
+       (cd linkto &&
+        git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+        (cd super &&
+         git submodule update --init --recursive
+        )
        )
 '
 
+test_expect_success 'submodule update clone shallow submodule' '
+       git clone cloned super3 &&
+       pwd=$(pwd)
+       (cd super3 &&
+        sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+        mv -f .gitmodules.tmp .gitmodules &&
+        git submodule update --init --depth=3
+        (cd submodule &&
+         test 1 = $(git log --oneline | wc -l)
+        )
+       )
+'
 test_done
index 107b4b7c455dc0be6400eb1d462868b940b1247d..91d4fd1facf8dc6f97ecd77cdfcdd03b7e1d8927 100755 (executable)
@@ -80,6 +80,22 @@ test_expect_success 'test basic "submodule foreach" usage' '
        test_i18ncmp expect actual
 '
 
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+       mkdir clone/sub &&
+       (
+               cd clone/sub &&
+               git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+       ) &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'setup nested submodules' '
        git clone submodule nested1 &&
        git clone submodule nested2 &&
index 436b7b606e3eab09a0dd8c1b67ab90e43c24dffc..bdc1f295030b61fd6d4fc0ef672dffa99c027d8b 100755 (executable)
@@ -13,9 +13,9 @@ commit_msg_is () {
        expect=commit_msg_is.expect
        actual=commit_msg_is.actual
 
-       printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
-       printf "%s" "$1" >$actual &&
-       test_i18ncmp $expect $actual
+       printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
+       printf "%s" "$1" >"$expect" &&
+       test_i18ncmp "$expect" "$actual"
 }
 
 # A sanity check to see if commit is working at all.
index e2ffdacc267c22e369247791e24338a20badf94d..ac3d0fe44507328f6363edc1d614bc9d8369899b 100755 (executable)
@@ -1335,4 +1335,66 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' '
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
+test_expect_success 'setup of test environment' '
+       git config status.showUntrackedFiles no &&
+       git status -s >expected_short &&
+       git status --no-short >expected_noshort
+'
+
+test_expect_success '"status.short=true" same as "-s"' '
+       git -c status.short=true status >actual &&
+       test_cmp expected_short actual
+'
+
+test_expect_success '"status.short=true" weaker than "--no-short"' '
+       git -c status.short=true status --no-short >actual &&
+       test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" same as "--no-short"' '
+       git -c status.short=false status >actual &&
+       test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" weaker than "-s"' '
+       git -c status.short=false status -s >actual &&
+       test_cmp expected_short actual
+'
+
+test_expect_success '"status.branch=true" same as "-b"' '
+       git status -sb >expected_branch &&
+       git -c status.branch=true status -s >actual &&
+       test_cmp expected_branch actual
+'
+
+test_expect_success '"status.branch=true" different from "--no-branch"' '
+       git status -s --no-branch  >expected_nobranch &&
+       git -c status.branch=true status -s >actual &&
+       test_must_fail test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--no-branch"' '
+       git -c status.branch=true status -s --no-branch >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--porcelain"' '
+       git -c status.branch=true status --porcelain >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" same as "--no-branch"' '
+       git -c status.branch=false status -s >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" weaker than "-b"' '
+       git -c status.branch=false status -sb >actual &&
+       test_cmp expected_branch actual
+'
+
+test_expect_success 'Restore default test environment' '
+       git config --unset status.showUntrackedFiles
+'
+
 test_done
index 4f09beca90ec18c9e541f1f880471f9f2de0de8a..31a798fda21f178cbbedf47ee92dbd997f9c4e42 100755 (executable)
@@ -77,7 +77,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -104,7 +104,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '
        echo three >main.txt &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -136,7 +136,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '
        ONTO=$(git rev-parse --short rebase_i_conflicts) &&
        test_must_fail git rebase -i rebase_i_conflicts &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -162,7 +162,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '
        test_must_fail git rebase -i rebase_i_conflicts &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -188,10 +188,9 @@ test_expect_success 'status when rebasing -i in edit mode' '
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~2) &&
-       TGT=$(git rev-parse --short two_rebase_i) &&
        git rebase -i HEAD~2 &&
        cat >expected <<-EOF &&
-       # HEAD detached from $TGT
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -216,9 +215,8 @@ test_expect_success 'status when splitting a commit' '
        ONTO=$(git rev-parse --short HEAD~3) &&
        git rebase -i HEAD~3 &&
        git reset HEAD^ &&
-       TGT=$(git rev-parse --short HEAD) &&
        cat >expected <<-EOF &&
-       # HEAD detached at $TGT
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -246,11 +244,10 @@ test_expect_success 'status after editing the last commit with --amend during a
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~3) &&
-       TGT=$(git rev-parse --short three_amend) &&
        git rebase -i HEAD~3 &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $TGT
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -280,7 +277,7 @@ test_expect_success 'status: (continue first edit) second edit' '
        git rebase -i HEAD~3 &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -302,7 +299,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -329,7 +326,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -351,7 +348,7 @@ test_expect_success 'status: (amend first edit) second edit' '
        git commit --amend -m "a" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -374,7 +371,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -402,7 +399,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "d" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -426,7 +423,7 @@ test_expect_success 'status: (split first edit) second edit' '
        git commit -m "e" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -451,7 +448,7 @@ test_expect_success 'status: (split first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -481,7 +478,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "h" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -510,7 +507,7 @@ test_expect_success 'status in an am session: file already exists' '
        cat >expected <<-\EOF &&
        # On branch am_already_exists
        # You are in the middle of an am session.
-       #   (fix conflicts and then run "git am --resolved")
+       #   (fix conflicts and then run "git am --continue")
        #   (use "git am --skip" to skip this patch)
        #   (use "git am --abort" to restore the original branch)
        #
@@ -532,7 +529,7 @@ test_expect_success 'status in an am session: file does not exist' '
        cat >expected <<-\EOF &&
        # On branch am_not_exists
        # You are in the middle of an am session.
-       #   (fix conflicts and then run "git am --resolved")
+       #   (fix conflicts and then run "git am --continue")
        #   (use "git am --skip" to skip this patch)
        #   (use "git am --abort" to restore the original branch)
        #
@@ -601,7 +598,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
        #
        # Unmerged paths:
@@ -669,7 +666,7 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
        test_i18ncmp expected actual
 '
 
-test_expect_success 'status showing detached from a tag' '
+test_expect_success 'status showing detached at and from a tag' '
        test_commit atag tagging &&
        git checkout atag &&
        cat >expected <<-\EOF
@@ -677,6 +674,14 @@ test_expect_success 'status showing detached from a tag' '
        nothing to commit (use -u to show untracked files)
        EOF
        git status --untracked-files=no >actual &&
+       test_i18ncmp expected actual &&
+
+       git reset --hard HEAD^ &&
+       cat >expected <<-\EOF
+       # HEAD detached from atag
+       nothing to commit (use -u to show untracked files)
+       EOF
+       git status --untracked-files=no >actual &&
        test_i18ncmp expected actual
 '
 
index 2f70433568e3134b09d7e1eb10cc5c8995c1a106..3ff5fb853c941586e3e5ed69a74a628dfe5bb84a 100755 (executable)
@@ -316,7 +316,7 @@ test_expect_success 'merge c1 with c2 (squash)' '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
-test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+test_expect_success 'unsuccessful merge of c1 with c2 (squash, ff-only)' '
        git reset --hard c1 &&
        test_must_fail git merge --squash --ff-only c2
 '
@@ -497,9 +497,15 @@ test_expect_success 'combining --squash and --no-ff is refused' '
        test_must_fail git merge --no-ff --squash c1
 '
 
-test_expect_success 'combining --ff-only and --no-ff is refused' '
-       test_must_fail git merge --ff-only --no-ff c1 &&
-       test_must_fail git merge --no-ff --ff-only c1
+test_expect_success 'option --ff-only overwrites --no-ff' '
+       git merge --no-ff --ff-only c1 &&
+       test_must_fail git merge --no-ff --ff-only c2
+'
+
+test_expect_success 'option --ff-only overwrites merge.ff=only config' '
+       git reset --hard c0 &&
+       test_config merge.ff only &&
+       git merge --no-ff c1
 '
 
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
index 9f46f22ca87be40e217c2ffb199368f0afbf9548..2813aa9a61dc52d6d72f684c51fe6894e623754d 100755 (executable)
@@ -956,6 +956,20 @@ test_expect_success $PREREQ 'utf8 author is correctly passed on' '
        grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
 '
 
+test_expect_success $PREREQ 'utf8 sender is not duplicated' '
+       clean_fake_sendmail &&
+       test_commit weird_sender &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+       git format-patch --stdout -1 >funny_name.patch &&
+       git send-email --from="Füñný Nâmé <odd_?=mail@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         funny_name.patch &&
+       grep "^From: " msgtxt1 >msgfrom &&
+       test_line_count = 1 msgfrom
+'
+
 test_expect_success $PREREQ 'sendemail.composeencoding works' '
        clean_fake_sendmail &&
        git config sendemail.composeencoding iso-8859-1 &&
index eeefa6790454d83bfb4ce740f7b1843c6a71c79c..b0d1d94bf417a7c3cdd4f1b07334416aacbee18d 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'gitattributes setting eol=lf produces lf newlines' '
                git init &&
                echo "* eol=lf" >.gitattributes &&
                git p4 sync //depot@all &&
-               git checkout master &&
+               git checkout -b master p4/master &&
                test_cmp "$cli"/f-unix-orig f-unix &&
                test_cmp "$cli"/f-win-as-lf f-win
        )
@@ -109,7 +109,7 @@ test_expect_success 'gitattributes setting eol=crlf produces crlf newlines' '
                git init &&
                echo "* eol=crlf" >.gitattributes &&
                git p4 sync //depot@all &&
-               git checkout master &&
+               git checkout -b master p4/master &&
                test_cmp "$cli"/f-unix-as-crlf f-unix &&
                test_cmp "$cli"/f-win-orig f-win
        )
index 81a1657efb18e88a448886aed60bb7e5fdb36212..272a071e85b0b7c6b34da06fd7492a257b09fa52 100755 (executable)
@@ -69,7 +69,7 @@ run_completion ()
        local -a COMPREPLY _words
        local _cword
        _words=( $1 )
-       test "${1: -1}" == ' ' && _words+=('')
+       test "${1: -1}" = ' ' && _words+=('')
        (( _cword = ${#_words[@]} - 1 ))
        __git_wrap__git_main && print_comp
 }
@@ -122,6 +122,140 @@ test_gitcomp_nl ()
 
 invalid_variable_name='${foo.bar}'
 
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for __gitdir tests' '
+       mkdir -p subdir/subsubdir &&
+       git init otherrepo
+'
+
+test_expect_success '__gitdir - from command line (through $__git_dir)' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+       echo "otherrepo/.git" >expected &&
+       __gitdir "otherrepo" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+       echo "remote" >expected &&
+       __gitdir "remote" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in cwd' '
+       echo ".git" >expected &&
+       __gitdir >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in parent' '
+       echo "$(pwd -P)/.git" >expected &&
+       (
+               cd subdir/subsubdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - cwd is a .git directory' '
+       echo "." >expected &&
+       (
+               cd .git &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - parent is a .git directory' '
+       echo "$(pwd -P)/.git" >expected &&
+       (
+               cd .git/refs/heads &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               export GIT_DIR &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               export GIT_DIR &&
+               cd subdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing $GIT_DIR' '
+       (
+               GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+               export GIT_DIR &&
+               test_must_fail __gitdir
+       )
+'
+
+test_expect_success '__gitdir - gitfile in cwd' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+       test_when_finished "rm -f subdir/.git" &&
+       (
+               cd subdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - gitfile in parent' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+       test_when_finished "rm -f subdir/.git" &&
+       (
+               cd subdir/subsubdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       mkdir otherrepo/dir &&
+       test_when_finished "rm -rf otherrepo/dir" &&
+       ln -s otherrepo/dir link &&
+       test_when_finished "rm -f link" &&
+       (
+               cd link &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - not a git repository' '
+       (
+               cd subdir/subsubdir &&
+               GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+               export GIT_CEILING_DIRECTORIES &&
+               test_must_fail __gitdir
+       )
+'
+
 test_expect_success '__gitcomp - trailing space - options' '
        test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
                --reset-author" <<-EOF
@@ -231,6 +365,7 @@ test_expect_success 'double dash "git" itself' '
        --exec-path Z
        --exec-path=
        --html-path Z
+       --man-path Z
        --info-path Z
        --work-tree=
        --namespace=
index 15521cc4f9be68ccd105916cee1322041682e8d5..3c3e4e8c38023595978368e0b1665e122e0bdfab 100755 (executable)
@@ -10,528 +10,558 @@ test_description='test git-specific bash prompt functions'
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
+c_red='\\[\\e[31m\\]'
+c_green='\\[\\e[32m\\]'
+c_lblue='\\[\\e[1;34m\\]'
+c_clear='\\[\\e[0m\\]'
 
 test_expect_success 'setup for prompt tests' '
-       mkdir -p subdir/subsubdir &&
        git init otherrepo &&
-       echo 1 > file &&
+       echo 1 >file &&
        git add file &&
        test_tick &&
        git commit -m initial &&
        git tag -a -m msg1 t1 &&
        git checkout -b b1 &&
-       echo 2 > file &&
+       echo 2 >file &&
        git commit -m "second b1" file &&
-       echo 3 > file &&
+       echo 3 >file &&
        git commit -m "third b1" file &&
        git tag -a -m msg2 t2 &&
        git checkout -b b2 master &&
-       echo 0 > file &&
+       echo 0 >file &&
        git commit -m "second b2" file &&
-       echo 00 > file &&
+       echo 00 >file &&
        git commit -m "another b2" file &&
-       echo 000 > file &&
+       echo 000 >file &&
        git commit -m "yet another b2" file &&
        git checkout master
 '
 
-test_expect_success 'gitdir - from command line (through $__git_dir)' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - repo as argument' '
-       echo "otherrepo/.git" > expected &&
-       __gitdir "otherrepo" > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - remote as argument' '
-       echo "remote" > expected &&
-       __gitdir "remote" > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in cwd' '
-       echo ".git" > expected &&
-       __gitdir > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in parent' '
-       echo "$(pwd -P)/.git" > expected &&
-       (
-               cd subdir/subsubdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - cwd is a .git directory' '
-       echo "." > expected &&
-       (
-               cd .git &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - parent is a .git directory' '
-       echo "$(pwd -P)/.git" > expected &&
-       (
-               cd .git/refs/heads &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-               export GIT_DIR &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-               export GIT_DIR &&
-               cd subdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - non-existing $GIT_DIR' '
-       (
-               GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
-               export GIT_DIR &&
-               test_must_fail __gitdir
-       )
-'
-
-test_expect_success 'gitdir - gitfile in cwd' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-       test_when_finished "rm -f subdir/.git" &&
-       (
-               cd subdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - gitfile in parent' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-       test_when_finished "rm -f subdir/.git" &&
-       (
-               cd subdir/subsubdir &&
-               __gitdir > "$actual"
-       ) &&
+test_expect_success 'prompt - branch name' '
+       printf " (master)" >expected &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
-test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       mkdir otherrepo/dir &&
-       test_when_finished "rm -rf otherrepo/dir" &&
-       ln -s otherrepo/dir link &&
-       test_when_finished "rm -f link" &&
-       (
-               cd link &&
-               __gitdir > "$actual"
-       ) &&
+test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+       printf " (master)" >expected &&
+       test_when_finished "git checkout master" &&
+       test_config core.preferSymlinkRefs true &&
+       git checkout master &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
-test_expect_success 'gitdir - not a git repository' '
-       (
-               cd subdir/subsubdir &&
-               GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
-               export GIT_CEILING_DIRECTORIES &&
-               test_must_fail __gitdir
-       )
-'
-
-test_expect_success 'prompt - branch name' '
-       printf " (master)" > expected &&
-       __git_ps1 > "$actual" &&
+test_expect_success 'prompt - unborn branch' '
+       printf " (unborn)" >expected &&
+       git checkout --orphan unborn &&
+       test_when_finished "git checkout master" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - detached head' '
-       printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected &&
+       printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+       test_config core.abbrev 13 &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - contains' '
-       printf " ((t2~1))" > expected &&
+       printf " ((t2~1))" >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=contains &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - branch' '
-       printf " ((b1~1))" > expected &&
+       printf " ((b1~1))" >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=branch &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - describe' '
-       printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected &&
+       printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=describe &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - default' '
-       printf " ((t2))" > expected &&
+       printf " ((t2))" >expected &&
        git checkout --detach b1 &&
        test_when_finished "git checkout master" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - deep inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                cd .git/refs/heads &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside bare repository' '
-       printf " (BARE:master)" > expected &&
+       printf " (BARE:master)" >expected &&
        git init --bare bare.git &&
        test_when_finished "rm -rf bare.git" &&
        (
                cd bare.git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - interactive rebase' '
-       printf " (b1|REBASE-i 2/3)" > expected
-       echo "#!$SHELL_PATH" >fake_editor.sh &&
-       cat >>fake_editor.sh <<\EOF &&
-echo "exec echo" > "$1"
-echo "edit $(git log -1 --format="%h")" >> "$1"
-echo "exec echo" >> "$1"
-EOF
+       printf " (b1|REBASE-i 2/3)" >expected
+       write_script fake_editor.sh <<-\EOF &&
+               echo "exec echo" >"$1"
+               echo "edit $(git log -1 --format="%h")" >>"$1"
+               echo "exec echo" >>"$1"
+       EOF
        test_when_finished "rm -f fake_editor.sh" &&
-       chmod a+x fake_editor.sh &&
        test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
        git checkout b1 &&
        test_when_finished "git checkout master" &&
        git rebase -i HEAD^ &&
        test_when_finished "git rebase --abort"
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase merge' '
-       printf " (b2|REBASE-m 1/3)" > expected &&
+       printf " (b2|REBASE-m 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase --merge b1 b2 &&
        test_when_finished "git rebase --abort" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase' '
-       printf " (b2|REBASE 1/3)" > expected &&
+       printf " (b2|REBASE 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase b1 b2 &&
        test_when_finished "git rebase --abort" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - merge' '
-       printf " (b1|MERGING)" > expected &&
+       printf " (b1|MERGING)" >expected &&
        git checkout b1 &&
        test_when_finished "git checkout master" &&
        test_must_fail git merge b2 &&
        test_when_finished "git reset --hard" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - cherry-pick' '
-       printf " (master|CHERRY-PICKING)" > expected &&
+       printf " (master|CHERRY-PICKING)" >expected &&
        test_must_fail git cherry-pick b1 &&
        test_when_finished "git reset --hard" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bisect' '
-       printf " (master|BISECTING)" > expected &&
+       printf " (master|BISECTING)" >expected &&
        git bisect start &&
        test_when_finished "git bisect reset" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - clean' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty worktree' '
-       printf " (master *)" > expected &&
-       echo "dirty" > file &&
+       printf " (master *)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index' '
-       printf " (master +)" > expected &&
-       echo "dirty" > file &&
+       printf " (master +)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
-       printf " (master *+)" > expected &&
-       echo "dirty index" > file &&
+       printf " (master *+)" >expected &&
+       echo "dirty index" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
-       echo "dirty worktree" > file &&
+       echo "dirty worktree" >file &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - before root commit' '
-       printf " (master #)" > expected &&
+       printf " (master #)" >expected &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                cd otherrepo &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState false &&
        (
                sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState true &&
        (
                sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState false &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
-       printf " (master *)" > expected &&
-       echo "dirty" > file &&
+       printf " (master *)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState true &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
-       echo "dirty" > file &&
+       printf " (GIT_DIR!)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - no stash' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - stash' '
-       printf " (master $)" > expected &&
+       printf " (master $)" >expected &&
        echo 2 >file &&
        git stash &&
        test_when_finished "git stash drop" &&
+       git pack-refs --all &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        echo 2 >file &&
        git stash &&
        test_when_finished "git stash drop" &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - no untracked files' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
                cd otherrepo &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - untracked files' '
-       printf " (master %%)" > expected &&
+       printf " (master %%)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles false &&
        (
                sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles true &&
        (
                sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles false &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
-       printf " (master %%)" > expected &&
+       printf " (master %%)" >expected &&
        test_config bash.showUntrackedFiles true &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - format string starting with dash' '
-       printf -- "-master" > expected &&
-       __git_ps1 "-%s" > "$actual" &&
+       printf -- "-master" >expected &&
+       __git_ps1 "-%s" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - pc mode' '
+       printf "BEFORE: (master):AFTER" >expected &&
+       printf "" >expected_output &&
+       (
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+               test_cmp expected_output "$actual" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - branch name' '
+       printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - detached head' '
+       printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
+       git checkout b1^ &&
+       test_when_finished "git checkout master" &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       git add -u &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
+       echo "dirty index" >file &&
+       test_when_finished "git reset --hard" &&
+       git add -u &&
+       echo "dirty worktree" >file &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               cd otherrepo &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+       printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               cd .git &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
+       echo 2 >file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       (
+               GIT_PS1_SHOWSTASHSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWUNTRACKEDFILES=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - zsh color pc mode' '
+       printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+       (
+               ZSH_VERSION=5.0.0 &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+               printf "%s" "$PS1" >"$actual"
+       ) &&
        test_cmp expected "$actual"
 '
 
index 8828ff78f184a451fb43709771cc39bf17186cfb..a7e9aacbb2d9b05b0d5083a0fd70cb9287fcf454 100644 (file)
@@ -343,6 +343,7 @@ test_declared_prereq () {
 }
 
 test_expect_failure () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
@@ -357,10 +358,11 @@ test_expect_failure () {
                        test_known_broken_failure_ "$1"
                fi
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 test_expect_success () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -375,7 +377,7 @@ test_expect_success () {
                        test_failure_ "$@"
                fi
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 # test_external runs external test scripts that provide continuous
index 43e2a391aa4e0b8db88a43cbaede76f09585ac18..b490283182ce29f91519fd390f2a9639efa8c09c 100644 (file)
@@ -185,6 +185,9 @@ do
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
+       --verbose-only=*)
+               verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
                # Ignore --quiet under a TAP::Harness. Saying how many tests
                # passed without the ok/not ok details is always an error.
@@ -199,17 +202,39 @@ do
        --valgrind=*)
                valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --valgrind-only=*)
+               valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-parallel=*)
+               valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-stride=*)
+               valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-offset=*)
+               valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        --tee)
                shift ;; # was handled already
        --root=*)
                root=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --statusprefix=*)
+               statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        *)
                echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
 done
 
-test -n "$valgrind" && verbose=t
+if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+then
+       test -z "$valgrind" && valgrind=memcheck
+       test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+       verbose=t
+fi
 
 if test -n "$color"
 then
@@ -304,12 +329,12 @@ trap 'die' EXIT
 
 test_ok_ () {
        test_success=$(($test_success + 1))
-       say_color "" "ok $test_count - $@"
+       say_color "" "${statusprefix}ok $test_count - $@"
 }
 
 test_failure_ () {
        test_failure=$(($test_failure + 1))
-       say_color error "not ok $test_count - $1"
+       say_color error "${statusprefix}not ok $test_count - $1"
        shift
        echo "$@" | sed -e 's/^/#       /'
        test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
@@ -317,18 +342,83 @@ test_failure_ () {
 
 test_known_broken_ok_ () {
        test_fixed=$(($test_fixed+1))
-       say_color error "ok $test_count - $@ # TODO known breakage vanished"
+       say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished"
 }
 
 test_known_broken_failure_ () {
        test_broken=$(($test_broken+1))
-       say_color warn "not ok $test_count - $@ # TODO known breakage"
+       say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
        test "$debug" = "" || eval "$1"
 }
 
+match_pattern_list () {
+       arg="$1"
+       shift
+       test -z "$*" && return 1
+       for pattern_
+       do
+               case "$arg" in
+               $pattern_)
+                       return 0
+               esac
+       done
+       return 1
+}
+
+maybe_teardown_verbose () {
+       test -z "$verbose_only" && return
+       exec 4>/dev/null 3>/dev/null
+       verbose=
+}
+
+last_verbose=t
+maybe_setup_verbose () {
+       test -z "$verbose_only" && return
+       if match_pattern_list $test_count $verbose_only ||
+               { test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
+       then
+               exec 4>&2 3>&1
+               # Emit a delimiting blank line when going from
+               # non-verbose to verbose.  Within verbose mode the
+               # delimiter is printed by test_expect_*.  The choice
+               # of the initial $last_verbose is such that before
+               # test 1, we do not print it.
+               test -z "$last_verbose" && echo >&3 ""
+               verbose=t
+       else
+               exec 4>/dev/null 3>/dev/null
+               verbose=
+       fi
+       last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       GIT_VALGRIND_ENABLED=
+}
+
+maybe_setup_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=t
+               return
+       fi
+       GIT_VALGRIND_ENABLED=
+       if match_pattern_list $test_count $valgrind_only
+       then
+               GIT_VALGRIND_ENABLED=t
+       elif test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
+       then
+               GIT_VALGRIND_ENABLED=t
+       fi
+}
+
 test_eval_ () {
        # This is a separate function because some tests use
        # "return" to end a test_expect_success block early.
@@ -338,8 +428,10 @@ test_eval_ () {
 test_run_ () {
        test_cleanup=:
        expecting_failure=$2
+       setup_malloc_check
        test_eval_ "$1"
        eval_ret=$?
+       teardown_malloc_check
 
        if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
        then
@@ -354,17 +446,24 @@ test_run_ () {
        return "$eval_ret"
 }
 
-test_skip () {
+test_start_ () {
        test_count=$(($test_count+1))
+       maybe_setup_verbose
+       maybe_setup_valgrind
+}
+
+test_finish_ () {
+       echo >&3 ""
+       maybe_teardown_valgrind
+       maybe_teardown_verbose
+}
+
+test_skip () {
        to_skip=
-       for skp in $GIT_SKIP_TESTS
-       do
-               case $this_test.$test_count in
-               $skp)
-                       to_skip=t
-                       break
-               esac
-       done
+       if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+       then
+               to_skip=t
+       fi
        if test -z "$to_skip" && test -n "$test_prereq" &&
           ! test_have_prereq "$test_prereq"
        then
@@ -378,8 +477,8 @@ test_skip () {
                        of_prereq=" of $test_prereq"
                fi
 
-               say_color skip >&3 "skipping test: $@"
-               say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
+               say_color skip >&3 "${statusprefix}skipping test: $@"
+               say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
                : true
                ;;
        *)
@@ -396,6 +495,8 @@ test_at_end_hook_ () {
 test_done () {
        GIT_EXIT_OK=t
 
+       # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
+       # output file
        if test -z "$HARNESS_ACTIVE"
        then
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
@@ -415,11 +516,11 @@ test_done () {
 
        if test "$test_fixed" != 0
        then
-               say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
+               say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)"
        fi
        if test "$test_broken" != 0
        then
-               say_color warn "# still have $test_broken known breakage(s)"
+               say_color warn "${statusprefix}# still have $test_broken known breakage(s)"
        fi
        if test "$test_broken" != 0 || test "$test_fixed" != 0
        then
@@ -442,9 +543,9 @@ test_done () {
                then
                        if test $test_remaining -gt 0
                        then
-                               say_color pass "# passed all $msg"
+                               say_color pass "${statusprefix}# passed all $msg"
                        fi
-                       say "1..$test_count$skip_all"
+                       say "${statusprefix}1..$test_count$skip_all"
                fi
 
                test -d "$remove_trash" &&
@@ -458,8 +559,8 @@ test_done () {
        *)
                if test $test_external_has_tap -eq 0
                then
-                       say_color error "# failed $test_failure among $msg"
-                       say "1..$test_count"
+                       say_color error "${statusprefix}# failed $test_failure among $msg"
+                       say "${statusprefix}1..$test_count"
                fi
 
                exit 1 ;;
@@ -467,6 +568,9 @@ test_done () {
        esac
 }
 
+
+# Set up a directory that we can put in PATH which redirects all git
+# calls to 'valgrind git ...'.
 if test -n "$valgrind"
 then
        make_symlink () {
@@ -514,31 +618,43 @@ then
                make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
        }
 
-       # override all git executables in TEST_DIRECTORY/..
-       GIT_VALGRIND=$TEST_DIRECTORY/valgrind
-       mkdir -p "$GIT_VALGRIND"/bin
-       for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
-       do
-               make_valgrind_symlink $file
-       done
-       # special-case the mergetools loadables
-       make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
-       OLDIFS=$IFS
-       IFS=:
-       for path in $PATH
-       do
-               ls "$path"/git-* 2> /dev/null |
-               while read file
+       # In the case of --valgrind-parallel, we only need to do the
+       # wrapping once, in the main script.  The worker children all
+       # have $valgrind_only_stride set, so we can skip based on that.
+       if test -z "$valgrind_only_stride"
+       then
+               # override all git executables in TEST_DIRECTORY/..
+               GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+               mkdir -p "$GIT_VALGRIND"/bin
+               for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
                do
-                       make_valgrind_symlink "$file"
+                       make_valgrind_symlink $file
                done
-       done
-       IFS=$OLDIFS
+               # special-case the mergetools loadables
+               make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
+               OLDIFS=$IFS
+               IFS=:
+               for path in $PATH
+               do
+                       ls "$path"/git-* 2> /dev/null |
+                       while read file
+                       do
+                               make_valgrind_symlink "$file"
+                       done
+               done
+               IFS=$OLDIFS
+       fi
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
        GIT_VALGRIND_MODE="$valgrind"
        export GIT_VALGRIND_MODE
+       GIT_VALGRIND_ENABLED=t
+       if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=
+       fi
+       export GIT_VALGRIND_ENABLED
 elif test -n "$GIT_TEST_INSTALLED"
 then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
@@ -623,21 +739,53 @@ then
 else
        mkdir -p "$TRASH_DIRECTORY"
 fi
+
+# Gross hack to spawn N sub-instances of the tests in parallel, and
+# summarize the results.  Note that if this is enabled, the script
+# terminates at the end of this 'if' block.
+if test -n "$valgrind_parallel"
+then
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               mkdir "$root"
+               TEST_OUTPUT_DIRECTORY="$root" \
+                       ${SHELL_PATH} "$0" \
+                       --root="$root" --statusprefix="[$i] " \
+                       --valgrind="$valgrind" \
+                       --valgrind-only-stride="$valgrind_parallel" \
+                       --valgrind-only-offset="$i" &
+               pids="$pids $!"
+       done
+       trap "kill $pids" INT TERM HUP
+       wait $pids
+       trap - INT TERM HUP
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
+                       sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
+               test_count=$(expr $test_count + $inner_total)
+               test_success=$(expr $test_success + $inner_success)
+               test_fixed=$(expr $test_fixed + $inner_fixed)
+               test_broken=$(expr $test_broken + $inner_broken)
+               test_failure=$(expr $test_failure + $inner_failed)
+       done
+       test_done
+fi
+
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$TRASH_DIRECTORY" || exit 1
 
 this_test=${0##*/}
 this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
-       case "$this_test" in
-       $skp)
-               say_color info >&3 "skipping test $this_test altogether"
-               skip_all="skip all tests in $this_test"
-               test_done
-       esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+       say_color info >&3 "skipping test $this_test altogether"
+       skip_all="skip all tests in $this_test"
+       test_done
+fi
 
 # Provide an implementation of the 'yes' utility
 yes () {
@@ -738,7 +886,14 @@ test_i18ngrep () {
 
 test_lazy_prereq PIPE '
        # test whether the filesystem supports FIFOs
-       rm -f testfifo && mkfifo testfifo
+       case $(uname -s) in
+       CYGWIN*)
+               false
+               ;;
+       *)
+               rm -f testfifo && mkfifo testfifo
+               ;;
+       esac
 '
 
 test_lazy_prereq SYMLINKS '
index 6b87c91b60cde4b19d6644227548f1ba6b226545..42153036dcfdee66e54fa4bd777ad3c36233d640 100755 (executable)
@@ -4,6 +4,9 @@ base=$(basename "$0")
 
 TOOL_OPTIONS='--leak-check=no'
 
+test -z "$GIT_VALGRIND_ENABLED" &&
+exec "$GIT_VALGRIND"/../../"$base" "$@"
+
 case "$GIT_VALGRIND_MODE" in
 memcheck-fast)
        ;;
diff --git a/test-prio-queue.c b/test-prio-queue.c
new file mode 100644 (file)
index 0000000..7be72f0
--- /dev/null
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+       const int *a = va, *b = vb;
+       return *a - *b;
+}
+
+static void show(int *v)
+{
+       if (!v)
+               printf("NULL\n");
+       else
+               printf("%d\n", *v);
+       free(v);
+}
+
+int main(int argc, char **argv)
+{
+       struct prio_queue pq = { intcmp };
+
+       while (*++argv) {
+               if (!strcmp(*argv, "get"))
+                       show(prio_queue_get(&pq));
+               else if (!strcmp(*argv, "dump")) {
+                       int *v;
+                       while ((v = prio_queue_get(&pq)))
+                              show(v);
+               }
+               else {
+                       int *v = malloc(sizeof(*v));
+                       *v = atoi(*argv);
+                       prio_queue_put(&pq, v);
+               }
+       }
+
+       return 0;
+}
index 359a671c8ca9232944cd6e92a2ac0a9eda7b429d..e15db9808c85a3918a8de3c409500aaa78d1328e 100644 (file)
@@ -1232,7 +1232,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
                 * then local and remote refs are likely to still be equal.
                 * Just feed them all to the fetch method in that case.
                 * This condition shouldn't be met in a non-deepening fetch
-                * (see builtin-fetch.c:quickfetch()).
+                * (see builtin/fetch.c:quickfetch()).
                 */
                heads = xmalloc(nr_refs * sizeof(*heads));
                for (rm = refs; rm; rm = rm->next)
index 4edebc535583f655cc120d169677ea221af554b3..ea70ea7e4a1b61c3006c614712ddd1aa41205ac6 100644 (file)
@@ -170,7 +170,7 @@ int transport_connect(struct transport *transport, const char *name,
 int transport_helper_init(struct transport *transport, const char *name);
 int bidirectional_transfer_loop(int input, int output);
 
-/* common methods used by transport.c and builtin-send-pack.c */
+/* common methods used by transport.c and builtin/send-pack.c */
 void transport_verify_remote_names(int nr_heads, const char **heads);
 
 void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);
index 438a40d63739d9397829e83d7af241054f729235..9c4d086b51364ff42a044be95e6ea55dffe7b79e 100644 (file)
@@ -127,6 +127,7 @@ void wt_status_prepare(struct wt_status *s)
        s->change.strdup_strings = 1;
        s->untracked.strdup_strings = 1;
        s->ignored.strdup_strings = 1;
+       s->show_branch = -1;  /* unspecified */
 }
 
 static void wt_status_print_unmerged_header(struct wt_status *s)
@@ -826,7 +827,7 @@ static void show_am_in_progress(struct wt_status *s,
        if (advice_status_hints) {
                if (!state->am_empty_patch)
                        status_printf_ln(s, color,
-                               _("  (fix conflicts and then run \"git am --resolved\")"));
+                               _("  (fix conflicts and then run \"git am --continue\")"));
                status_printf_ln(s, color,
                        _("  (use \"git am --skip\" to skip this patch)"));
                status_printf_ln(s, color,
@@ -1037,7 +1038,6 @@ static char *read_and_strip_branch(const char *path)
 }
 
 struct grab_1st_switch_cbdata {
-       int found;
        struct strbuf buf;
        unsigned char nsha1[20];
 };
@@ -1061,7 +1061,6 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        for (end = target; *end && *end != '\n'; end++)
                ;
        strbuf_add(&cb->buf, target, end - target);
-       cb->found = 1;
        return 1;
 }
 
@@ -1178,7 +1177,10 @@ void wt_status_print(struct wt_status *s)
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
-                       if (state.detached_from) {
+                       if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
+                               on_what = _("rebase in progress; onto ");
+                               branch_name = state.onto;
+                       } else if (state.detached_from) {
                                unsigned char sha1[20];
                                branch_name = state.detached_from;
                                if (!get_sha1("HEAD", sha1) &&
index 219a3bbca613192d8b127659e4bfc0d60f38cf10..c0339919ccf78c4fe7d78123f9c3e891075d0602 100644 (file)
@@ -39,6 +39,8 @@ extern "C" {
 #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
 #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
index b2eb6db2c851aea7eb08b17d802155876bef211c..2358a2d6326e54308413cb8a5e6b61eba06324e9 100644 (file)
@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
        xch->i2 = i2;
        xch->chg1 = chg1;
        xch->chg2 = chg2;
+       xch->ignore = 0;
 
        return xch;
 }
@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        xdchange_t *xch, *xche;
 
        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;
                if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
                                     xch->i2, xche->i2 + xche->chg2 - xch->i2,
                                     ecb->priv) < 0)
@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        return 0;
 }
 
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+       xdchange_t *xch;
+
+       for (xch = xscr; xch; xch = xch->next) {
+               int ignore = 1;
+               xrecord_t **rec;
+               long i;
+
+               rec = &xe->xdf1.recs[xch->i1];
+               for (i = 0; i < xch->chg1 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+               rec = &xe->xdf2.recs[xch->i2];
+               for (i = 0; i < xch->chg2 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+               xch->ignore = ignore;
+       }
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
        if (xscr) {
+               if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+                       xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
                if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
                        xdl_free_script(xscr);
index 7a92ea9c4d84a559ae1d0bd90ebe667828d8f9cb..8b81206c9af0767bd91c4b9e453f7c5c2bde47b1 100644 (file)
@@ -41,6 +41,7 @@ typedef struct s_xdchange {
        struct s_xdchange *next;
        long i1, i2;
        long chg1, chg2;
+       int ignore;
 } xdchange_t;
 
 
index d11dbf9f13c13db16fa05f7539170862d6da9795..4d8645867e7d0103d49882877a7e4a8abacd24ea 100644 (file)
@@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
 /*
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
  */
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
-       xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+       xdchange_t *xch, *xchp, *lxch;
        long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+       long max_ignorable = xecfg->ctxlen;
+       unsigned long ignored = 0; /* number of ignored blank lines */
+
+       /* remove ignorable changes that are too far before other changes */
+       for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+               xch = xchp->next;
+
+               if (xch == NULL ||
+                   xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+                       *xscr = xch;
+       }
+
+       if (*xscr == NULL)
+               return NULL;
+
+       lxch = *xscr;
 
-       for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-               if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+       for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+               long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+               if (distance > max_common)
                        break;
 
-       return xchp;
+               if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+                       lxch = xch;
+                       ignored = 0;
+               } else if (distance < max_ignorable && xch->ignore) {
+                       ignored += xch->chg2;
+               } else if (lxch != xchp &&
+                          xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
+                       break;
+               } else if (!xch->ignore) {
+                       lxch = xch;
+                       ignored = 0;
+               } else {
+                       ignored += xch->chg2;
+               }
+       }
+
+       return lxch;
 }
 
 
@@ -139,7 +174,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                return xdl_emit_common(xe, xscr, ecb, xecfg);
 
        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;
 
                s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
                s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
index c2e2e830273782dc597606ddbb0401c04dce8f8f..d29710770ce40bafa6e9eb2b2ea7c9c8ba43c727 100644 (file)
@@ -27,7 +27,7 @@
 typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                           xdemitconf_t const *xecfg);
 
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg);
 
index 9504eaecb8ac45248ea22b0779aa7492cd15d83e..62cb23dfd37743e4985655998ccabd56db160233 100644 (file)
@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
        return nl + 1;
 }
 
+int xdl_blankline(const char *line, long size, long flags)
+{
+       long i;
+
+       if (!(flags & XDF_WHITESPACE_FLAGS))
+               return (size <= 1);
+
+       for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+               ;
+
+       return (i == size);
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
index ad1428ed699383bf6f736b28033aeb9dbe114b76..4646ce575251b07053f20285be99422d6576603e 100644 (file)
@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
 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);