Merge branch 'dt/refs-check-refname-component-optim'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:52 +0000 (12:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:52 +0000 (12:18 -0700)
* dt/refs-check-refname-component-optim:
refs.c: optimize check_refname_component()

168 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes/2.1.0.txt
Documentation/config.txt
Documentation/diff-config.txt
Documentation/git-bisect.txt
Documentation/git-config.txt
Documentation/git-daemon.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-format-patch.txt
Documentation/git-help.txt
Documentation/git-ls-files.txt
Documentation/git-patch-id.txt
Documentation/git-read-tree.txt
Documentation/git-replace.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gitweb.conf.txt
Documentation/glossary-content.txt
Documentation/howto/keep-canonical-history-correct.txt [new file with mode: 0644]
Documentation/howto/setup-git-server-over-http.txt
Documentation/revisions.txt
Documentation/technical/api-argv-array.txt
Documentation/technical/api-run-command.txt
Documentation/technical/api-strbuf.txt
Documentation/technical/http-protocol.txt
Documentation/user-manual.txt
Makefile
advice.c
argv-array.c
argv-array.h
builtin/blame.c
builtin/checkout.c
builtin/commit.c
builtin/config.c
builtin/fast-export.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/log.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/mv.c
builtin/patch-id.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/tag.c
builtin/update-index.c
cache.h
column.c
combine-diff.c
commit.c
compat/bswap.h
compat/mingw.c
compat/mingw.h
compat/poll/poll.c
config.c
connect.c
contrib/completion/git-completion.bash
contrib/credential/wincred/Makefile
contrib/diffall/README [deleted file]
contrib/diffall/git-diffall [deleted file]
contrib/subtree/Makefile
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
contrib/svn-fe/svnrdump_sim.py
contrib/vim/README [deleted file]
daemon.c
diff-lib.c
diff.c
dir.c
environment.c
fast-import.c
git-compat-util.h
git-instaweb.sh
git-p4.py
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-remote-testgit.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
http-push.c
http.c
http.h
imap-send.c
merge-recursive.c
notes.c
pack-objects.c
pack-revindex.c
pager.c
parse-options.h
path.c
perl/Git/SVN/Log.pm
pretty.c
reflog-walk.c
refs.c
refs.h
remote-curl.c
remote.c
revision.c
run-command.c
run-command.h
sha1_file.c
sideband.c
strbuf.c
strbuf.h
t/README
t/check-non-portable-shell.pl
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/broken-smart-http.sh [changed mode: 0755->0644]
t/lib-httpd/error.sh [new file with mode: 0755]
t/t0008-ignores.sh
t/t1400-update-ref.sh
t/t1410-reflog.sh
t/t1507-rev-parse-upstream.sh
t/t2107-update-index-basic.sh
t/t3032-merge-recursive-options.sh
t/t3404-rebase-interactive.sh
t/t3420-rebase-autostash.sh
t/t3910-mac-os-precompose.sh
t/t4014-format-patch.sh
t/t4039-diff-assume-unchanged.sh
t/t4041-diff-submodule-option.sh
t/t4201-shortlog.sh
t/t4204-patch-id.sh
t/t4205-log-pretty-formats.sh
t/t5000-tar-tree.sh
t/t5001-archive-attr.sh
t/t5003-archive-zip.sh
t/t5004-archive-corner-cases.sh
t/t5150-request-pull.sh
t/t5538-push-shallow.sh
t/t5542-push-http-shallow.sh [new file with mode: 0755]
t/t5550-http-fetch-dumb.sh
t/t5560-http-backend-noserver.sh
t/t5801-remote-helpers.sh
t/t6006-rev-list-format.sh
t/t6039-merge-ignorecase.sh [new file with mode: 0755]
t/t6050-replace.sh
t/t7007-show.sh
t/t7102-reset.sh
t/t7500-commit.sh
t/t7502-commit.sh
t/t7508-status.sh
t/t7600-merge.sh
t/t8003-blame-corner-cases.sh
t/t9138-git-svn-authors-prog.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9807-git-p4-submit.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
unicode_width.h [new file with mode: 0644]
update_unicode.sh [new file with mode: 0755]
utf8.c
wt-status.c
index dc600f9b36d09f0668064e044520c7ce633f09d8..42294e59a1fcf5d744199b6a0feafd4ca6e3eda1 100644 (file)
 /config.mak.autogen
 /config.mak.append
 /configure
+/unicode
 /tags
 /TAGS
 /cscope*
index f424dbd75c80abb4d6fecce18f6fc25859bdcb00..4d90c77c7bd643b11df8d3094efe142493df53b9 100644 (file)
@@ -18,6 +18,14 @@ code.  For Git in general, three rough rules are:
    judgement call, the decision based more on real world
    constraints people face than what the paper standard says.
 
+ - Fixing style violations while working on a real change as a
+   preparatory clean-up step is good, but otherwise avoid useless code
+   churn for the sake of conforming to the style.
+
+   "Once it _is_ in the tree, it's not really worth the patch noise to
+   go and fix it up."
+   Cf. http://article.gmane.org/gmane.linux.kernel/943020
+
 Make your code readable and sensible, and don't try to be clever.
 
 As for more concrete guidelines, just imitate the existing code
@@ -34,7 +42,17 @@ For shell scripts specifically (not exhaustive):
 
  - We use tabs for indentation.
 
- - Case arms are indented at the same depth as case and esac lines.
+ - Case arms are indented at the same depth as case and esac lines,
+   like this:
+
+       case "$variable" in
+       pattern1)
+               do this
+               ;;
+       pattern2)
+               do that
+               ;;
+       esac
 
  - Redirection operators should be written with space before, but no
    space after them.  In other words, write 'echo test >"$file"'
@@ -43,6 +61,14 @@ For shell scripts specifically (not exhaustive):
    redirection target in a variable (as shown above), our code does so
    because some versions of bash issue a warning without the quotes.
 
+       (incorrect)
+       cat hello > world < universe
+       echo hello >$world
+
+       (correct)
+       cat hello >world <universe
+       echo hello >"$world"
+
  - We prefer $( ... ) for command substitution; unlike ``, it
    properly nests.  It should have been the way Bourne spelled
    it from day one, but unfortunately isn't.
@@ -81,14 +107,33 @@ For shell scripts specifically (not exhaustive):
    "then" should be on the next line for if statements, and "do"
    should be on the next line for "while" and "for".
 
+       (incorrect)
+       if test -f hello; then
+               do this
+       fi
+
+       (correct)
+       if test -f hello
+       then
+               do this
+       fi
+
  - We prefer "test" over "[ ... ]".
 
  - We do not write the noiseword "function" in front of shell
    functions.
 
- - We prefer a space between the function name and the parentheses. The
-   opening "{" should also be on the same line.
-   E.g.: my_function () {
+ - We prefer a space between the function name and the parentheses,
+   and no space inside the parentheses. The opening "{" should also
+   be on the same line.
+
+       (incorrect)
+       my_function(){
+               ...
+
+       (correct)
+       my_function () {
+               ...
 
  - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
    [::], [==], or [..]) for portability.
@@ -106,6 +151,19 @@ For shell scripts specifically (not exhaustive):
    interface translatable. See "Marking strings for translation" in
    po/README.
 
+ - We do not write our "test" command with "-a" and "-o" and use "&&"
+   or "||" to concatenate multiple "test" commands instead, because
+   the use of "-a/-o" is often error-prone.  E.g.
+
+     test -n "$x" -a "$a" = "$b"
+
+   is buggy and breaks when $x is "=", but
+
+     test -n "$x" && test "$a" = "$b"
+
+   does not have such a problem.
+
+
 For C programs:
 
  - We use tabs to indent, and interpret tabs as taking up to
@@ -149,7 +207,7 @@ For C programs:
    of "else if" statements, it can make sense to add braces to
    single line blocks.
 
- - We try to avoid assignments inside if().
+ - We try to avoid assignments in the condition of an "if" statement.
 
  - Try to make your code understandable.  You may put comments
    in, but comments invariably tend to stale out when the code
@@ -177,6 +235,88 @@ For C programs:
  - Double negation is often harder to understand than no negation
    at all.
 
+ - There are two schools of thought when it comes to comparison,
+   especially inside a loop. Some people prefer to have the less stable
+   value on the left hand side and the more stable value on the right hand
+   side, e.g. if you have a loop that counts variable i down to the
+   lower bound,
+
+       while (i > lower_bound) {
+               do something;
+               i--;
+       }
+
+   Other people prefer to have the textual order of values match the
+   actual order of values in their comparison, so that they can
+   mentally draw a number line from left to right and place these
+   values in order, i.e.
+
+       while (lower_bound < i) {
+               do something;
+               i--;
+       }
+
+   Both are valid, and we use both.  However, the more "stable" the
+   stable side becomes, the more we tend to prefer the former
+   (comparison with a constant, "i > 0", is an extreme example).
+   Just do not mix styles in the same part of the code and mimic
+   existing styles in the neighbourhood.
+
+ - There are two schools of thought when it comes to splitting a long
+   logical line into multiple lines.  Some people push the second and
+   subsequent lines far enough to the right with tabs and align them:
+
+        if (the_beginning_of_a_very_long_expression_that_has_to ||
+               span_more_than_a_single_line_of ||
+               the_source_text) {
+                ...
+
+   while other people prefer to align the second and the subsequent
+   lines with the column immediately inside the opening parenthesis,
+   with tabs and spaces, following our "tabstop is always a multiple
+   of 8" convention:
+
+        if (the_beginning_of_a_very_long_expression_that_has_to ||
+           span_more_than_a_single_line_of ||
+           the_source_text) {
+                ...
+
+   Both are valid, and we use both.  Again, just do not mix styles in
+   the same part of the code and mimic existing styles in the
+   neighbourhood.
+
+ - When splitting a long logical line, some people change line before
+   a binary operator, so that the result looks like a parse tree when
+   you turn your head 90-degrees counterclockwise:
+
+        if (the_beginning_of_a_very_long_expression_that_has_to
+           || span_more_than_a_single_line_of_the_source_text) {
+
+   while other people prefer to leave the operator at the end of the
+   line:
+
+        if (the_beginning_of_a_very_long_expression_that_has_to ||
+           span_more_than_a_single_line_of_the_source_text) {
+
+   Both are valid, but we tend to use the latter more, unless the
+   expression gets fairly complex, in which case the former tends to
+   be easier to read.  Again, just do not mix styles in the same part
+   of the code and mimic existing styles in the neighbourhood.
+
+ - When splitting a long logical line, with everything else being
+   equal, it is preferable to split after the operator at higher
+   level in the parse tree.  That is, this is more preferable:
+
+       if (a_very_long_variable * that_is_used_in +
+           a_very_long_expression) {
+               ...
+
+   than
+
+       if (a_very_long_variable *
+           that_is_used_in + a_very_long_expression) {
+               ...
+
  - Some clever tricks, like using the !! operator with arithmetic
    constructs, can be extremely confusing to others.  Avoid them,
    unless there is a compelling reason to use them.
index fc6b2cf9ec0d2582b89d65ab6976275c4756bca5..cea0e7ae3db37dcd366e094abdd2a2f32514a247 100644 (file)
@@ -59,6 +59,7 @@ SP_ARTICLES += howto/recover-corrupted-blob-object
 SP_ARTICLES += howto/recover-corrupted-object-harder
 SP_ARTICLES += howto/rebuild-from-update-hook
 SP_ARTICLES += howto/rebase-from-internal-branch
+SP_ARTICLES += howto/keep-canonical-history-correct
 SP_ARTICLES += howto/maintain-git
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
index ad53d0deb63a6a45181e6966808ac6a1fdf01ed1..5974734bb4d5993fdbd9786272d4dd9824b3f96c 100644 (file)
@@ -1,18 +1,61 @@
 Git v2.1 Release Notes
 ======================
 
+Backward compatibility notes
+----------------------------
+
+ * The default value we give to the environment variable LESS has been
+   changed from "FRSX" to "FRX", losing "S" (chop long lines instead
+   of wrapping).  Existing users who prefer not to see line-wrapped
+   output may want to set
+
+     $ git config core.pager "less -S"
+
+   to restore the traditional behaviour.  It is expected that people
+   find output from the most subcommands easier to read with the new
+   default, except for "blame" which tends to produce really long
+   lines.  To override the new default only for "git blame", you can
+   do this:
+
+     $ git config pager.blame "less -S"
+
+ * A few disused directories in contrib/ have been retired.
+
+
 Updates since v2.0
 ------------------
 
 UI, Workflows & Features
 
+ * Since the very beginning of Git, we gave the LESS environment a
+   default value "FRSX" when we spawn "less" as the pager.  "S" (chop
+   long lines instead of wrapping) has been removed from this default
+   set of options, because it is more or less a personal taste thing,
+   as opposed to others that have good justifications (i.e. "R" is
+   very much justified because many kinds of output we produce are
+   colored and "FX" is justified because output we produce is often
+   shorter than a page).
+
+ * The logic and data used to compute the display width needed for
+   UTF-8 strings have been updated to match Unicode 6.3 better.
+
  * "git commit --date=<date>" option learned to read from more
    timestamp formats, including "--date=now".
 
+ * The `core.commentChar` configuration variable is used to specify a
+   custom comment character other than the default "#" to be used in
+   the commit log editor.  This can be set to `auto` to attempt to
+   choose a different character that does not conflict with what
+   already starts a line in the message being edited for cases like
+   "git commit --amend".
+
  * "git grep" learned grep.fullname configuration variable to force
    "--full-name" to be default.  This may cause regressions on
    scripted users that do not expect this new behaviour.
 
+ * "git imap-send" learned to ask the credential helper for auth
+   material.
+
  * "git merge" without argument, even when there is an upstream
    defined for the current branch, refused to run until
    merge.defaultToUpstream is set to true.  Flip the default of that
@@ -37,9 +80,20 @@ UI, Workflows & Features
    digit in the hour part, e.g. 2014-01-07T5:01:02.048176Z, emitted
    by some broken subversion server implementations.
 
+ * "git tag" when editing the tag message shows the name of the tag
+   being edited as a comment in the editor.
+
 
 Performance, Internal Implementation, etc.
 
+ * Build procedure for 'subtree' (in contrib/) has been cleaned up.
+
+ * The `core.deltabasecachelimit` used to default to 16 MiB , but this
+   proved to be too small, and has been bumped to 96 MiB.
+
+ * "git blame" has been optimized greatly by reorganising the data
+   structure that is used to keep track of the work to be done.
+
  * "git diff" that compares 3-or-more trees (e.g. parents and the
    result of a merge) have been optimized.
 
@@ -59,6 +113,22 @@ Unless otherwise noted, all the fixes since v2.0 in the maintenance
 track are contained in this release (see the maintenance releases'
 notes for details).
 
+ * We used to unconditionally disable the pager in the pager process
+   we spawn to feed out output, but that prevented people who want to
+   run "less" within "less" from doing so.
+   (merge c0459ca je/pager-do-not-recurse later to maint).
+
+ * Tools that read diagnostic output in our standard error stream do
+   not want to see terminal control sequence (e.g. erase-to-eol).
+   Detect them by checking if the standard error stream is connected
+   to a tty.
+   (merge 38de156 mn/sideband-no-ansi later to maint).
+
+ * Mishandling of patterns in .gitignore that has trailing SPs quoted
+   with backslashes (e.g. ones that end with "\ ") have been
+   corrected.
+   (merge e61a6c1 pb/trim-trailing-spaces later to maint).
+
  * "--ignore-space-change" option of "git apply" ignored the spaces
    at the beginning of line too aggressively, which is inconsistent
    with the option of the same name "diff" and "git diff" have.
@@ -69,18 +139,62 @@ notes for details).
    lines in its output.
    (merge dd75553 jx/blame-align-relative-time later to maint).
 
+ * "git blame" assigned the blame to the copy in the working-tree if
+   the repository is set to core.autocrlf=input and the file used CRLF
+   line endings.
+   (merge 4d4813a bc/blame-crlf-test later to maint).
+
+ * "git commit --allow-empty-messag -C $commit" did not work when the
+   commit did not have any log message.
+   (merge 076cbd6 jk/commit-C-pick-empty later to maint).
+
+ * "git grep -O" to show the lines that hit in the pager did not work
+   well with case insensitive search.  We now spawn "less" with its
+   "-I" option when it is used as the pager (which is the default).
+   (merge f7febbe sk/spawn-less-case-insensitively-from-grep-O-i later to maint).
+
  * We used to disable threaded "git index-pack" on platforms without
    thread-safe pread(); use a different workaround for such
    platforms to allow threaded "git index-pack".
    (merge 3953949 nd/index-pack-one-fd-per-thread later to maint).
 
+ * The error reporting from "git index-pack" has been improved to
+   distinguish missing objects from type errors.
+   (merge 77583e7 jk/index-pack-report-missing later to maint).
+
+ * "git mailinfo" used to read beyond the end of header string while
+   parsing an incoming e-mail message to extract the patch.
+   (merge b1a013d rs/mailinfo-header-cmp later to maint).
+
+ * On a case insensitive filesystem, merge-recursive incorrectly
+   deleted the file that is to be renamed to a name that is the same
+   except for case differences.
+   (merge baa37bf dt/merge-recursive-case-insensitive later to maint).
+
  * "git rerere forget" did not work well when merge.conflictstyle
    was set to a non-default value.
    (merge de3d8bb fc/rerere-conflict-style later to maint).
 
+ * "git log --exclude=<glob> --all | git shortlog" worked as expected,
+   but "git shortlog --exclude=<glob> --all", which is supposed to be
+   identical to the above pipeline, was not accepted at the command
+   line argument parser level.
+   (merge eb07774 jc/shortlog-ref-exclude later to maint).
+
+ * "git show -s" (i.e. show log message only) used to incorrectly emit
+   an extra blank line after a merge commit.
+   (merge ad2f725 mk/show-s-no-extra-blank-line-for-merges later to maint).
+
  * "git status", even though it is a read-only operation, tries to
    update the index with refreshed lstat(2) info to optimize future
    accesses to the working tree opportunistically, but this could
    race with a "read-write" operation that modify the index while it
    is running.  Detect such a race and avoid overwriting the index.
    (merge 426ddee ym/fix-opportunistic-index-update-race later to maint).
+
+ * "git update-index --cacheinfo" in 2.0 release crashed on a
+   malformed command line.
+   (merge c8e1ee4 jc/rev-parse-argh-dashed-multi-words later to maint).
+
+ * The mode to run tests with HTTP server tests disabled was broken.
+   (merge afa53fe na/no-http-test-in-the-middle later to maint).
index 1932e9b9a2be5437dd467f91472318a5bf1bb992..9f467d3820a40d1281d29707e98fa16c2bbde63f 100644 (file)
@@ -381,7 +381,7 @@ false), while all other repositories are assumed to be bare (bare
 core.worktree::
        Set the path to the root of the working tree.
        This can be overridden by the GIT_WORK_TREE environment
-       variable and the '--work-tree' command line option.
+       variable and the '--work-tree' command-line option.
        The value can be an absolute path or relative to the path to
        the .git directory, which is either specified by --git-dir
        or GIT_DIR, or automatically discovered.
@@ -489,7 +489,7 @@ core.deltaBaseCacheLimit::
        to avoid unpacking and decompressing frequently used base
        objects multiple times.
 +
-Default is 16 MiB on all platforms.  This should be reasonable
+Default is 96 MiB on all platforms.  This should be reasonable
 for all users/operating systems, except on the largest projects.
 You probably do not need to adjust this value.
 +
@@ -523,7 +523,7 @@ core.askpass::
        environment variable. If not set, fall back to the value of the
        'SSH_ASKPASS' environment variable or, failing that, a simple password
        prompt. The external program shall be given a suitable prompt as
-       command line argument and write the password on its STDOUT.
+       command-line argument and write the password on its STDOUT.
 
 core.attributesfile::
        In addition to '.gitattributes' (per-directory) and
@@ -544,6 +544,9 @@ core.commentchar::
        messages consider a line that begins with this character
        commented, and removes them after the editor returns
        (default '#').
++
+If set to "auto", `git-commit` would select a character that is not
+the beginning character of any line in existing commit messages.
 
 sequence.editor::
        Text editor used by `git rebase -i` for editing the rebase instruction file.
@@ -558,14 +561,19 @@ core.pager::
        configuration, then `$PAGER`, and then the default chosen at
        compile time (usually 'less').
 +
-When the `LESS` environment variable is unset, Git sets it to `FRSX`
+When the `LESS` environment variable is unset, Git sets it to `FRX`
 (if `LESS` environment variable is set, Git does not change it at
 all).  If you want to selectively override Git's default setting
-for `LESS`, you can set `core.pager` to e.g. `less -+S`.  This will
+for `LESS`, you can set `core.pager` to e.g. `less -S`.  This will
 be passed to the shell by Git, which will translate the final
-command to `LESS=FRSX less -+S`. The environment tells the command
-to set the `S` option to chop long lines but the command line
-resets it to the default to fold long lines.
+command to `LESS=FRX less -S`. The environment does not set the
+`S` option but the command line does, instructing less to truncate
+long lines. Similarly, setting `core.pager` to `less -+F` will
+deactivate the `F` option specified by the environment from the
+command-line, deactivating the "quit if one screen" behavior of
+`less`.  One can specifically activate some flags for particular
+commands: for example, setting `pager.blame` to `less -S` enables
+line truncation only for `git blame`.
 +
 Likewise, when the `LV` environment variable is unset, Git sets it
 to `-c`.  You can override this setting by exporting `LV` with
@@ -613,9 +621,9 @@ core.preloadindex::
 +
 This can speed up operations like 'git diff' and 'git status' especially
 on filesystems like NFS that have weak caching semantics and thus
-relatively high IO latencies.  With this set to 'true', Git will do the
+relatively high IO latencies.  When enabled, Git will do the
 index comparison to the filesystem data in parallel, allowing
-overlapping IO's.
+overlapping IO's.  Defaults to true.
 
 core.createObject::
        You can set this to 'link', in which case a hardlink followed by
@@ -1114,6 +1122,10 @@ format.signature::
        Set this variable to the empty string ("") to suppress
        signature generation.
 
+format.signaturefile::
+       Works just like format.signature except the contents of the
+       file specified by this variable will be used as the signature.
+
 format.suffix::
        The default for format-patch is to output files with the suffix
        `.patch`. Use this variable to change that suffix (make sure to
@@ -1324,7 +1336,7 @@ grep.extendedRegexp::
 gpg.program::
        Use this custom program instead of "gpg" found on $PATH when
        making or verifying a PGP signature. The program must support the
-       same command line interface as GPG, namely, to verify a detached
+       same command-line interface as GPG, namely, to verify a detached
        signature, "gpg --verify $file - <$signature" is run, and the
        program is expected to signal a good signature by exiting with
        code 0, and to generate an ascii-armored detached signature, the
@@ -2293,9 +2305,11 @@ status.submodulesummary::
        --summary-limit option of linkgit:git-submodule[1]). Please note
        that the summary output command will be suppressed for all
        submodules when `diff.ignoreSubmodules` is set to 'all' or only
-       for those submodules where `submodule.<name>.ignore=all`. To
+       for those submodules where `submodule.<name>.ignore=all`. The only
+       exception to that rule is that status and commit will show staged
+       submodule changes. To
        also view the summary for ignored submodules you can either use
-       the --ignore-submodules=dirty command line option or the 'git
+       the --ignore-submodules=dirty command-line option or the 'git
        submodule summary' command, which shows a similar output but does
        not honor these settings.
 
@@ -2317,14 +2331,16 @@ submodule.<name>.branch::
 submodule.<name>.fetchRecurseSubmodules::
        This option can be used to control recursive fetching of this
        submodule. It can be overridden by using the --[no-]recurse-submodules
-       command line option to "git fetch" and "git pull".
+       command-line option to "git fetch" and "git pull".
        This setting will override that from in the linkgit:gitmodules[5]
        file.
 
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
-       modified, "dirty" will ignore all changes to the submodules work tree and
+       modified (but it will nonetheless show up in the output of status and
+       commit when it has been staged), "dirty" will ignore all changes
+       to the submodules work tree and
        takes only differences between the HEAD of the submodule and the commit
        recorded in the superproject into account. "untracked" will additionally
        let submodules with modified tracked files in their work tree show up.
index f07b4513ed76978e3bb7ef02ebf31d504f7098ff..b0017795208d971eb40001ff20bd2516c8d9a58e 100644 (file)
@@ -76,7 +76,7 @@ diff.ignoreSubmodules::
        this setting when reporting uncommitted changes. Setting it to
        'all' disables the submodule summary normally shown by 'git commit'
        and 'git status' when 'status.submodulesummary' is set unless it is
-       overridden by using the --ignore-submodules command line option.
+       overridden by using the --ignore-submodules command-line option.
        The 'git submodule' commands are not affected by this setting.
 
 diff.mnemonicprefix::
index f986c5cb3a09989aebefcd9ba28f419280ffc544..4cb52a7302077e17d5a05983a5e6c924b4694194 100644 (file)
@@ -117,7 +117,7 @@ $ git bisect visualize
 `view` may also be used as a synonym for `visualize`.
 
 If the 'DISPLAY' environment variable is not set, 'git log' is used
-instead.  You can also give command line options such as `-p` and
+instead.  You can also give command-line options such as `-p` and
 `--stat`.
 
 ------------
index e9917b89a91b36c87892ea1c08522f17be3c0d89..9dfa1a5ce265eacd64febc3775cb05525b58d7f0 100644 (file)
@@ -256,7 +256,7 @@ 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*.
 
-You can override these rules either by command line options or by environment
+You can override these rules either by command-line options or by environment
 variables. The '--global' and the '--system' options will limit the file used
 to the global or system-wide file respectively. The GIT_CONFIG environment
 variable has a similar effect, but you can specify any filename you want.
index 223f7315236be845a158f63d5909804f965837ba..a69b3616ec3bdc9686114f4d0b7a851c0ce93339 100644 (file)
@@ -169,7 +169,7 @@ Git configuration files in that directory are readable by `<user>`.
 --forbid-override=<service>::
        Allow/forbid overriding the site-wide default with per
        repository configuration.  By default, all the services
-       are overridable.
+       may be overridden.
 
 --[no-]informative-errors::
        When informative errors are turned on, git-daemon will report
@@ -184,7 +184,7 @@ Git configuration files in that directory are readable by `<user>`.
        Every time a client connects, first run an external command
        specified by the <path> with service name (e.g. "upload-pack"),
        path to the repository, hostname (%H), canonical hostname
-       (%CH), ip address (%IP), and tcp port (%P) as its command line
+       (%CH), IP address (%IP), and TCP port (%P) as its command-line
        arguments. The external command can decide to decline the
        service by exiting with a non-zero status (or to allow it by
        exiting with a zero status).  It can also look at the $REMOTE_ADDR
@@ -204,7 +204,7 @@ SERVICES
 --------
 
 These services can be globally enabled/disabled using the
-command line options of this command.  If a finer-grained
+command-line options of this command.  If finer-grained
 control is desired (e.g. to allow 'git archive' to be run
 against only in a few selected repositories the daemon serves),
 the per-repository configuration file can be used to enable or
index 85f1f30fdf5c0a741c3c575bfa393fc7d88d308f..221506b04ba55fac2151d8ac95da7df57eb9d411 100644 (file)
@@ -105,6 +105,10 @@ marks the same across runs.
        in the commit (as opposed to just listing the files which are
        different from the commit's first parent).
 
+--refspec::
+       Apply the specified refspec to each ref exported. Multiple of them can
+       be specified.
+
 [<git-rev-list-args>...]::
        A list of arguments, acceptable to 'git rev-parse' and
        'git rev-list', that specifies the specific objects and references
index fd22a9a0c1312382a972a39e06063e7ff77b027d..377eeaa36db2407f76a9cd21777bb2eda4ff74bc 100644 (file)
@@ -231,7 +231,7 @@ Date Formats
 ~~~~~~~~~~~~
 The following date formats are supported.  A frontend should select
 the format it will use for this import by passing the format name
-in the \--date-format=<fmt> command line option.
+in the \--date-format=<fmt> command-line option.
 
 `raw`::
        This is the Git native format and is `<time> SP <offutc>`.
@@ -348,7 +348,7 @@ and control the current import process.  More detailed discussion
 `done`::
        Marks the end of the stream. This command is optional
        unless the `done` feature was requested using the
-       `--done` command line option or `feature done` command.
+       `--done` command-line option or `feature done` command.
 
 `cat-blob`::
        Causes fast-import to print a blob in 'cat-file --batch'
@@ -437,7 +437,7 @@ the email address from the other fields in the line.  Note that
 of bytes, except `LT`, `GT` and `LF`.  `<name>` is typically UTF-8 encoded.
 
 The time of the change is specified by `<when>` using the date format
-that was selected by the \--date-format=<fmt> command line option.
+that was selected by the \--date-format=<fmt> command-line option.
 See ``Date Formats'' above for the set of supported formats, and
 their syntax.
 
@@ -483,6 +483,9 @@ Marks must be declared (via `mark`) before they can be used.
 * Any valid Git SHA-1 expression that resolves to a commit.  See
   ``SPECIFYING REVISIONS'' in linkgit:gitrevisions[7] for details.
 
+* The special null SHA-1 (40 zeros) specifies that the branch is to be
+  removed.
+
 The special case of restarting an incremental import from the
 current branch value should be written as:
 ----
@@ -1085,7 +1088,7 @@ Option commands must be the first commands on the input (not counting
 feature commands), to give an option command after any non-option
 command is an error.
 
-The following commandline options change import semantics and may therefore
+The following command-line options change import semantics and may therefore
 not be passed as option:
 
 * date-format
@@ -1099,7 +1102,7 @@ not be passed as option:
 If the `done` feature is not in use, treated as if EOF was read.
 This can be used to tell fast-import to finish early.
 
-If the `--done` command line option or `feature done` command is
+If the `--done` command-line option or `feature done` command is
 in use, the `done` command is mandatory and marks the end of the
 stream.
 
index 5c0a4ab2d6d4c123899fa32e8f147a8c077f2130..c0fd470da42991404c722f45ee7ac48cd774287b 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
                   [(--attach|--inline)[=<boundary>] | --no-attach]
                   [-s | --signoff]
                   [--signature=<signature> | --no-signature]
+                  [--signature-file=<file>]
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
@@ -233,6 +234,9 @@ configuration options in linkgit:git-notes[1] to use this workflow).
        signature option is omitted the signature defaults to the Git version
        number.
 
+--signature-file=<file>::
+       Works just like --signature except the signature is read from a file.
+
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
        filenames, use specified suffix.  A common alternative is
index b21e9d79be22cf134cf4e5f009fc8d71a635cc73..3956525218bf45f2968fe2039e4dfb75a6f2222b 100644 (file)
@@ -80,9 +80,9 @@ CONFIGURATION VARIABLES
 help.format
 ~~~~~~~~~~~
 
-If no command line option is passed, the 'help.format' configuration
+If no command-line option is passed, the 'help.format' configuration
 variable will be checked. The following values are supported for this
-variable; they make 'git help' behave as their corresponding command
+variable; they make 'git help' behave as their corresponding command-
 line option:
 
 * "man" corresponds to '-m|--man',
@@ -93,15 +93,15 @@ help.browser, web.browser and browser.<tool>.path
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The 'help.browser', 'web.browser' and 'browser.<tool>.path' will also
-be checked if the 'web' format is chosen (either by command line
+be checked if the 'web' format is chosen (either by command-line
 option or configuration variable). See '-w|--web' in the OPTIONS
 section above and linkgit:git-web{litdd}browse[1].
 
 man.viewer
 ~~~~~~~~~~
 
-The 'man.viewer' config variable will be checked if the 'man' format
-is chosen. The following values are currently supported:
+The 'man.viewer' configuration variable will be checked if the 'man'
+format is chosen. The following values are currently supported:
 
 * "man": use the 'man' program as usual,
 * "woman": use 'emacsclient' to launch the "woman" mode in emacs
@@ -124,7 +124,7 @@ For example, this configuration:
                viewer = woman
 ------------------------------------------------
 
-will try to use konqueror first. But this may fail (for example if
+will try to use konqueror first. But this may fail (for example, if
 DISPLAY is not set) and in that case emacs' woman mode will be tried.
 
 If everything fails, or if no viewer is configured, the viewer specified
index c0856a6e0a0a625fcc4c394057597d633a0487ac..e26f01fb1d1aec3526879059fe6ee20438965f68 100644 (file)
@@ -185,15 +185,15 @@ specifies the format of exclude patterns.
 
 These exclude patterns come from these places, in order:
 
-  1. The command line flag --exclude=<pattern> specifies a
+  1. The command-line flag --exclude=<pattern> specifies a
      single pattern.  Patterns are ordered in the same order
      they appear in the command line.
 
-  2. The command line flag --exclude-from=<file> specifies a
+  2. The command-line flag --exclude-from=<file> specifies a
      file containing a list of patterns.  Patterns are ordered
      in the same order they appear in the file.
 
-  3. The command line flag --exclude-per-directory=<name> specifies
+  3. The command-line flag --exclude-per-directory=<name> specifies
      a name of the file in each directory 'git ls-files'
      examines, normally `.gitignore`.  Files in deeper
      directories take precedence.  Patterns are ordered in the
index 312c3b1fe5f970a5fdab0e7e1ba0fcbe021eaaa5..31efc587ee694da6885ed1ce9d1a4c1419808397 100644 (file)
@@ -8,14 +8,14 @@ git-patch-id - Compute unique ID for a patch
 SYNOPSIS
 --------
 [verse]
-'git patch-id' < <patch>
+'git patch-id' [--stable | --unstable] < <patch>
 
 DESCRIPTION
 -----------
-A "patch ID" is nothing but a SHA-1 of the diff associated with a patch, with
-whitespace and line numbers ignored.  As such, it's "reasonably stable", but at
-the same time also reasonably unique, i.e., two patches that have the same "patch
-ID" are almost guaranteed to be the same thing.
+A "patch ID" is nothing but a sum of SHA-1 of the file diffs associated with a
+patch, with whitespace and line numbers ignored.  As such, it's "reasonably
+stable", but at the same time also reasonably unique, i.e., two patches that
+have the same "patch ID" are almost guaranteed to be the same thing.
 
 IOW, you can use this thing to look for likely duplicate commits.
 
@@ -27,6 +27,33 @@ This can be used to make a mapping from patch ID to commit ID.
 
 OPTIONS
 -------
+
+--stable::
+       Use a "stable" sum of hashes as the patch ID. With this option:
+        - Reordering file diffs that make up a patch does not affect the ID.
+          In particular, two patches produced by comparing the same two trees
+          with two different settings for "-O<orderfile>" result in the same
+          patch ID signature, thereby allowing the computed result to be used
+          as a key to index some meta-information about the change between
+          the two trees;
+
+        - Result is different from the value produced by git 1.9 and older
+          or produced when an "unstable" hash (see --unstable below) is
+          configured - even when used on a diff output taken without any use
+          of "-O<orderfile>", thereby making existing databases storing such
+          "unstable" or historical patch-ids unusable.
+
+       This is the default if patchid.stable is set to true.
+
+--unstable::
+       Use an "unstable" hash as the patch ID. With this option,
+       the result produced is compatible with the patch-id value produced
+       by git 1.9 and older.  Users with pre-existing databases storing
+       patch-ids produced by git 1.9 and older (who do not deal with reordered
+       patches) may want to use this option.
+
+       This is the default.
+
 <patch>::
        The diff to create the ID of.
 
index 056c0dba81292ae63fedf2d911bce8f5fa3f7d19..fa1d557e5b98eb6dc40e3baa65964138b4cc0621 100644 (file)
@@ -283,7 +283,7 @@ merge. The different stages represent the "result tree" (stage 0, aka
 you are trying to merge (stage 2 and 3 respectively).
 
 The order of stages 1, 2 and 3 (hence the order of three
-<tree-ish> command line arguments) are significant when you
+<tree-ish> command-line arguments) are significant when you
 start a 3-way merge with an index file that is already
 populated.  Here is an outline of how the algorithm works:
 
index 0a02f70657776bc5d862add34f466b4fe98f6e35..61461b9f3399bd99547407ae0f3ccb4a38b77582 100644 (file)
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git replace' [-f] <object> <replacement>
+'git replace' [-f] --edit <object>
 'git replace' -d <object>...
 'git replace' [--format=<format>] [-l [<pattern>]]
 
@@ -63,6 +64,15 @@ OPTIONS
 --delete::
        Delete existing replace refs for the given objects.
 
+--edit <object>::
+       Edit an object's content interactively. The existing content
+       for <object> is pretty-printed into a temporary file, an
+       editor is launched on the file, and the result is parsed to
+       create a new object of the same type as <object>. A
+       replacement ref is then created to replace <object> with the
+       newly created object. See linkgit:git-var[1] for details about
+       how the editor will be chosen.
+
 -l <pattern>::
 --list <pattern>::
        List replace refs for objects that match the given pattern (or
@@ -92,7 +102,9 @@ CREATING REPLACEMENT OBJECTS
 
 linkgit:git-filter-branch[1], linkgit:git-hash-object[1] and
 linkgit:git-rebase[1], among other git commands, can be used to create
-replacement objects from existing objects.
+replacement objects from existing objects. The `--edit` option can
+also be used with 'git replace' to create a replacement object by
+editing an existing object.
 
 If you want to replace many blobs, trees or commits that are part of a
 string of commits, you may just want to create a replacement string of
@@ -117,6 +129,8 @@ linkgit:git-filter-branch[1]
 linkgit:git-rebase[1]
 linkgit:git-tag[1]
 linkgit:git-branch[1]
+linkgit:git-commit[1]
+linkgit:git-var[1]
 linkgit:git[1]
 
 GIT
index f0e57a597b6e551d891c064fa69837db632237c6..d0fa18aaa8b14f9171800a05742fb1fe326b547b 100644 (file)
@@ -20,7 +20,7 @@ files in the directory), or directly as a revision list.  In the
 last case, any format accepted by linkgit:git-format-patch[1] can
 be passed to git send-email.
 
-The header of the email is configurable by command line options.  If not
+The header of the email is configurable via command-line options.  If not
 specified on the command line, the user will be prompted with a ReadLine
 enabled interface to provide the necessary information.
 
@@ -68,7 +68,7 @@ The --cc option must be repeated for each user you want on the cc list.
 When '--compose' is used, git send-email will use the From, Subject, and
 In-Reply-To headers specified in the message. If the body of the message
 (what you type after the headers and a blank line) only contains blank
-(or Git: prefixed) lines the summary won't be sent, but From, Subject,
+(or Git: prefixed) lines, the summary won't be sent, but From, Subject,
 and In-Reply-To headers will be used unless they are removed.
 +
 Missing From or In-Reply-To headers will be prompted for.
@@ -78,7 +78,7 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
 --from=<address>::
        Specify the sender of the emails.  If not specified on the command line,
        the value of the 'sendemail.from' configuration option is used.  If
-       neither the command line option nor 'sendemail.from' are set, then the
+       neither the command-line option nor 'sendemail.from' are set, then the
        user will be prompted for the value.  The default for the prompt will be
        the value of GIT_AUTHOR_IDENT, or GIT_COMMITTER_IDENT if that is not
        set, as returned by "git var -l".
index fce585388c773f89bec19fdfb639f92651e62450..44c970ce18143e2a45c9f5364535e26508b50317 100644 (file)
@@ -148,8 +148,8 @@ the same local time zone.
 [verse]
 config key: svn-remote.<name>.ignore-paths
 +
-If the ignore-paths config key is set and the command line option is
-also given, both regular expressions will be used.
+If the ignore-paths configuration key is set, and the command-line
+option is also given, both regular expressions will be used.
 +
 Examples:
 +
index 2de575f5beed10ed616289d88929c466a0fcb296..16ede5b4c36d3a231d6af3849f7eeb8f404daf98 100644 (file)
@@ -62,7 +62,7 @@ CONF.VAR (from -c option) and web.browser
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The web browser can be specified using a configuration variable passed
-with the -c (or --config) command line option, or the 'web.browser'
+with the -c (or --config) command-line option, or the 'web.browser'
 configuration variable if the former is not used.
 
 browser.<tool>.path
@@ -87,7 +87,7 @@ the URLs passed as arguments.
 Note about konqueror
 --------------------
 
-When 'konqueror' is specified by a command line option or a
+When 'konqueror' is specified by a command-line option or a
 configuration variable, we launch 'kfmclient' to try to open the HTML
 man page on an already opened konqueror in a new tab if possible.
 
index b075e0bed55c9cfd84b130ef6e93080ef3207517..3bd68b016791ac082e2a556a4835b22ba241b92f 100644 (file)
@@ -29,7 +29,7 @@ in-depth introduction.
 After you mastered the basic concepts, you can come back to this
 page to learn what commands Git offers.  You can learn more about
 individual Git commands with "git help command".  linkgit:gitcli[7]
-manual page gives you an overview of the command line command syntax.
+manual page gives you an overview of the command-line command syntax.
 
 Formatted and hyperlinked version of the latest Git documentation
 can be viewed at `http://git-htmldocs.googlecode.com/git/git.html`.
@@ -39,7 +39,7 @@ ifdef::stalenotes[]
 ============
 
 You are reading the documentation for the latest (possibly
-unreleased) version of Git, that is available from 'master'
+unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
@@ -755,7 +755,7 @@ Git so take care if using Cogito etc.
 
 'GIT_WORK_TREE'::
        Set the path to the root of the working tree.
-       This can also be controlled by the '--work-tree' command line
+       This can also be controlled by the '--work-tree' command-line
        option and the core.worktree configuration variable.
 
 'GIT_NAMESPACE'::
@@ -880,7 +880,7 @@ for further details.
 'GIT_ASKPASS'::
        If this environment variable is set, then Git commands which need to
        acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
-       will call this program with a suitable prompt as command line argument
+       will call this program with a suitable prompt as command-line argument
        and read the password from its STDOUT. See also the 'core.askpass'
        option in linkgit:git-config[1].
 
index 1c3e109cb3d30e69121f9b6681e870a66cf04609..dfe7d8372723c7a4970b8a7100764d1becc7f830 100644 (file)
@@ -3,7 +3,7 @@ gitcli(7)
 
 NAME
 ----
-gitcli - Git command line interface and conventions
+gitcli - Git command-line interface and conventions
 
 SYNOPSIS
 --------
@@ -66,13 +66,13 @@ you will.
 Here are the rules regarding the "flags" that you should follow when you are
 scripting Git:
 
- * it's preferred to use the non dashed form of Git commands, which means that
+ * it's preferred to use the non-dashed form of Git commands, which means that
    you should prefer `git foo` to `git-foo`.
 
  * splitting short options to separate words (prefer `git foo -a -b`
    to `git foo -ab`, the latter may not even work).
 
- * when a command line option takes an argument, use the 'stuck' form.  In
+ * when a command-line option takes an argument, use the 'stuck' form.  In
    other words, write `git foo -oArg` instead of `git foo -o Arg` for short
    options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg`
    for long options.  An option that takes optional option-argument must be
@@ -103,7 +103,7 @@ Here is a list of the facilities provided by this option parser.
 Magic Options
 ~~~~~~~~~~~~~
 Commands which have the enhanced option parser activated all understand a
-couple of magic command line options:
+couple of magic command-line options:
 
 -h::
        gives a pretty printed usage of the command.
index 7e03fcc62d1a388552cdd1e92f75183c0278cf28..7ae50aa26aff26cb9dd619abc7c29372c4156bcc 100644 (file)
@@ -27,7 +27,7 @@ gitk-specific options.
 
 gitk generally only understands options with arguments in the
 'sticked' form (see linkgit:gitcli[7]) due to limitations in the
-command line parser.
+command-line parser.
 
 rev-list options and arguments
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 347a9f76ee809c3691dc6580b0fba3ebde0b0e3c..f6c0dfd0290a9b2174c645630998d2a62cdb9f5e 100644 (file)
@@ -67,7 +67,9 @@ submodule.<name>.fetchRecurseSubmodules::
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
-       modified, "dirty" will ignore all changes to the submodules work tree and
+       modified (but will nonetheless show up in the output of status and
+       commit when it has been staged), "dirty" will ignore all changes
+       to the submodules work tree and
        takes only differences between the HEAD of the submodule and the commit
        recorded in the superproject into account. "untracked" will additionally
        let submodules with modified tracked files in their work tree show up.
index 952f503afb6dd8e65ffeaf09425a0968593f39b5..ebe7a6c24cfef75441906a3e4de550b9fd296c0b 100644 (file)
@@ -904,7 +904,7 @@ the following in your GITWEB_CONFIG file:
        $feature{'snapshot'}{'override'} = 1;
 
 If you allow overriding for the snapshot feature, you can specify which
-snapshot formats are globally disabled. You can also add any command line
+snapshot formats are globally disabled. You can also add any command-line
 options you want (such as setting the compression level). For instance, you
 can disable Zip compressed snapshots and set *gzip*(1) to run at level 6 by
 adding the following lines to your gitweb configuration file:
index be0858c188184eb510688ffc0598664541c1d10c..4e0b971824f698841a6395588c2b53b0e1cf988d 100644 (file)
@@ -1,7 +1,7 @@
 [[def_alternate_object_database]]alternate object database::
        Via the alternates mechanism, a <<def_repository,repository>>
        can inherit part of its <<def_object_database,object database>>
-       from another object database, which is called "alternate".
+       from another object database, which is called an "alternate".
 
 [[def_bare_repository]]bare repository::
        A bare repository is normally an appropriately
diff --git a/Documentation/howto/keep-canonical-history-correct.txt b/Documentation/howto/keep-canonical-history-correct.txt
new file mode 100644 (file)
index 0000000..35d48ef
--- /dev/null
@@ -0,0 +1,216 @@
+From: Junio C Hamano <gitster@pobox.com>
+Date: Wed, 07 May 2014 13:15:39 -0700
+Subject: Beginner question on "Pull is mostly evil"
+Abstract: This how-to explains a method for keeping a
+ project's history correct when using git pull.
+Content-type: text/asciidoc
+
+Keep authoritative canonical history correct with git pull
+==========================================================
+
+Sometimes a new project integrator will end up with project history
+that appears to be "backwards" from what other project developers
+expect. This howto presents a suggested integration workflow for
+maintaining a central repository.
+
+Suppose that that central repository has this history:
+
+------------
+    ---o---o---A
+------------
+
+which ends at commit `A` (time flows from left to right and each node
+in the graph is a commit, lines between them indicating parent-child
+relationship).
+
+Then you clone it and work on your own commits, which leads you to
+have this history in *your* repository:
+
+------------
+    ---o---o---A---B---C
+------------
+
+Imagine your coworker did the same and built on top of `A` in *his*
+repository in the meantime, and then pushed it to the
+central repository:
+
+------------
+    ---o---o---A---X---Y---Z
+------------
+
+Now, if you `git push` at this point, because your history that leads
+to `C` lacks `X`, `Y` and `Z`, it will fail.  You need to somehow make
+the tip of your history a descendant of `Z`.
+
+One suggested way to solve the problem is "fetch and then merge", aka
+`git pull`. When you fetch, your repository will have a history like
+this:
+
+------------
+    ---o---o---A---B---C
+               \
+                X---Y---Z
+------------
+
+Once you run merge after that, while still on *your* branch, i.e. `C`,
+you will create a merge `M` and make the history look like this:
+
+------------
+    ---o---o---A---B---C---M
+               \         /
+                X---Y---Z
+------------
+
+`M` is a descendant of `Z`, so you can push to update the central
+repository.  Such a merge `M` does not lose any commit in both
+histories, so in that sense it may not be wrong, but when people want
+to talk about "the authoritative canonical history that is shared
+among the project participants", i.e. "the trunk", they often view
+it as "commits you see by following the first-parent chain", and use
+this command to view it:
+
+------------
+    $ git log --first-parent
+------------
+
+For all other people who observed the central repository after your
+coworker pushed `Z` but before you pushed `M`, the commit on the trunk
+used to be `o-o-A-X-Y-Z`.  But because you made `M` while you were on
+`C`, `M`'s first parent is `C`, so by pushing `M` to advance the
+central repository, you made `X-Y-Z` a side branch, not on the trunk.
+
+You would rather want to have a history of this shape:
+
+------------
+    ---o---o---A---X---Y---Z---M'
+               \             /
+                B-----------C
+------------
+
+so that in the first-parent chain, it is clear that the project first
+did `X` and then `Y` and then `Z` and merged a change that consists of
+two commits `B` and `C` that achieves a single goal.  You may have
+worked on fixing the bug #12345 with these two patches, and the merge
+`M'` with swapped parents can say in its log message "Merge
+fix-bug-12345". Having a way to tell `git pull` to create a merge
+but record the parents in reverse order may be a way to do so.
+
+Note that I said "achieves a single goal" above, because this is
+important.  "Swapping the merge order" only covers a special case
+where the project does not care too much about having unrelated
+things done on a single merge but cares a lot about first-parent
+chain.
+
+There are multiple schools of thought about the "trunk" management.
+
+ 1. Some projects want to keep a completely linear history without any
+    merges.  Obviously, swapping the merge order would not match their
+    taste.  You would need to flatten your history on top of the
+    updated upstream to result in a history of this shape instead:
++
+------------
+    ---o---o---A---X---Y---Z---B---C
+------------
++
+with `git pull --rebase` or something.
+
+ 2. Some projects tolerate merges in their history, but do not worry
+    too much about the first-parent order, and allow fast-forward
+    merges.  To them, swapping the merge order does not hurt, but
+    it is unnecessary.
+
+ 3. Some projects want each commit on the "trunk" to do one single
+    thing.  The output of `git log --first-parent` in such a project
+    would show either a merge of a side branch that completes a single
+    theme, or a single commit that completes a single theme by itself.
+    If your two commits `B` and `C` (or they may even be two groups of
+    commits) were solving two independent issues, then the merge `M'`
+    we made in the earlier example by swapping the merge order is
+    still not up to the project standard.  It merges two unrelated
+    efforts `B` and `C` at the same time.
+
+For projects in the last category (Git itself is one of them),
+individual developers would want to prepare a history more like
+this:
+
+------------
+                C0--C1--C2     topic-c
+               /
+    ---o---o---A                master
+               \
+                B0--B1--B2     topic-b
+------------
+
+That is, keeping separate topics on separate branches, perhaps like
+so:
+
+------------
+    $ git clone $URL work && cd work
+    $ git checkout -b topic-b master
+    $ ... work to create B0, B1 and B2 to complete one theme
+    $ git checkout -b topic-c master
+    $ ... same for the theme of topic-c
+------------
+
+And then
+
+------------
+    $ git checkout master
+    $ git pull --ff-only
+------------
+
+would grab `X`, `Y` and `Z` from the upstream and advance your master
+branch:
+
+------------
+                C0--C1--C2     topic-c
+               /
+    ---o---o---A---X---Y---Z    master
+               \
+                B0--B1--B2     topic-b
+------------
+
+And then you would merge these two branches separately:
+
+------------
+    $ git merge topic-b
+    $ git merge topic-c
+------------
+
+to result in
+
+------------
+                C0--C1---------C2
+               /                 \
+    ---o---o---A---X---Y---Z---M---N
+               \             /
+                B0--B1-----B2
+------------
+
+and push it back to the central repository.
+
+It is very much possible that while you are merging topic-b and
+topic-c, somebody again advanced the history in the central repository
+to put `W` on top of `Z`, and make your `git push` fail.
+
+In such a case, you would rewind to discard `M` and `N`, update the
+tip of your 'master' again and redo the two merges:
+
+------------
+    $ git reset --hard origin/master
+    $ git pull --ff-only
+    $ git merge topic-b
+    $ git merge topic-c
+------------
+
+The procedure will result in a history that looks like this:
+
+------------
+                C0--C1--------------C2
+               /                     \
+    ---o---o---A---X---Y---Z---W---M'--N'
+               \                 /
+                B0--B1---------B2
+------------
+
+See also http://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
index 6de4f3c4875e3d9607754d5ca76a17e00c8f5841..f44e5e9458592a0554e4e2e163a6b029c1435f80 100644 (file)
@@ -181,7 +181,7 @@ On Debian:
 
    Most tests should pass.
 
-A command line tool to test WebDAV is cadaver. If you prefer GUIs, for
+A command-line tool to test WebDAV is cadaver. If you prefer GUIs, for
 example, konqueror can open WebDAV URLs as "webdav://..." or
 "webdavs://...".
 
index 5a286d0d61ffc5b3daa00d7d550d5931a91b35ec..07961185fe0cddf78822aee75b43f206cbcd4579 100644 (file)
@@ -94,7 +94,9 @@ some output processing may assume ref names in UTF-8.
 '<branchname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
   The suffix '@\{upstream\}' to a branchname (short form '<branchname>@\{u\}')
   refers to the branch that the branch specified by branchname is set to build on
-  top of.  A missing branchname defaults to the current one.
+  top of (configured with `branch.<name>.remote` and
+  `branch.<name>.merge`).  A missing branchname defaults to the
+  current one.
 
 '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
index a6b7d83a8eb05086cb88a9168117aa54cf018551..1a797812fb426189b03a814498dd7019c96607b4 100644 (file)
@@ -53,11 +53,3 @@ Functions
 `argv_array_clear`::
        Free all memory associated with the array and return it to the
        initial, empty state.
-
-`argv_array_detach`::
-       Detach the argv array from the `struct argv_array`, transferring
-       ownership of the allocated array and strings.
-
-`argv_array_free_detached`::
-       Free the memory allocated by a `struct argv_array` that was later
-       detached and is now no longer needed.
index 5d7d7f2d32f58f8682ca5efb2fd98fe7e74247dd..69510ae57afcedbe13334160c6a26c52c42e8d54 100644 (file)
@@ -109,6 +109,13 @@ terminated), of which .argv[0] is the program name to run (usually
 without a path). If the command to run is a git command, set argv[0] to
 the command name without the 'git-' prefix and set .git_cmd = 1.
 
+Note that the ownership of the memory pointed to by .argv stays with the
+caller, but it should survive until `finish_command` completes. If the
+.argv member is NULL, `start_command` will point it at the .args
+`argv_array` (so you may use one or the other, but you must use exactly
+one). The memory in .args will be cleaned up automatically during
+`finish_command` (or during `start_command` when it is unsuccessful).
+
 The members .in, .out, .err are used to redirect stdin, stdout,
 stderr as follows:
 
index 4396be9dda07dfad679f834120b7f1aa2adb16c5..077a7096a4ba3c47a0cbc8981abdde1b7b35d924 100644 (file)
@@ -134,6 +134,15 @@ Functions
 
        Strip whitespace from the beginning of a string.
 
+`strbuf_reencode`::
+
+       Replace the contents of the strbuf with a reencoded form.  Returns -1
+       on error, 0 on success.
+
+`strbuf_tolower`::
+
+       Lowercase each character in the buffer using `tolower`.
+
 `strbuf_cmp`::
 
        Compare two buffers. Returns an integer less than, equal to, or greater
index 20525d98bb67cd3710c9738f99020c01f2d1d885..59be59b0ebba8c24875643521c9135ab907a33ce 100644 (file)
@@ -374,7 +374,7 @@ C: Send one `$GIT_URL/git-upload-pack` request:
    C: 0000
 
 The stream is organized into "commands", with each command
-appearing by itself in a pkt-line.  Within a command line
+appearing by itself in a pkt-line.  Within a command line,
 the text leading up to the first space is the command name,
 and the remainder of the line to the first LF is the value.
 Command lines are terminated with an LF as the last byte of
index 022e74e616400664e7cf59ace487c8b53d6f5cab..7330d880f373fcb067c1dec954ddd60cda370c5d 100644 (file)
@@ -416,12 +416,11 @@ REVISIONS" section of linkgit:gitrevisions[7].
 Updating a repository with git fetch
 ------------------------------------
 
-Eventually the developer cloned from will do additional work in her
-repository, creating new commits and advancing the branches to point
-at the new commits.
+After you clone a repository and commit a few changes of your own, you
+may wish to check the original repository for updates.
 
-The command `git fetch`, with no arguments, will update all of the
-remote-tracking branches to the latest version found in her
+The `git-fetch` command, with no arguments, will update all of the
+remote-tracking branches to the latest version found in the original
 repository.  It will not touch any of your own branches--not even the
 "master" branch that was created for you on clone.
 
@@ -1811,8 +1810,8 @@ manner.
 You can then import these into your mail client and send them by
 hand.  However, if you have a lot to send at once, you may prefer to
 use the linkgit:git-send-email[1] script to automate the process.
-Consult the mailing list for your project first to determine how they
-prefer such patches be handled.
+Consult the mailing list for your project first to determine
+their requirements for submitting patches.
 
 [[importing-patches]]
 Importing patches to a project
@@ -2255,7 +2254,7 @@ $ git checkout test && git merge speed-up-spinlocks
 It is unlikely that you would have any conflicts here ... but you might if you
 spent a while on this step and had also pulled new versions from upstream.
 
-Some time later when enough time has passed and testing done, you can pull the
+Sometime later when enough time has passed and testing done, you can pull the
 same branch into the `release` tree ready to go upstream.  This is where you
 see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
@@ -4231,9 +4230,9 @@ Most of what `git rev-list` did is contained in `revision.c` and
 controls how and what revisions are walked, and more.
 
 The original job of `git rev-parse` is now taken by the function
-`setup_revisions()`, which parses the revisions and the common command line
+`setup_revisions()`, which parses the revisions and the common command-line
 options for the revision walker. This information is stored in the struct
-`rev_info` for later consumption. You can do your own command line option
+`rev_info` for later consumption. You can do your own command-line option
 parsing after calling `setup_revisions()`. After that, you have to call
 `prepare_revision_walk()` for initialization, and then you can get the
 commits one by one with the function `get_revision()`.
index 08fc9ca430d218a8dc279270dc2ec4beaaf01827..07ea1058379ab963648d0df7fd2917e0d2efa8a7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -729,6 +729,7 @@ LIB_H += transport.h
 LIB_H += tree-walk.h
 LIB_H += tree.h
 LIB_H += unpack-trees.h
+LIB_H += unicode_width.h
 LIB_H += url.h
 LIB_H += urlmatch.h
 LIB_H += userdiff.h
index 486f823c78118212080a48e74debd7adae241cda..c50ebdf5fe82d88b901ca26668806c94e03fbb37 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -76,16 +76,15 @@ int git_default_advice_config(const char *var, const char *value)
 
 int error_resolve_conflict(const char *me)
 {
-       error("'%s' is not possible because you have unmerged files.", me);
+       error("%s is not possible because you have unmerged files.", me);
        if (advice_resolve_conflict)
                /*
                 * Message used both when 'git commit' fails and when
                 * other commands doing a merge do.
                 */
-               advise(_("Fix them up in the work tree,\n"
-                        "and then use 'git add/rm <file>' as\n"
-                        "appropriate to mark resolution and make a commit,\n"
-                        "or use 'git commit -a'."));
+               advise(_("Fix them up in the work tree, and then use 'git add/rm <file>'\n"
+                        "as appropriate to mark resolution and make a commit, or use\n"
+                        "'git commit -a'."));
        return -1;
 }
 
index 9e960d549c158a4c761942bb17824c1d5e075d49..256741d2262b237c56b6730bea9d52c9b39b7ee3 100644 (file)
@@ -68,23 +68,3 @@ void argv_array_clear(struct argv_array *array)
        }
        argv_array_init(array);
 }
-
-const char **argv_array_detach(struct argv_array *array, int *argc)
-{
-       const char **argv =
-               array->argv == empty_argv || array->argc == 0 ? NULL : array->argv;
-       if (argc)
-               *argc = array->argc;
-       argv_array_init(array);
-       return argv;
-}
-
-void argv_array_free_detached(const char **argv)
-{
-       if (argv) {
-               int i;
-               for (i = 0; argv[i]; i++)
-                       free((char **)argv[i]);
-               free(argv);
-       }
-}
index 85ba438ac1e877fa865e5b4d2301733735fa5d03..c65e6e825a96efca6111184e5c50d2406064631f 100644 (file)
@@ -19,7 +19,5 @@ LAST_ARG_MUST_BE_NULL
 void argv_array_pushl(struct argv_array *, ...);
 void argv_array_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
-const char **argv_array_detach(struct argv_array *array, int *argc);
-void argv_array_free_detached(const char **argv);
 
 #endif /* ARGV_ARRAY_H */
index 128fc64d64f46af010ffcc1cf83b98705c60dabc..a52a279144bec14f2b2a0795f30e0c5871f60c1b 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * Blame
  *
- * Copyright (c) 2006, Junio C Hamano
+ * Copyright (c) 2006, 2014 by its authors
+ * See COPYING for licensing conditions
  */
 
 #include "cache.h"
@@ -18,7 +19,9 @@
 #include "cache-tree.h"
 #include "string-list.h"
 #include "mailmap.h"
+#include "mergesort.h"
 #include "parse-options.h"
+#include "prio-queue.h"
 #include "utf8.h"
 #include "userdiff.h"
 #include "line-range.h"
@@ -83,11 +86,42 @@ static unsigned blame_copy_score;
  */
 struct origin {
        int refcnt;
+       /* Record preceding blame record for this blob */
        struct origin *previous;
+       /* origins are put in a list linked via `next' hanging off the
+        * corresponding commit's util field in order to make finding
+        * them fast.  The presence in this chain does not count
+        * towards the origin's reference count.  It is tempting to
+        * let it count as long as the commit is pending examination,
+        * but even under circumstances where the commit will be
+        * present multiple times in the priority queue of unexamined
+        * commits, processing the first instance will not leave any
+        * work requiring the origin data for the second instance.  An
+        * interspersed commit changing that would have to be
+        * preexisting with a different ancestry and with the same
+        * commit date in order to wedge itself between two instances
+        * of the same commit in the priority queue _and_ produce
+        * blame entries relevant for it.  While we don't want to let
+        * us get tripped up by this case, it certainly does not seem
+        * worth optimizing for.
+        */
+       struct origin *next;
        struct commit *commit;
+       /* `suspects' contains blame entries that may be attributed to
+        * this origin's commit or to parent commits.  When a commit
+        * is being processed, all suspects will be moved, either by
+        * assigning them to an origin in a different commit, or by
+        * shipping them to the scoreboard's ent list because they
+        * cannot be attributed to a different commit.
+        */
+       struct blame_entry *suspects;
        mmfile_t file;
        unsigned char blob_sha1[20];
        unsigned mode;
+       /* guilty gets set when shipping any suspects to the final
+        * blame list instead of other commits
+        */
+       char guilty;
        char path[FLEX_ARRAY];
 };
 
@@ -176,10 +210,22 @@ static inline struct origin *origin_incref(struct origin *o)
 static void origin_decref(struct origin *o)
 {
        if (o && --o->refcnt <= 0) {
+               struct origin *p, *l = NULL;
                if (o->previous)
                        origin_decref(o->previous);
                free(o->file.ptr);
-               free(o);
+               /* Should be present exactly once in commit chain */
+               for (p = o->commit->util; p; l = p, p = p->next) {
+                       if (p == o) {
+                               if (l)
+                                       l->next = p->next;
+                               else
+                                       o->commit->util = p->next;
+                               free(o);
+                               return;
+                       }
+               }
+               die("internal error in blame::origin_decref");
        }
 }
 
@@ -193,8 +239,12 @@ static void drop_origin_blob(struct origin *o)
 
 /*
  * Each group of lines is described by a blame_entry; it can be split
- * as we pass blame to the parents.  They form a linked list in the
- * scoreboard structure, sorted by the target line number.
+ * as we pass blame to the parents.  They are arranged in linked lists
+ * kept as `suspects' of some unprocessed origin, or entered (when the
+ * blame origin has been finalized) into the scoreboard structure.
+ * While the scoreboard structure is only sorted at the end of
+ * processing (according to final image line number), the lists
+ * attached to an origin are sorted by the target line number.
  */
 struct blame_entry {
        struct blame_entry *next;
@@ -210,15 +260,6 @@ struct blame_entry {
        /* the commit that introduced this group into the final image */
        struct origin *suspect;
 
-       /* true if the suspect is truly guilty; false while we have not
-        * checked if the group came from one of its parents.
-        */
-       char guilty;
-
-       /* true if the entry has been scanned for copies in the current parent
-        */
-       char scanned;
-
        /* the line number of the first line of this group in the
         * suspect's file; internally all line numbers are 0 based.
         */
@@ -230,12 +271,113 @@ struct blame_entry {
        unsigned score;
 };
 
+/*
+ * Any merge of blames happens on lists of blames that arrived via
+ * different parents in a single suspect.  In this case, we want to
+ * sort according to the suspect line numbers as opposed to the final
+ * image line numbers.  The function body is somewhat longish because
+ * it avoids unnecessary writes.
+ */
+
+static struct blame_entry *blame_merge(struct blame_entry *list1,
+                                      struct blame_entry *list2)
+{
+       struct blame_entry *p1 = list1, *p2 = list2,
+               **tail = &list1;
+
+       if (!p1)
+               return p2;
+       if (!p2)
+               return p1;
+
+       if (p1->s_lno <= p2->s_lno) {
+               do {
+                       tail = &p1->next;
+                       if ((p1 = *tail) == NULL) {
+                               *tail = p2;
+                               return list1;
+                       }
+               } while (p1->s_lno <= p2->s_lno);
+       }
+       for (;;) {
+               *tail = p2;
+               do {
+                       tail = &p2->next;
+                       if ((p2 = *tail) == NULL)  {
+                               *tail = p1;
+                               return list1;
+                       }
+               } while (p1->s_lno > p2->s_lno);
+               *tail = p1;
+               do {
+                       tail = &p1->next;
+                       if ((p1 = *tail) == NULL) {
+                               *tail = p2;
+                               return list1;
+                       }
+               } while (p1->s_lno <= p2->s_lno);
+       }
+}
+
+static void *get_next_blame(const void *p)
+{
+       return ((struct blame_entry *)p)->next;
+}
+
+static void set_next_blame(void *p1, void *p2)
+{
+       ((struct blame_entry *)p1)->next = p2;
+}
+
+/*
+ * Final image line numbers are all different, so we don't need a
+ * three-way comparison here.
+ */
+
+static int compare_blame_final(const void *p1, const void *p2)
+{
+       return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
+               ? 1 : -1;
+}
+
+static int compare_blame_suspect(const void *p1, const void *p2)
+{
+       const struct blame_entry *s1 = p1, *s2 = p2;
+       /*
+        * to allow for collating suspects, we sort according to the
+        * respective pointer value as the primary sorting criterion.
+        * The actual relation is pretty unimportant as long as it
+        * establishes a total order.  Comparing as integers gives us
+        * that.
+        */
+       if (s1->suspect != s2->suspect)
+               return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
+       if (s1->s_lno == s2->s_lno)
+               return 0;
+       return s1->s_lno > s2->s_lno ? 1 : -1;
+}
+
+static struct blame_entry *blame_sort(struct blame_entry *head,
+                                     int (*compare_fn)(const void *, const void *))
+{
+       return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);
+}
+
+static int compare_commits_by_reverse_commit_date(const void *a,
+                                                 const void *b,
+                                                 void *c)
+{
+       return -compare_commits_by_commit_date(a, b, c);
+}
+
 /*
  * The current state of the blame assignment.
  */
 struct scoreboard {
        /* the final commit (i.e. where we started digging from) */
        struct commit *final;
+       /* Priority queue for commits with unassigned blame records */
+       struct prio_queue commits;
        struct rev_info *revs;
        const char *path;
 
@@ -268,7 +410,6 @@ static void coalesce(struct scoreboard *sb)
 
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
                if (ent->suspect == next->suspect &&
-                   ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
                        ent->next = next->next;
@@ -283,6 +424,30 @@ static void coalesce(struct scoreboard *sb)
                sanity_check_refcnt(sb);
 }
 
+/*
+ * Merge the given sorted list of blames into a preexisting origin.
+ * If there were no previous blames to that commit, it is entered into
+ * the commit priority queue of the score board.
+ */
+
+static void queue_blames(struct scoreboard *sb, struct origin *porigin,
+                        struct blame_entry *sorted)
+{
+       if (porigin->suspects)
+               porigin->suspects = blame_merge(porigin->suspects, sorted);
+       else {
+               struct origin *o;
+               for (o = porigin->commit->util; o; o = o->next) {
+                       if (o->suspects) {
+                               porigin->suspects = sorted;
+                               return;
+                       }
+               }
+               porigin->suspects = sorted;
+               prio_queue_put(&sb->commits, porigin->commit);
+       }
+}
+
 /*
  * Given a commit and a path in it, create a new origin structure.
  * The callers that add blame to the scoreboard should use
@@ -295,23 +460,32 @@ static struct origin *make_origin(struct commit *commit, const char *path)
        o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
        o->commit = commit;
        o->refcnt = 1;
+       o->next = commit->util;
+       commit->util = o;
        strcpy(o->path, path);
        return o;
 }
 
 /*
  * Locate an existing origin or create a new one.
+ * This moves the origin to front position in the commit util list.
  */
 static struct origin *get_origin(struct scoreboard *sb,
                                 struct commit *commit,
                                 const char *path)
 {
-       struct blame_entry *e;
+       struct origin *o, *l;
 
-       for (e = sb->ent; e; e = e->next) {
-               if (e->suspect->commit == commit &&
-                   !strcmp(e->suspect->path, path))
-                       return origin_incref(e->suspect);
+       for (o = commit->util, l = NULL; o; l = o, o = o->next) {
+               if (!strcmp(o->path, path)) {
+                       /* bump to front */
+                       if (l) {
+                               l->next = o->next;
+                               o->next = commit->util;
+                               commit->util = o;
+                       }
+                       return origin_incref(o);
+               }
        }
        return make_origin(commit, path);
 }
@@ -350,41 +524,19 @@ static struct origin *find_origin(struct scoreboard *sb,
                                  struct commit *parent,
                                  struct origin *origin)
 {
-       struct origin *porigin = NULL;
+       struct origin *porigin;
        struct diff_options diff_opts;
        const char *paths[2];
 
-       if (parent->util) {
-               /*
-                * Each commit object can cache one origin in that
-                * commit.  This is a freestanding copy of origin and
-                * not refcounted.
-                */
-               struct origin *cached = parent->util;
-               if (!strcmp(cached->path, origin->path)) {
+       /* First check any existing origins */
+       for (porigin = parent->util; porigin; porigin = porigin->next)
+               if (!strcmp(porigin->path, origin->path)) {
                        /*
                         * The same path between origin and its parent
                         * without renaming -- the most common case.
                         */
-                       porigin = get_origin(sb, parent, cached->path);
-
-                       /*
-                        * If the origin was newly created (i.e. get_origin
-                        * would call make_origin if none is found in the
-                        * scoreboard), it does not know the blob_sha1/mode,
-                        * so copy it.  Otherwise porigin was in the
-                        * scoreboard and already knows blob_sha1/mode.
-                        */
-                       if (porigin->refcnt == 1) {
-                               hashcpy(porigin->blob_sha1, cached->blob_sha1);
-                               porigin->mode = cached->mode;
-                       }
-                       return porigin;
+                       return origin_incref (porigin);
                }
-               /* otherwise it was not very useful; free it */
-               free(parent->util);
-               parent->util = NULL;
-       }
 
        /* See if the origin->path is different between parent
         * and origin first.  Most of the time they are the
@@ -450,19 +602,6 @@ static struct origin *find_origin(struct scoreboard *sb,
        }
        diff_flush(&diff_opts);
        free_pathspec(&diff_opts.pathspec);
-       if (porigin) {
-               /*
-                * Create a freestanding copy that is not part of
-                * the refcounted origin found in the scoreboard, and
-                * cache it in the commit.
-                */
-               struct origin *cached;
-
-               cached = make_origin(porigin->commit, porigin->path);
-               hashcpy(cached->blob_sha1, porigin->blob_sha1);
-               cached->mode = porigin->mode;
-               parent->util = cached;
-       }
        return porigin;
 }
 
@@ -509,46 +648,31 @@ static struct origin *find_rename(struct scoreboard *sb,
 }
 
 /*
- * Link in a new blame entry to the scoreboard.  Entries that cover the
- * same line range have been removed from the scoreboard previously.
+ * Append a new blame entry to a given output queue.
  */
-static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
+static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
 {
-       struct blame_entry *ent, *prev = NULL;
-
        origin_incref(e->suspect);
 
-       for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
-               prev = ent;
-
-       /* prev, if not NULL, is the last one that is below e */
-
-       if (prev) {
-               e->next = prev->next;
-               prev->next = e;
-       }
-       else {
-               e->next = sb->ent;
-               sb->ent = e;
-       }
+       e->next = **queue;
+       **queue = e;
+       *queue = &e->next;
 }
 
 /*
  * src typically is on-stack; we want to copy the information in it to
- * a malloced blame_entry that is already on the linked list of the
- * scoreboard.  The origin of dst loses a refcnt while the origin of src
- * gains one.
+ * a malloced blame_entry that gets added to the given queue.  The
+ * origin of dst loses a refcnt.
  */
-static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
+static void dup_entry(struct blame_entry ***queue,
+                     struct blame_entry *dst, struct blame_entry *src)
 {
-       struct blame_entry *n;
-
-       n = dst->next;
        origin_incref(src->suspect);
        origin_decref(dst->suspect);
        memcpy(dst, src, sizeof(*src));
-       dst->next = n;
-       dst->score = 0;
+       dst->next = **queue;
+       **queue = dst;
+       *queue = &dst->next;
 }
 
 static const char *nth_line(struct scoreboard *sb, long lno)
@@ -620,10 +744,11 @@ static void split_overlap(struct blame_entry *split,
 
 /*
  * split_overlap() divided an existing blame e into up to three parts
- * in split.  Adjust the linked list of blames in the scoreboard to
+ * in split.  Any assigned blame is moved to queue to
  * reflect the split.
  */
-static void split_blame(struct scoreboard *sb,
+static void split_blame(struct blame_entry ***blamed,
+                       struct blame_entry ***unblamed,
                        struct blame_entry *split,
                        struct blame_entry *e)
 {
@@ -631,61 +756,39 @@ static void split_blame(struct scoreboard *sb,
 
        if (split[0].suspect && split[2].suspect) {
                /* The first part (reuse storage for the existing entry e) */
-               dup_entry(e, &split[0]);
+               dup_entry(unblamed, e, &split[0]);
 
                /* The last part -- me */
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(sb, new_entry);
+               add_blame_entry(unblamed, new_entry);
 
                /* ... and the middle part -- parent */
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(sb, new_entry);
+               add_blame_entry(blamed, new_entry);
        }
        else if (!split[0].suspect && !split[2].suspect)
                /*
                 * The parent covers the entire area; reuse storage for
                 * e and replace it with the parent.
                 */
-               dup_entry(e, &split[1]);
+               dup_entry(blamed, e, &split[1]);
        else if (split[0].suspect) {
                /* me and then parent */
-               dup_entry(e, &split[0]);
+               dup_entry(unblamed, e, &split[0]);
 
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(sb, new_entry);
+               add_blame_entry(blamed, new_entry);
        }
        else {
                /* parent and then me */
-               dup_entry(e, &split[1]);
+               dup_entry(blamed, e, &split[1]);
 
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(sb, new_entry);
-       }
-
-       if (DEBUG) { /* sanity */
-               struct blame_entry *ent;
-               int lno = sb->ent->lno, corrupt = 0;
-
-               for (ent = sb->ent; ent; ent = ent->next) {
-                       if (lno != ent->lno)
-                               corrupt = 1;
-                       if (ent->s_lno < 0)
-                               corrupt = 1;
-                       lno += ent->num_lines;
-               }
-               if (corrupt) {
-                       lno = sb->ent->lno;
-                       for (ent = sb->ent; ent; ent = ent->next) {
-                               printf("L %8d l %8d n %8d\n",
-                                      lno, ent->lno, ent->num_lines);
-                               lno = ent->lno + ent->num_lines;
-                       }
-                       die("oops");
-               }
+               add_blame_entry(unblamed, new_entry);
        }
 }
 
@@ -702,74 +805,146 @@ static void decref_split(struct blame_entry *split)
 }
 
 /*
- * Helper for blame_chunk().  blame_entry e is known to overlap with
- * the patch hunk; split it and pass blame to the parent.
+ * reverse_blame reverses the list given in head, appending tail.
+ * That allows us to build lists in reverse order, then reverse them
+ * afterwards.  This can be faster than building the list in proper
+ * order right away.  The reason is that building in proper order
+ * requires writing a link in the _previous_ element, while building
+ * in reverse order just requires placing the list head into the
+ * _current_ element.
  */
-static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
-                         int tlno, int plno, int same,
-                         struct origin *parent)
-{
-       struct blame_entry split[3];
-
-       split_overlap(split, e, tlno, plno, same, parent);
-       if (split[1].suspect)
-               split_blame(sb, split, e);
-       decref_split(split);
-}
 
-/*
- * Find the line number of the last line the target is suspected for.
- */
-static int find_last_in_target(struct scoreboard *sb, struct origin *target)
+static struct blame_entry *reverse_blame(struct blame_entry *head,
+                                        struct blame_entry *tail)
 {
-       struct blame_entry *e;
-       int last_in_target = -1;
-
-       for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || e->suspect != target)
-                       continue;
-               if (last_in_target < e->s_lno + e->num_lines)
-                       last_in_target = e->s_lno + e->num_lines;
+       while (head) {
+               struct blame_entry *next = head->next;
+               head->next = tail;
+               tail = head;
+               head = next;
        }
-       return last_in_target;
+       return tail;
 }
 
 /*
  * Process one hunk from the patch between the current suspect for
- * blame_entry e and its parent.  Find and split the overlap, and
- * pass blame to the overlapping part to the parent.
+ * blame_entry e and its parent.  This first blames any unfinished
+ * entries before the chunk (which is where target and parent start
+ * differing) on the parent, and then splits blame entries at the
+ * start and at the end of the difference region.  Since use of -M and
+ * -C options may lead to overlapping/duplicate source line number
+ * ranges, all we can rely on from sorting/merging is the order of the
+ * first suspect line number.
  */
-static void blame_chunk(struct scoreboard *sb,
-                       int tlno, int plno, int same,
-                       struct origin *target, struct origin *parent)
+static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
+                       int tlno, int offset, int same,
+                       struct origin *parent)
 {
-       struct blame_entry *e;
+       struct blame_entry *e = **srcq;
+       struct blame_entry *samep = NULL, *diffp = NULL;
 
-       for (e = sb->ent; e; e = e->next) {
-               if (e->guilty || e->suspect != target)
-                       continue;
-               if (same <= e->s_lno)
-                       continue;
-               if (tlno < e->s_lno + e->num_lines)
-                       blame_overlap(sb, e, tlno, plno, same, parent);
+       while (e && e->s_lno < tlno) {
+               struct blame_entry *next = e->next;
+               /*
+                * current record starts before differing portion.  If
+                * it reaches into it, we need to split it up and
+                * examine the second part separately.
+                */
+               if (e->s_lno + e->num_lines > tlno) {
+                       /* Move second half to a new record */
+                       int len = tlno - e->s_lno;
+                       struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
+                       n->suspect = e->suspect;
+                       n->lno = e->lno + len;
+                       n->s_lno = e->s_lno + len;
+                       n->num_lines = e->num_lines - len;
+                       e->num_lines = len;
+                       e->score = 0;
+                       /* Push new record to diffp */
+                       n->next = diffp;
+                       diffp = n;
+               } else
+                       origin_decref(e->suspect);
+               /* Pass blame for everything before the differing
+                * chunk to the parent */
+               e->suspect = origin_incref(parent);
+               e->s_lno += offset;
+               e->next = samep;
+               samep = e;
+               e = next;
+       }
+       /*
+        * As we don't know how much of a common stretch after this
+        * diff will occur, the currently blamed parts are all that we
+        * can assign to the parent for now.
+        */
+
+       if (samep) {
+               **dstq = reverse_blame(samep, **dstq);
+               *dstq = &samep->next;
        }
+       /*
+        * Prepend the split off portions: everything after e starts
+        * after the blameable portion.
+        */
+       e = reverse_blame(diffp, e);
+
+       /*
+        * Now retain records on the target while parts are different
+        * from the parent.
+        */
+       samep = NULL;
+       diffp = NULL;
+       while (e && e->s_lno < same) {
+               struct blame_entry *next = e->next;
+
+               /*
+                * If current record extends into sameness, need to split.
+                */
+               if (e->s_lno + e->num_lines > same) {
+                       /*
+                        * Move second half to a new record to be
+                        * processed by later chunks
+                        */
+                       int len = same - e->s_lno;
+                       struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
+                       n->suspect = origin_incref(e->suspect);
+                       n->lno = e->lno + len;
+                       n->s_lno = e->s_lno + len;
+                       n->num_lines = e->num_lines - len;
+                       e->num_lines = len;
+                       e->score = 0;
+                       /* Push new record to samep */
+                       n->next = samep;
+                       samep = n;
+               }
+               e->next = diffp;
+               diffp = e;
+               e = next;
+       }
+       **srcq = reverse_blame(diffp, reverse_blame(samep, e));
+       /* Move across elements that are in the unblamable portion */
+       if (diffp)
+               *srcq = &diffp->next;
 }
 
 struct blame_chunk_cb_data {
-       struct scoreboard *sb;
-       struct origin *target;
        struct origin *parent;
-       long plno;
-       long tlno;
+       long offset;
+       struct blame_entry **dstq;
+       struct blame_entry **srcq;
 };
 
+/* diff chunks are from parent to target */
 static int blame_chunk_cb(long start_a, long count_a,
                          long start_b, long count_b, void *data)
 {
        struct blame_chunk_cb_data *d = data;
-       blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
-       d->plno = start_a + count_a;
-       d->tlno = start_b + count_b;
+       if (start_a - start_b != d->offset)
+               die("internal error in blame::blame_chunk_cb");
+       blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
+                   start_b + count_b, d->parent);
+       d->offset = start_a + count_a - (start_b + count_b);
        return 0;
 }
 
@@ -778,29 +953,32 @@ static int blame_chunk_cb(long start_a, long count_a,
  * for the lines it is suspected to its parent.  Run diff to find
  * which lines came from parent and pass blame for them.
  */
-static int pass_blame_to_parent(struct scoreboard *sb,
-                               struct origin *target,
-                               struct origin *parent)
+static void pass_blame_to_parent(struct scoreboard *sb,
+                                struct origin *target,
+                                struct origin *parent)
 {
-       int last_in_target;
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
+       struct blame_entry *newdest = NULL;
 
-       memset(&d, 0, sizeof(d));
-       d.sb = sb; d.target = target; d.parent = parent;
-       last_in_target = find_last_in_target(sb, target);
-       if (last_in_target < 0)
-               return 1; /* nothing remains for this target */
+       if (!target->suspects)
+               return; /* nothing remains for this target */
+
+       d.parent = parent;
+       d.offset = 0;
+       d.dstq = &newdest; d.srcq = &target->suspects;
 
        fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
 
        diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
-       /* The rest (i.e. anything after tlno) are the same as the parent */
-       blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
+       /* The rest are the same as the parent */
+       blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
+       *d.dstq = NULL;
+       queue_blames(sb, parent, newdest);
 
-       return 0;
+       return;
 }
 
 /*
@@ -945,43 +1123,80 @@ static void find_copy_in_blob(struct scoreboard *sb,
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 }
 
+/* Move all blame entries from list *source that have a score smaller
+ * than score_min to the front of list *small.
+ * Returns a pointer to the link pointing to the old head of the small list.
+ */
+
+static struct blame_entry **filter_small(struct scoreboard *sb,
+                                        struct blame_entry **small,
+                                        struct blame_entry **source,
+                                        unsigned score_min)
+{
+       struct blame_entry *p = *source;
+       struct blame_entry *oldsmall = *small;
+       while (p) {
+               if (ent_score(sb, p) <= score_min) {
+                       *small = p;
+                       small = &p->next;
+                       p = *small;
+               } else {
+                       *source = p;
+                       source = &p->next;
+                       p = *source;
+               }
+       }
+       *small = oldsmall;
+       *source = NULL;
+       return small;
+}
+
 /*
  * See if lines currently target is suspected for can be attributed to
  * parent.
  */
-static int find_move_in_parent(struct scoreboard *sb,
-                              struct origin *target,
-                              struct origin *parent)
+static void find_move_in_parent(struct scoreboard *sb,
+                               struct blame_entry ***blamed,
+                               struct blame_entry **toosmall,
+                               struct origin *target,
+                               struct origin *parent)
 {
-       int last_in_target, made_progress;
        struct blame_entry *e, split[3];
+       struct blame_entry *unblamed = target->suspects;
+       struct blame_entry *leftover = NULL;
        mmfile_t file_p;
 
-       last_in_target = find_last_in_target(sb, target);
-       if (last_in_target < 0)
-               return 1; /* nothing remains for this target */
+       if (!unblamed)
+               return; /* nothing remains for this target */
 
        fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        if (!file_p.ptr)
-               return 0;
+               return;
 
-       made_progress = 1;
-       while (made_progress) {
-               made_progress = 0;
-               for (e = sb->ent; e; e = e->next) {
-                       if (e->guilty || e->suspect != target ||
-                           ent_score(sb, e) < blame_move_score)
-                               continue;
+       /* At each iteration, unblamed has a NULL-terminated list of
+        * entries that have not yet been tested for blame.  leftover
+        * contains the reversed list of entries that have been tested
+        * without being assignable to the parent.
+        */
+       do {
+               struct blame_entry **unblamedtail = &unblamed;
+               struct blame_entry *next;
+               for (e = unblamed; e; e = next) {
+                       next = e->next;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
                            blame_move_score < ent_score(sb, &split[1])) {
-                               split_blame(sb, split, e);
-                               made_progress = 1;
+                               split_blame(blamed, &unblamedtail, split, e);
+                       } else {
+                               e->next = leftover;
+                               leftover = e;
                        }
                        decref_split(split);
                }
-       }
-       return 0;
+               *unblamedtail = NULL;
+               toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
+       } while (unblamed);
+       target->suspects = reverse_blame(leftover, NULL);
 }
 
 struct blame_list {
@@ -993,62 +1208,46 @@ struct blame_list {
  * Count the number of entries the target is suspected for,
  * and prepare a list of entry and the best split.
  */
-static struct blame_list *setup_blame_list(struct scoreboard *sb,
-                                          struct origin *target,
-                                          int min_score,
+static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
                                           int *num_ents_p)
 {
        struct blame_entry *e;
        int num_ents, i;
        struct blame_list *blame_list = NULL;
 
-       for (e = sb->ent, num_ents = 0; e; e = e->next)
-               if (!e->scanned && !e->guilty &&
-                   e->suspect == target &&
-                   min_score < ent_score(sb, e))
-                       num_ents++;
+       for (e = unblamed, num_ents = 0; e; e = e->next)
+               num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
-               for (e = sb->ent, i = 0; e; e = e->next)
-                       if (!e->scanned && !e->guilty &&
-                           e->suspect == target &&
-                           min_score < ent_score(sb, e))
-                               blame_list[i++].ent = e;
+               for (e = unblamed, i = 0; e; e = e->next)
+                       blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
        return blame_list;
 }
 
-/*
- * Reset the scanned status on all entries.
- */
-static void reset_scanned_flag(struct scoreboard *sb)
-{
-       struct blame_entry *e;
-       for (e = sb->ent; e; e = e->next)
-               e->scanned = 0;
-}
-
 /*
  * For lines target is suspected for, see if we can find code movement
  * across file boundary from the parent commit.  porigin is the path
  * in the parent we already tried.
  */
-static int find_copy_in_parent(struct scoreboard *sb,
-                              struct origin *target,
-                              struct commit *parent,
-                              struct origin *porigin,
-                              int opt)
+static void find_copy_in_parent(struct scoreboard *sb,
+                               struct blame_entry ***blamed,
+                               struct blame_entry **toosmall,
+                               struct origin *target,
+                               struct commit *parent,
+                               struct origin *porigin,
+                               int opt)
 {
        struct diff_options diff_opts;
        int i, j;
-       int retval;
        struct blame_list *blame_list;
        int num_ents;
+       struct blame_entry *unblamed = target->suspects;
+       struct blame_entry *leftover = NULL;
 
-       blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
-       if (!blame_list)
-               return 1; /* nothing remains for this target */
+       if (!unblamed)
+               return; /* nothing remains for this target */
 
        diff_setup(&diff_opts);
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
@@ -1078,9 +1277,9 @@ static int find_copy_in_parent(struct scoreboard *sb,
        if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
                diffcore_std(&diff_opts);
 
-       retval = 0;
-       while (1) {
-               int made_progress = 0;
+       do {
+               struct blame_entry **unblamedtail = &unblamed;
+               blame_list = setup_blame_list(unblamed, &num_ents);
 
                for (i = 0; i < diff_queued_diff.nr; i++) {
                        struct diff_filepair *p = diff_queued_diff.queue[i];
@@ -1117,27 +1316,21 @@ static int find_copy_in_parent(struct scoreboard *sb,
                        struct blame_entry *split = blame_list[j].split;
                        if (split[1].suspect &&
                            blame_copy_score < ent_score(sb, &split[1])) {
-                               split_blame(sb, split, blame_list[j].ent);
-                               made_progress = 1;
+                               split_blame(blamed, &unblamedtail, split,
+                                           blame_list[j].ent);
+                       } else {
+                               blame_list[j].ent->next = leftover;
+                               leftover = blame_list[j].ent;
                        }
-                       else
-                               blame_list[j].ent->scanned = 1;
                        decref_split(split);
                }
                free(blame_list);
-
-               if (!made_progress)
-                       break;
-               blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
-               if (!blame_list) {
-                       retval = 1;
-                       break;
-               }
-       }
-       reset_scanned_flag(sb);
+               *unblamedtail = NULL;
+               toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
+       } while (unblamed);
+       target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
        free_pathspec(&diff_opts.pathspec);
-       return retval;
 }
 
 /*
@@ -1147,20 +1340,21 @@ static int find_copy_in_parent(struct scoreboard *sb,
 static void pass_whole_blame(struct scoreboard *sb,
                             struct origin *origin, struct origin *porigin)
 {
-       struct blame_entry *e;
+       struct blame_entry *e, *suspects;
 
        if (!porigin->file.ptr && origin->file.ptr) {
                /* Steal its file */
                porigin->file = origin->file;
                origin->file.ptr = NULL;
        }
-       for (e = sb->ent; e; e = e->next) {
-               if (e->suspect != origin)
-                       continue;
+       suspects = origin->suspects;
+       origin->suspects = NULL;
+       for (e = suspects; e; e = e->next) {
                origin_incref(porigin);
                origin_decref(e->suspect);
                e->suspect = porigin;
        }
+       queue_blames(sb, porigin, suspects);
 }
 
 /*
@@ -1184,6 +1378,27 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit)
        return cnt;
 }
 
+/* Distribute collected unsorted blames to the respected sorted lists
+ * in the various origins.
+ */
+static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
+{
+       blamed = blame_sort(blamed, compare_blame_suspect);
+       while (blamed)
+       {
+               struct origin *porigin = blamed->suspect;
+               struct blame_entry *suspects = NULL;
+               do {
+                       struct blame_entry *next = blamed->next;
+                       blamed->next = suspects;
+                       suspects = blamed;
+                       blamed = next;
+               } while (blamed && blamed->suspect == porigin);
+               suspects = reverse_blame(suspects, NULL);
+               queue_blames(sb, porigin, suspects);
+       }
+}
+
 #define MAXSG 16
 
 static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
@@ -1194,6 +1409,8 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
        struct commit_list *sg;
        struct origin *sg_buf[MAXSG];
        struct origin *porigin, **sg_origin = sg_buf;
+       struct blame_entry *toosmall = NULL;
+       struct blame_entry *blames, **blametail = &blames;
 
        num_sg = num_scapegoats(revs, commit);
        if (!num_sg)
@@ -1255,38 +1472,71 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                        origin_incref(porigin);
                        origin->previous = porigin;
                }
-               if (pass_blame_to_parent(sb, origin, porigin))
+               pass_blame_to_parent(sb, origin, porigin);
+               if (!origin->suspects)
                        goto finish;
        }
 
        /*
         * Optionally find moves in parents' files.
         */
-       if (opt & PICKAXE_BLAME_MOVE)
-               for (i = 0, sg = first_scapegoat(revs, commit);
-                    i < num_sg && sg;
-                    sg = sg->next, i++) {
-                       struct origin *porigin = sg_origin[i];
-                       if (!porigin)
-                               continue;
-                       if (find_move_in_parent(sb, origin, porigin))
-                               goto finish;
+       if (opt & PICKAXE_BLAME_MOVE) {
+               filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
+               if (origin->suspects) {
+                       for (i = 0, sg = first_scapegoat(revs, commit);
+                            i < num_sg && sg;
+                            sg = sg->next, i++) {
+                               struct origin *porigin = sg_origin[i];
+                               if (!porigin)
+                                       continue;
+                               find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
+                               if (!origin->suspects)
+                                       break;
+                       }
                }
+       }
 
        /*
         * Optionally find copies from parents' files.
         */
-       if (opt & PICKAXE_BLAME_COPY)
+       if (opt & PICKAXE_BLAME_COPY) {
+               if (blame_copy_score > blame_move_score)
+                       filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
+               else if (blame_copy_score < blame_move_score) {
+                       origin->suspects = blame_merge(origin->suspects, toosmall);
+                       toosmall = NULL;
+                       filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
+               }
+               if (!origin->suspects)
+                       goto finish;
+
                for (i = 0, sg = first_scapegoat(revs, commit);
                     i < num_sg && sg;
                     sg = sg->next, i++) {
                        struct origin *porigin = sg_origin[i];
-                       if (find_copy_in_parent(sb, origin, sg->item,
-                                               porigin, opt))
+                       find_copy_in_parent(sb, &blametail, &toosmall,
+                                           origin, sg->item, porigin, opt);
+                       if (!origin->suspects)
                                goto finish;
                }
+       }
 
- finish:
+finish:
+       *blametail = NULL;
+       distribute_blame(sb, blames);
+       /*
+        * prepend toosmall to origin->suspects
+        *
+        * There is no point in sorting: this ends up on a big
+        * unsorted list in the caller anyway.
+        */
+       if (toosmall) {
+               struct blame_entry **tail = &toosmall;
+               while (*tail)
+                       tail = &(*tail)->next;
+               *tail = origin->suspects;
+               origin->suspects = toosmall;
+       }
        for (i = 0; i < num_sg; i++) {
                if (sg_origin[i]) {
                        drop_origin_blob(sg_origin[i]);
@@ -1481,14 +1731,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 }
 
 /*
- * The blame_entry is found to be guilty for the range.  Mark it
- * as such, and show it in incremental output.
+ * The blame_entry is found to be guilty for the range.
+ * Show it in incremental output.
  */
 static void found_guilty_entry(struct blame_entry *ent)
 {
-       if (ent->guilty)
-               return;
-       ent->guilty = 1;
        if (incremental) {
                struct origin *suspect = ent->suspect;
 
@@ -1502,32 +1749,34 @@ static void found_guilty_entry(struct blame_entry *ent)
 }
 
 /*
- * The main loop -- while the scoreboard has lines whose true origin
- * is still unknown, pick one blame_entry, and allow its current
- * suspect to pass blames to its parents.
- */
+ * The main loop -- while we have blobs with lines whose true origin
+ * is still unknown, pick one blob, and allow its lines to pass blames
+ * to its parents. */
 static void assign_blame(struct scoreboard *sb, int opt)
 {
        struct rev_info *revs = sb->revs;
+       struct commit *commit = prio_queue_get(&sb->commits);
 
-       while (1) {
+       while (commit) {
                struct blame_entry *ent;
-               struct commit *commit;
-               struct origin *suspect = NULL;
+               struct origin *suspect = commit->util;
 
                /* find one suspect to break down */
-               for (ent = sb->ent; !suspect && ent; ent = ent->next)
-                       if (!ent->guilty)
-                               suspect = ent->suspect;
-               if (!suspect)
-                       return; /* all done */
+               while (suspect && !suspect->suspects)
+                       suspect = suspect->next;
+
+               if (!suspect) {
+                       commit = prio_queue_get(&sb->commits);
+                       continue;
+               }
+
+               assert(commit == suspect->commit);
 
                /*
                 * We will use this suspect later in the loop,
                 * so hold onto it in the meantime.
                 */
                origin_incref(suspect);
-               commit = suspect->commit;
                parse_commit(commit);
                if (reverse ||
                    (!(commit->object.flags & UNINTERESTING) &&
@@ -1543,9 +1792,22 @@ static void assign_blame(struct scoreboard *sb, int opt)
                        commit->object.flags |= UNINTERESTING;
 
                /* Take responsibility for the remaining entries */
-               for (ent = sb->ent; ent; ent = ent->next)
-                       if (ent->suspect == suspect)
+               ent = suspect->suspects;
+               if (ent) {
+                       suspect->guilty = 1;
+                       for (;;) {
+                               struct blame_entry *next = ent->next;
                                found_guilty_entry(ent);
+                               if (next) {
+                                       ent = next;
+                                       continue;
+                               }
+                               ent->next = sb->ent;
+                               sb->ent = suspect->suspects;
+                               suspect->suspects = NULL;
+                               break;
+                       }
+               }
                origin_decref(suspect);
 
                if (DEBUG) /* sanity */
@@ -1609,9 +1871,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
        char hex[41];
 
        strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
-       printf("%s%c%d %d %d\n",
+       printf("%s %d %d %d\n",
               hex,
-              ent->guilty ? ' ' : '*', /* purely for debugging */
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
@@ -1724,17 +1985,16 @@ static void output(struct scoreboard *sb, int option)
 
        if (option & OUTPUT_PORCELAIN) {
                for (ent = sb->ent; ent; ent = ent->next) {
-                       struct blame_entry *oth;
-                       struct origin *suspect = ent->suspect;
-                       struct commit *commit = suspect->commit;
+                       int count = 0;
+                       struct origin *suspect;
+                       struct commit *commit = ent->suspect->commit;
                        if (commit->object.flags & MORE_THAN_ONE_PATH)
                                continue;
-                       for (oth = ent->next; oth; oth = oth->next) {
-                               if ((oth->suspect->commit != commit) ||
-                                   !strcmp(oth->suspect->path, suspect->path))
-                                       continue;
-                               commit->object.flags |= MORE_THAN_ONE_PATH;
-                               break;
+                       for (suspect = commit->util; suspect; suspect = suspect->next) {
+                               if (suspect->guilty && count++) {
+                                       commit->object.flags |= MORE_THAN_ONE_PATH;
+                                       break;
+                               }
                        }
                }
        }
@@ -2095,11 +2355,9 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
-       convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
-       commit->util = origin;
 
        /*
         * Read the current index, replace the path entry with
@@ -2417,12 +2675,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        memset(&sb, 0, sizeof(sb));
 
        sb.revs = &revs;
-       if (!reverse)
+       if (!reverse) {
                final_commit_name = prepare_final(&sb);
+               sb.commits.compare = compare_commits_by_commit_date;
+       }
        else if (contents_from)
                die("--contents and --children do not blend well.");
-       else
+       else {
                final_commit_name = prepare_initial(&sb);
+               sb.commits.compare = compare_commits_by_reverse_commit_date;
+       }
 
        if (!sb.final) {
                /*
@@ -2511,12 +2773,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                ent->next = next;
                origin_incref(o);
        }
+
+       o->suspects = ent;
+       prio_queue_put(&sb.commits, o->commit);
+
        origin_decref(o);
 
        range_set_release(&ranges);
        string_list_clear(&range_list, 0);
 
-       sb.ent = ent;
+       sb.ent = NULL;
        sb.path = path;
 
        read_mailmap(&mailmap, NULL);
@@ -2529,6 +2795,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        if (incremental)
                return 0;
 
+       sb.ent = blame_sort(sb.ent, compare_blame_final);
+
        coalesce(&sb);
 
        if (!(output_option & OUTPUT_PORCELAIN))
index ff4492162d9543598c1cdc8951f8604a4712516d..f1dc56e55f7b2200412142b10517458ccfda2952 100644 (file)
@@ -651,12 +651,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                        }
                }
                if (old->path && old->name) {
-                       char log_file[PATH_MAX], ref_file[PATH_MAX];
-
-                       git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
-                       git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
-                       if (!file_exists(ref_file) && file_exists(log_file))
-                               remove_path(log_file);
+                       if (!ref_exists(old->path) && reflog_exists(old->path))
+                               delete_reflog(old->path);
                }
        }
        remove_branch_state();
index caec5c0e186f0b2870fc9066f34425cec06283a9..5e2221c8e8c842314cb177d55223e87328b3da26 100644 (file)
@@ -620,6 +620,36 @@ static int author_date_is_interesting(void)
        return author_message || force_date;
 }
 
+static void adjust_comment_line_char(const struct strbuf *sb)
+{
+       char candidates[] = "#;@!$%^&|:";
+       char *candidate;
+       const char *p;
+
+       comment_line_char = candidates[0];
+       if (!memchr(sb->buf, comment_line_char, sb->len))
+               return;
+
+       p = sb->buf;
+       candidate = strchr(candidates, *p);
+       if (candidate)
+               *candidate = ' ';
+       for (p = sb->buf; *p; p++) {
+               if ((p[0] == '\n' || p[0] == '\r') && p[1]) {
+                       candidate = strchr(candidates, p[1]);
+                       if (candidate)
+                               *candidate = ' ';
+               }
+       }
+
+       for (p = candidates; *p == ' '; p++)
+               ;
+       if (!*p)
+               die(_("unable to select a comment character that is not used\n"
+                     "in the current commit message"));
+       comment_line_char = *p;
+}
+
 static int prepare_to_commit(const char *index_file, const char *prefix,
                             struct commit *current_head,
                             struct wt_status *s,
@@ -676,9 +706,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        } else if (use_message) {
                char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
-               if (!use_editor && (!buffer || buffer[2] == '\0'))
-                       die(_("commit has empty message"));
-               strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+               if (buffer)
+                       strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
        } else if (fixup_message) {
@@ -774,6 +803,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
 
+       if (auto_comment_line_char)
+               adjust_comment_line_char(&sb);
        strbuf_release(&sb);
 
        /* This checks if committer ident is explicitly given */
@@ -849,7 +880,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                (int)(ci.mail_end - ci.mail_begin), ci.mail_begin);
 
                if (ident_shown)
-                       status_printf_ln(s, GIT_COLOR_NORMAL, "");
+                       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
 
                saved_color_setting = s->use_color;
                s->use_color = 0;
@@ -867,8 +898,22 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                if (get_sha1(parent, sha1))
                        commitable = !!active_nr;
-               else
-                       commitable = index_differs_from(parent, 0);
+               else {
+                       /*
+                        * Unless the user did explicitly request a submodule
+                        * ignore mode by passing a command line option we do
+                        * not ignore any changed submodule SHA-1s when
+                        * comparing index and parent, no matter what is
+                        * configured. Otherwise we won't commit any
+                        * submodules which were manually staged, which would
+                        * be really confusing.
+                        */
+                       int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+                       if (ignore_submodule_arg &&
+                           !strcmp(ignore_submodule_arg, "all"))
+                               diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
+                       commitable = index_differs_from(parent, diff_flags);
+               }
        }
        strbuf_release(&committer_ident);
 
index 5677c942b6936f3332b1b4ed12541f45c4228dfb..fcd84747015a3d0a1da048612f5ac119a7e161d1 100644 (file)
@@ -395,19 +395,6 @@ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
        return 0;
 }
 
-static char *dup_downcase(const char *string)
-{
-       char *result;
-       size_t len, i;
-
-       len = strlen(string);
-       result = xmalloc(len + 1);
-       for (i = 0; i < len; i++)
-               result[i] = tolower(string[i]);
-       result[i] = '\0';
-       return result;
-}
-
 static int get_urlmatch(const char *var, const char *url)
 {
        char *section_tail;
@@ -422,7 +409,7 @@ static int get_urlmatch(const char *var, const char *url)
        if (!url_normalize(url, &config.url))
                die("%s", config.url.err);
 
-       config.section = dup_downcase(var);
+       config.section = xstrdup_tolower(var);
        section_tail = strchr(config.section, '.');
        if (section_tail) {
                *section_tail = '\0';
index b8d8a3aaf9ee8294be9a78956a70b476d327ee05..ef4481615fd2f4d60cd3cd6a4bbcceaca93c92d5 100644 (file)
@@ -17,6 +17,7 @@
 #include "utf8.h"
 #include "parse-options.h"
 #include "quote.h"
+#include "remote.h"
 
 static const char *fast_export_usage[] = {
        N_("git fast-export [rev-list-opts]"),
@@ -31,6 +32,8 @@ static int use_done_feature;
 static int no_data;
 static int full_tree;
 static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
+static struct refspec *refspecs;
+static int refspecs_nr;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -525,6 +528,15 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
                        continue;
 
+               if (refspecs) {
+                       char *private;
+                       private = apply_refspecs(refspecs, refspecs_nr, full_name);
+                       if (private) {
+                               free(full_name);
+                               full_name = private;
+                       }
+               }
+
                commit = get_commit(e, full_name);
                if (!commit) {
                        warning("%s: Unexpected object of type %s, skipping.",
@@ -661,6 +673,19 @@ static void import_marks(char *input_file)
        fclose(f);
 }
 
+static void handle_deletes(void)
+{
+       int i;
+       for (i = 0; i < refspecs_nr; i++) {
+               struct refspec *refspec = &refspecs[i];
+               if (*refspec->src)
+                       continue;
+
+               printf("reset %s\nfrom %s\n\n",
+                               refspec->dst, sha1_to_hex(null_sha1));
+       }
+}
+
 int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -668,6 +693,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
        uint32_t lastimportid;
+       struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
                            N_("show progress after <n> objects")),
@@ -688,6 +714,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "use-done-feature", &use_done_feature,
                             N_("Use the done feature to terminate the stream")),
                OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
+               OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
+                            N_("Apply refspec to exported refs")),
                OPT_END()
        };
 
@@ -701,11 +729,27 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        revs.topo_order = 1;
        revs.show_source = 1;
        revs.rewrite_parents = 1;
+       argc = parse_options(argc, argv, prefix, options, fast_export_usage,
+                       PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
        argc = setup_revisions(argc, argv, &revs, NULL);
-       argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
        if (argc > 1)
                usage_with_options (fast_export_usage, options);
 
+       if (refspecs_list.nr) {
+               const char **refspecs_str;
+               int i;
+
+               refspecs_str = xmalloc(sizeof(*refspecs_str) * refspecs_list.nr);
+               for (i = 0; i < refspecs_list.nr; i++)
+                       refspecs_str[i] = refspecs_list.items[i].string;
+
+               refspecs_nr = refspecs_list.nr;
+               refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);
+
+               string_list_clear(&refspecs_list, 1);
+               free(refspecs_str);
+       }
+
        if (use_done_feature)
                printf("feature done\n");
 
@@ -733,6 +777,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        }
 
        handle_tags_and_duplicates();
+       handle_deletes();
 
        if (export_filename && lastimportid != last_idnum)
                export_marks(export_filename);
@@ -740,5 +785,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (use_done_feature)
                printf("done\n");
 
+       free_refspec(refspecs_nr, refspecs);
+
        return 0;
 }
index 85f5c2bc62ec1ead593f5aa2e3d7077e65f2f061..8d219d8c42cb4dfb64468f9b4d82648bff646685 100644 (file)
@@ -26,6 +26,7 @@ static const char * const builtin_gc_usage[] = {
 };
 
 static int pack_refs = 1;
+static int prune_reflogs = 1;
 static int aggressive_depth = 250;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
@@ -258,6 +259,19 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
        return NULL;
 }
 
+static int gc_before_repack(void)
+{
+       if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, pack_refs_cmd.argv[0]);
+
+       if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, reflog.argv[0]);
+
+       pack_refs = 0;
+       prune_reflogs = 0;
+       return 0;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int aggressive = 0;
@@ -320,12 +334,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                                fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
                        fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
                }
-               if (detach_auto)
+               if (detach_auto) {
+                       if (gc_before_repack())
+                               return -1;
                        /*
                         * failure to daemonize is ok, we'll continue
                         * in foreground
                         */
                        daemonize();
+               }
        } else
                add_repack_all_option();
 
@@ -337,11 +354,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                    name, (uintmax_t)pid);
        }
 
-       if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
-               return error(FAILED_RUN, pack_refs_cmd.argv[0]);
-
-       if (run_command_v_opt(reflog.argv, RUN_GIT_CMD))
-               return error(FAILED_RUN, reflog.argv[0]);
+       if (gc_before_repack())
+               return -1;
 
        if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
                return error(FAILED_RUN, repack.argv[0]);
index 69ac2d8797ec32dc206425db0a2f3d7356810cfe..c86a142f308d614daefd459fd9f9ddebe03f4cb8 100644 (file)
@@ -361,9 +361,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
                argv[i] = path_list->items[i].string;
        argv[path_list->nr] = NULL;
 
-       if (prefix && chdir(prefix))
-               die(_("Failed to chdir: %s"), prefix);
-       status = run_command_v_opt(argv, RUN_USING_SHELL);
+       status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL);
        if (status)
                exit(status);
        free(argv);
@@ -874,6 +872,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                if (len > 4 && is_dir_sep(pager[len - 5]))
                        pager += len - 4;
 
+               if (opt.ignore_case && !strcmp("less", pager))
+                       string_list_append(&path_list, "-I");
+
                if (!strcmp("less", pager) || !strcmp("vi", pager)) {
                        struct strbuf buf = STRBUF_INIT;
                        strbuf_addf(&buf, "+/%s%s",
index 87d8dd81cc4c24ecf7feee932bc8b39a826bb55d..18f57de58b02de33909a40ea9720e6602cc9dcb5 100644 (file)
@@ -207,8 +207,13 @@ static unsigned check_object(struct object *obj)
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
                int type = sha1_object_info(obj->sha1, &size);
-               if (type != obj->type || type <= 0)
-                       die(_("object of unexpected type"));
+               if (type <= 0)
+                       die(_("did not receive expected object %s"),
+                             sha1_to_hex(obj->sha1));
+               if (type != obj->type)
+                       die(_("object %s: expected type %s, found %s"),
+                           sha1_to_hex(obj->sha1),
+                           typename(obj->type), typename(type));
                obj->flags |= FLAG_CHECKED;
                return 1;
        }
index 39e883635279ad21f3ddd7b224f484b9b04700b0..a7ba211731ecc02523c4509878679f050c151f0b 100644 (file)
@@ -63,6 +63,8 @@ static int parse_decoration_style(const char *var, const char *value)
                return DECORATE_FULL_REFS;
        else if (!strcmp(value, "short"))
                return DECORATE_SHORT_REFS;
+       else if (!strcmp(value, "auto"))
+               return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
        return -1;
 }
 
@@ -158,13 +160,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        if (rev->show_notes)
                init_display_notes(&rev->notes_opt);
 
-       if (rev->diffopt.pickaxe || rev->diffopt.filter)
+       if (rev->diffopt.pickaxe || rev->diffopt.filter ||
+           DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
                rev->always_show_header = 0;
-       if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
-               rev->always_show_header = 0;
-               if (rev->diffopt.pathspec.nr != 1)
-                       usage("git logs can only follow renames on one pathname at a time");
-       }
 
        if (source)
                rev->show_source = 1;
@@ -673,6 +671,7 @@ static void add_header(const char *value)
 static int thread;
 static int do_signoff;
 static const char *signature = git_version_string;
+static const char *signature_file;
 static int config_cover_letter;
 
 enum {
@@ -742,6 +741,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.signature"))
                return git_config_string(&signature, var, value);
+       if (!strcmp(var, "format.signaturefile"))
+               return git_config_pathname(&signature_file, var, value);
        if (!strcmp(var, "format.coverletter")) {
                if (value && !strcasecmp(value, "auto")) {
                        config_cover_letter = COVER_AUTO;
@@ -844,8 +845,13 @@ static void gen_message_id(struct rev_info *info, char *base)
 
 static void print_signature(void)
 {
-       if (signature && *signature)
-               printf("-- \n%s\n\n", signature);
+       if (!signature || !*signature)
+               return;
+
+       printf("-- \n%s", signature);
+       if (signature[strlen(signature)-1] != '\n')
+               putchar('\n');
+       putchar('\n');
 }
 
 static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@ -1230,6 +1236,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
+               OPT_FILENAME(0, "signature-file", &signature_file,
+                               N_("add a signature from a file")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
                OPT_END()
        };
@@ -1447,6 +1455,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        cover_letter = (config_cover_letter == COVER_ON);
        }
 
+       if (!signature) {
+               ; /* --no-signature inhibits all signatures */
+       } else if (signature && signature != git_version_string) {
+               ; /* non-default signature already set */
+       } else if (signature_file) {
+               struct strbuf buf = STRBUF_INIT;
+
+               if (strbuf_read_file(&buf, signature_file, 128) < 0)
+                       die_errno(_("unable to read signature file '%s'"), signature_file);
+               signature = strbuf_detach(&buf, NULL);
+       }
+
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
index 3e9eefb0915a4496ead9ddbf3836a4455351dba0..b2a4b92992748ab52fc6fd3f31041dd88dca54f8 100644 (file)
@@ -92,7 +92,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
 
        if (argv[i]) {
                int j;
-               pattern = xcalloc(sizeof(const char *), argc - i + 1);
+               pattern = xcalloc(argc - i + 1, sizeof(const char *));
                for (j = i; j < argc; j++) {
                        int len = strlen(argv[j]);
                        char *p = xmalloc(len + 3);
index 2c3cd8eab7073669001ad605020577279ba7a07d..cf11c8d6071dd791508d3bd5f46d30550765a90f 100644 (file)
@@ -334,7 +334,7 @@ static int check_header(const struct strbuf *line,
        }
        if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
                for (i = 0; header[i]; i++) {
-                       if (!memcmp("Subject", header[i], 7)) {
+                       if (!strcmp("Subject", header[i])) {
                                handle_header(&hdr_data[i], line);
                                ret = 1;
                                goto check_header_out;
@@ -929,13 +929,13 @@ static void handle_info(void)
                else
                        continue;
 
-               if (!memcmp(header[i], "Subject", 7)) {
+               if (!strcmp(header[i], "Subject")) {
                        if (!keep_subject) {
                                cleanup_subject(hdr);
                                cleanup_space(hdr);
                        }
                        output_header_lines(fout, "Subject", hdr);
-               } else if (!memcmp(header[i], "From", 4)) {
+               } else if (!strcmp(header[i], "From")) {
                        cleanup_space(hdr);
                        handle_from(hdr);
                        fprintf(fout, "Author: %s\n", name.buf);
index 2a7243f52e413c821f9f75969edcc0a9822c8e21..180ef99127d47d0e8fe9f12a590fd9ba9e524f08 100644 (file)
@@ -203,7 +203,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        }
                } else if (cache_name_pos(src, length) < 0)
                        bad = _("not under version control");
-               else if (lstat(dst, &st) == 0) {
+               else if (lstat(dst, &st) == 0 &&
+                        (!ignore_case || strcasecmp(src, dst))) {
                        bad = _("destination exists");
                        if (force) {
                                /*
index 3cfe02d5a5716de1a3aaa5edc1ca0e3d74c03b03..77db8739b5165f15a437651b0803711f6e019839 100644 (file)
@@ -1,17 +1,14 @@
 #include "builtin.h"
 
-static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
+static void flush_current_id(int patchlen, unsigned char *id, unsigned char *result)
 {
-       unsigned char result[20];
        char name[50];
 
        if (!patchlen)
                return;
 
-       git_SHA1_Final(result, c);
        memcpy(name, sha1_to_hex(id), 41);
        printf("%s %s\n", sha1_to_hex(result), name);
-       git_SHA1_Init(c);
 }
 
 static int remove_space(char *line)
@@ -56,10 +53,31 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
        return 1;
 }
 
-static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf)
+static void flush_one_hunk(unsigned char *result, git_SHA_CTX *ctx)
+{
+       unsigned char hash[20];
+       unsigned short carry = 0;
+       int i;
+
+       git_SHA1_Final(hash, ctx);
+       git_SHA1_Init(ctx);
+       /* 20-byte sum, with carry */
+       for (i = 0; i < 20; ++i) {
+               carry += result[i] + hash[i];
+               result[i] = carry;
+               carry >>= 8;
+       }
+}
+
+static int get_one_patchid(unsigned char *next_sha1, unsigned char *result,
+                          struct strbuf *line_buf, int stable)
 {
        int patchlen = 0, found_next = 0;
        int before = -1, after = -1;
+       git_SHA_CTX ctx;
+
+       git_SHA1_Init(&ctx);
+       hashclr(result);
 
        while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
                char *line = line_buf->buf;
@@ -107,6 +125,8 @@ static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct st
                                break;
 
                        /* Else we're parsing another header.  */
+                       if (stable)
+                               flush_one_hunk(result, &ctx);
                        before = after = -1;
                }
 
@@ -119,39 +139,63 @@ static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct st
                /* Compute the sha without whitespace */
                len = remove_space(line);
                patchlen += len;
-               git_SHA1_Update(ctx, line, len);
+               git_SHA1_Update(&ctx, line, len);
        }
 
        if (!found_next)
                hashclr(next_sha1);
 
+       flush_one_hunk(result, &ctx);
+
        return patchlen;
 }
 
-static void generate_id_list(void)
+static void generate_id_list(int stable)
 {
-       unsigned char sha1[20], n[20];
-       git_SHA_CTX ctx;
+       unsigned char sha1[20], n[20], result[20];
        int patchlen;
        struct strbuf line_buf = STRBUF_INIT;
 
-       git_SHA1_Init(&ctx);
        hashclr(sha1);
        while (!feof(stdin)) {
-               patchlen = get_one_patchid(n, &ctx, &line_buf);
-               flush_current_id(patchlen, sha1, &ctx);
+               patchlen = get_one_patchid(n, result, &line_buf, stable);
+               flush_current_id(patchlen, sha1, result);
                hashcpy(sha1, n);
        }
        strbuf_release(&line_buf);
 }
 
-static const char patch_id_usage[] = "git patch-id < patch";
+static const char patch_id_usage[] = "git patch-id [--stable | --unstable] < patch";
+
+static int git_patch_id_config(const char *var, const char *value, void *cb)
+{
+       int *stable = cb;
+
+       if (!strcmp(var, "patchid.stable")) {
+               *stable = git_config_bool(var, value);
+               return 0;
+       }
+
+       return git_default_config(var, value, cb);
+}
 
 int cmd_patch_id(int argc, const char **argv, const char *prefix)
 {
-       if (argc != 1)
+       int stable = -1;
+
+       git_config(git_patch_id_config, &stable);
+
+       /* If nothing is set, default to unstable. */
+       if (stable < 0)
+               stable = 0;
+
+       if (argc == 2 && !strcmp(argv[1], "--stable"))
+               stable = 1;
+       else if (argc == 2 && !strcmp(argv[1], "--unstable"))
+               stable = 0;
+       else if (argc != 1)
                usage(patch_id_usage);
 
-       generate_id_list();
+       generate_id_list(stable);
        return 0;
 }
index c12a9784e6b45d50129b786a45bb5f4e4dd0801a..e8a8fb13b9ffc8d3276048131e33e9874bc3bdea 100644 (file)
@@ -369,7 +369,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        if (!lock)
                return error("cannot lock ref '%s'", ref);
        log_file = git_pathdup("logs/%s", ref);
-       if (!file_exists(log_file))
+       if (!reflog_exists(ref))
                goto finish;
        if (!cmd->dry_run) {
                newlog_path = git_pathdup("logs/%s.lock", ref);
index b3ab4cf8f6517c715845db7e17c67cad5f50d730..c9102e8fe94b41af5136a9cc23db939effae147a 100644 (file)
@@ -282,7 +282,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                item = string_list_insert(&branch_list, name);
 
                if (!item->util)
-                       item->util = xcalloc(sizeof(struct branch_info), 1);
+                       item->util = xcalloc(1, sizeof(struct branch_info));
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
@@ -398,7 +398,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
 
                item = string_list_append(&states->push,
                                          abbrev_branch(ref->peer_ref->name));
-               item->util = xcalloc(sizeof(struct push_info), 1);
+               item->util = xcalloc(1, sizeof(struct push_info));
                info = item->util;
                info->forced = ref->force;
                info->dest = xstrdup(abbrev_branch(ref->name));
@@ -433,7 +433,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
                item = string_list_append(&states->push, _("(matching)"));
-               info = item->util = xcalloc(sizeof(struct push_info), 1);
+               info = item->util = xcalloc(1, sizeof(struct push_info));
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        }
@@ -446,7 +446,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
                else
                        item = string_list_append(&states->push, _("(delete)"));
 
-               info = item->util = xcalloc(sizeof(struct push_info), 1);
+               info = item->util = xcalloc(1, sizeof(struct push_info));
                info->forced = spec->force;
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(spec->dst ? spec->dst : item->string);
@@ -749,15 +749,23 @@ static int mv(int argc, const char **argv)
 
 static int remove_branches(struct string_list *branches)
 {
+       const char **branch_names;
        int i, result = 0;
+
+       branch_names = xmalloc(branches->nr * sizeof(*branch_names));
+       for (i = 0; i < branches->nr; i++)
+               branch_names[i] = branches->items[i].string;
+       result |= repack_without_refs(branch_names, branches->nr);
+       free(branch_names);
+
        for (i = 0; i < branches->nr; i++) {
                struct string_list_item *item = branches->items + i;
                const char *refname = item->string;
-               unsigned char *sha1 = item->util;
 
-               if (delete_ref(refname, sha1, 0))
+               if (delete_ref(refname, NULL, 0))
                        result |= error(_("Could not remove branch %s"), refname);
        }
+
        return result;
 }
 
@@ -789,10 +797,6 @@ static int rm(int argc, const char **argv)
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
 
-       strbuf_addf(&buf, "remote.%s", remote->name);
-       if (git_config_rename_section(buf.buf, NULL) < 1)
-               return error(_("Could not remove config section '%s'"), buf.buf);
-
        read_branches();
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
@@ -837,6 +841,12 @@ static int rm(int argc, const char **argv)
        }
        string_list_clear(&skipped, 0);
 
+       if (!result) {
+               strbuf_addf(&buf, "remote.%s", remote->name);
+               if (git_config_rename_section(buf.buf, NULL) < 1)
+                       return error(_("Could not remove config section '%s'"), buf.buf);
+       }
+
        return result;
 }
 
@@ -1303,6 +1313,8 @@ static int prune_remote(const char *remote, int dry_run)
 {
        int result = 0, i;
        struct ref_states states;
+       struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
+       const char **delete_refs;
        const char *dangling_msg = dry_run
                ? _(" %s will become dangling!")
                : _(" %s has become dangling!");
@@ -1316,11 +1328,20 @@ static int prune_remote(const char *remote, int dry_run)
                       states.remote->url_nr
                       ? states.remote->url[0]
                       : _("(no URL)"));
+
+               delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
+               for (i = 0; i < states.stale.nr; i++)
+                       delete_refs[i] = states.stale.items[i].util;
+               if (!dry_run)
+                       result |= repack_without_refs(delete_refs, states.stale.nr);
+               free(delete_refs);
        }
 
        for (i = 0; i < states.stale.nr; i++) {
                const char *refname = states.stale.items[i].util;
 
+               string_list_insert(&delete_refs_list, refname);
+
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
 
@@ -1330,9 +1351,11 @@ static int prune_remote(const char *remote, int dry_run)
                else
                        printf_ln(_(" * [pruned] %s"),
                               abbrev_ref(refname, "refs/remotes/"));
-               warn_dangling_symref(stdout, dangling_msg, refname);
        }
 
+       warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
+       string_list_clear(&delete_refs_list, 0);
+
        free_remote_ref_states(&states);
        return result;
 }
index b62420a01af820225f23d08b4d0e885505640c77..1bb491d3c4f282e7f68d25cae0e00f419df1dd93 100644 (file)
 #include "builtin.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "run-command.h"
 
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
+       N_("git replace [-f] --edit <object>"),
        N_("git replace -d <object>..."),
        N_("git replace [--format=<format>] [-l [<pattern>]]"),
        NULL
@@ -123,26 +125,36 @@ static int delete_replace_ref(const char *name, const char *ref,
        return 0;
 }
 
-static int replace_object(const char *object_ref, const char *replace_ref,
-                         int force)
+static void check_ref_valid(unsigned char object[20],
+                           unsigned char prev[20],
+                           char *ref,
+                           int ref_size,
+                           int force)
 {
-       unsigned char object[20], prev[20], repl[20];
-       enum object_type obj_type, repl_type;
-       char ref[PATH_MAX];
-       struct ref_lock *lock;
-
-       if (get_sha1(object_ref, object))
-               die("Failed to resolve '%s' as a valid ref.", object_ref);
-       if (get_sha1(replace_ref, repl))
-               die("Failed to resolve '%s' as a valid ref.", replace_ref);
-
-       if (snprintf(ref, sizeof(ref),
+       if (snprintf(ref, ref_size,
                     "refs/replace/%s",
-                    sha1_to_hex(object)) > sizeof(ref) - 1)
+                    sha1_to_hex(object)) > ref_size - 1)
                die("replace ref name too long: %.*s...", 50, ref);
        if (check_refname_format(ref, 0))
                die("'%s' is not a valid ref name.", ref);
 
+       if (read_ref(ref, prev))
+               hashclr(prev);
+       else if (!force)
+               die("replace ref '%s' already exists", ref);
+}
+
+static int replace_object_sha1(const char *object_ref,
+                              unsigned char object[20],
+                              const char *replace_ref,
+                              unsigned char repl[20],
+                              int force)
+{
+       unsigned char prev[20];
+       enum object_type obj_type, repl_type;
+       char ref[PATH_MAX];
+       struct ref_lock *lock;
+
        obj_type = sha1_object_info(object, NULL);
        repl_type = sha1_object_info(repl, NULL);
        if (!force && obj_type != repl_type)
@@ -152,10 +164,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
                    object_ref, typename(obj_type),
                    replace_ref, typename(repl_type));
 
-       if (read_ref(ref, prev))
-               hashclr(prev);
-       else if (!force)
-               die("replace ref '%s' already exists", ref);
+       check_ref_valid(object, prev, ref, sizeof(ref), force);
 
        lock = lock_any_ref_for_update(ref, prev, 0, NULL);
        if (!lock)
@@ -166,13 +175,140 @@ static int replace_object(const char *object_ref, const char *replace_ref,
        return 0;
 }
 
+static int replace_object(const char *object_ref, const char *replace_ref, int force)
+{
+       unsigned char object[20], repl[20];
+
+       if (get_sha1(object_ref, object))
+               die("Failed to resolve '%s' as a valid ref.", object_ref);
+       if (get_sha1(replace_ref, repl))
+               die("Failed to resolve '%s' as a valid ref.", replace_ref);
+
+       return replace_object_sha1(object_ref, object, replace_ref, repl, force);
+}
+
+/*
+ * Write the contents of the object named by "sha1" to the file "filename",
+ * pretty-printed for human editing based on its type.
+ */
+static void export_object(const unsigned char *sha1, const char *filename)
+{
+       const char *argv[] = { "--no-replace-objects", "cat-file", "-p", NULL, NULL };
+       struct child_process cmd = { argv };
+       int fd;
+
+       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (fd < 0)
+               die_errno("unable to open %s for writing", filename);
+
+       argv[3] = sha1_to_hex(sha1);
+       cmd.git_cmd = 1;
+       cmd.out = fd;
+
+       if (run_command(&cmd))
+               die("cat-file reported failure");
+
+       close(fd);
+}
+
+/*
+ * Read a previously-exported (and possibly edited) object back from "filename",
+ * interpreting it as "type", and writing the result to the object database.
+ * The sha1 of the written object is returned via sha1.
+ */
+static void import_object(unsigned char *sha1, enum object_type type,
+                         const char *filename)
+{
+       int fd;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               die_errno("unable to open %s for reading", filename);
+
+       if (type == OBJ_TREE) {
+               const char *argv[] = { "mktree", NULL };
+               struct child_process cmd = { argv };
+               struct strbuf result = STRBUF_INIT;
+
+               cmd.argv = argv;
+               cmd.git_cmd = 1;
+               cmd.in = fd;
+               cmd.out = -1;
+
+               if (start_command(&cmd))
+                       die("unable to spawn mktree");
+
+               if (strbuf_read(&result, cmd.out, 41) < 0)
+                       die_errno("unable to read from mktree");
+               close(cmd.out);
+
+               if (finish_command(&cmd))
+                       die("mktree reported failure");
+               if (get_sha1_hex(result.buf, sha1) < 0)
+                       die("mktree did not return an object name");
+
+               strbuf_release(&result);
+       } else {
+               struct stat st;
+               int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;
+
+               if (fstat(fd, &st) < 0)
+                       die_errno("unable to fstat %s", filename);
+               if (index_fd(sha1, fd, &st, type, NULL, flags) < 0)
+                       die("unable to write object to database");
+               /* index_fd close()s fd for us */
+       }
+
+       /*
+        * No need to close(fd) here; both run-command and index-fd
+        * will have done it for us.
+        */
+}
+
+static int edit_and_replace(const char *object_ref, int force)
+{
+       char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
+       enum object_type type;
+       unsigned char old[20], new[20], prev[20];
+       char ref[PATH_MAX];
+
+       if (get_sha1(object_ref, old) < 0)
+               die("Not a valid object name: '%s'", object_ref);
+
+       type = sha1_object_info(old, NULL);
+       if (type < 0)
+               die("unable to get object type for %s", sha1_to_hex(old));
+
+       check_ref_valid(old, prev, ref, sizeof(ref), force);
+
+       export_object(old, tmpfile);
+       if (launch_editor(tmpfile, NULL, NULL) < 0)
+               die("editing object file failed");
+       import_object(new, type, tmpfile);
+
+       free(tmpfile);
+
+       if (!hashcmp(old, new))
+               return error("new object is the same as the old one: '%s'", sha1_to_hex(old));
+
+       return replace_object_sha1(object_ref, old, "replacement", new, force);
+}
+
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
-       int list = 0, delete = 0, force = 0;
+       int force = 0;
        const char *format = NULL;
+       enum {
+               MODE_UNSPECIFIED = 0,
+               MODE_LIST,
+               MODE_DELETE,
+               MODE_EDIT,
+               MODE_REPLACE
+       } cmdmode = MODE_UNSPECIFIED;
        struct option options[] = {
-               OPT_BOOL('l', "list", &list, N_("list replace refs")),
-               OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
+               OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
+               OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
+               OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
                OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
@@ -182,44 +318,43 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
 
-       if (list && delete)
-               usage_msg_opt("-l and -d cannot be used together",
-                             git_replace_usage, options);
+       if (!cmdmode)
+               cmdmode = argc ? MODE_REPLACE : MODE_LIST;
 
-       if (format && delete)
-               usage_msg_opt("--format and -d cannot be used together",
+       if (format && cmdmode != MODE_LIST)
+               usage_msg_opt("--format cannot be used when not listing",
                              git_replace_usage, options);
 
-       if (force && (list || delete))
-               usage_msg_opt("-f cannot be used with -d or -l",
+       if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)
+               usage_msg_opt("-f only makes sense when writing a replacement",
                              git_replace_usage, options);
 
-       /* Delete refs */
-       if (delete) {
+       switch (cmdmode) {
+       case MODE_DELETE:
                if (argc < 1)
                        usage_msg_opt("-d needs at least one argument",
                                      git_replace_usage, options);
                return for_each_replace_name(argv, delete_replace_ref);
-       }
 
-       /* Replace object */
-       if (!list && argc) {
+       case MODE_REPLACE:
                if (argc != 2)
                        usage_msg_opt("bad number of arguments",
                                      git_replace_usage, options);
-               if (format)
-                       usage_msg_opt("--format cannot be used when not listing",
-                                     git_replace_usage, options);
                return replace_object(argv[0], argv[1], force);
-       }
 
-       /* List refs, even if "list" is not set */
-       if (argc > 1)
-               usage_msg_opt("only one pattern can be given with -l",
-                             git_replace_usage, options);
-       if (force)
-               usage_msg_opt("-f needs some arguments",
-                             git_replace_usage, options);
+       case MODE_EDIT:
+               if (argc != 1)
+                       usage_msg_opt("-e needs exactly one argument",
+                                     git_replace_usage, options);
+               return edit_and_replace(argv[0], force);
+
+       case MODE_LIST:
+               if (argc > 1)
+                       usage_msg_opt("only one pattern can be given with -l",
+                                     git_replace_usage, options);
+               return list_replace_refs(argv[0], format);
 
-       return list_replace_refs(argv[0], format);
+       default:
+               die("BUG: invalid cmdmode %d", (int)cmdmode);
+       }
 }
index f3440023abecc672386381749d5e5855f3673ecf..c6e8a7112700c03b2615f9c99c9858884128ab25 100644 (file)
@@ -338,11 +338,11 @@ static int do_sign(struct strbuf *buffer)
 }
 
 static const char tag_template[] =
-       N_("\nWrite a tag message\n"
+       N_("\nWrite a message for tag:\n  %s\n"
        "Lines starting with '%c' will be ignored.\n");
 
 static const char tag_template_nocleanup[] =
-       N_("\nWrite a tag message\n"
+       N_("\nWrite a message for tag:\n  %s\n"
        "Lines starting with '%c' will be kept; you may remove them"
        " yourself if you want to.\n");
 
@@ -438,9 +438,9 @@ static void create_tag(const unsigned char *object, const char *tag,
                        struct strbuf buf = STRBUF_INIT;
                        strbuf_addch(&buf, '\n');
                        if (opt->cleanup_mode == CLEANUP_ALL)
-                               strbuf_commented_addf(&buf, _(tag_template), comment_line_char);
+                               strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
                        else
-                               strbuf_commented_addf(&buf, _(tag_template_nocleanup), comment_line_char);
+                               strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
                        write_or_die(fd, buf.buf, buf.len);
                        strbuf_release(&buf);
                }
index ba54e19cd559018fcca52b5251bffa78165e16a3..ebea285e1b6863bdb8e4b2a30d1487d18ecc5084 100644 (file)
@@ -637,6 +637,9 @@ static int parse_new_style_cacheinfo(const char *arg,
        unsigned long ul;
        char *endp;
 
+       if (!arg)
+               return -1;
+
        errno = 0;
        ul = strtoul(arg, &endp, 8);
        if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul)
diff --git a/cache.h b/cache.h
index 62a4d39efcfa5f0cbf4257acfbbb9f8fdd5d2859..cbe1935ba6b17b6ccc37a400ea7cf0f67830e1d0 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -618,6 +618,7 @@ extern int precomposed_unicode;
  * that is subject to stripspace.
  */
 extern char comment_line_char;
+extern int auto_comment_line_char;
 
 enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
@@ -825,7 +826,6 @@ int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
 int daemon_avoid_alias(const char *path);
-int offset_1st_component(const char *path);
 
 /* object replacement */
 #define LOOKUP_REPLACE_OBJECT 1
@@ -1294,8 +1294,8 @@ extern int check_repository_format_version(const char *var, const char *value, v
 extern int git_env_bool(const char *, int);
 extern int git_config_system(void);
 extern int config_error_nonbool(const char *);
-#if defined(__GNUC__) && ! defined(__clang__)
-#define config_error_nonbool(s) (config_error_nonbool(s), -1)
+#if defined(__GNUC__)
+#define config_error_nonbool(s) (config_error_nonbool(s), const_error())
 #endif
 extern const char *get_log_output_encoding(void);
 extern const char *get_commit_output_encoding(void);
index 8d1ce88d1493cf18436b03f843c6d90190d47bb2..1a468debb4abdda3cbb90fcc4972b3d40da6f2ed 100644 (file)
--- a/column.c
+++ b/column.c
@@ -370,46 +370,29 @@ static struct child_process column_process;
 
 int run_column_filter(int colopts, const struct column_options *opts)
 {
-       const char *av[10];
-       int ret, ac = 0;
-       struct strbuf sb_colopt  = STRBUF_INIT;
-       struct strbuf sb_width   = STRBUF_INIT;
-       struct strbuf sb_padding = STRBUF_INIT;
+       struct argv_array *argv;
 
        if (fd_out != -1)
                return -1;
 
-       av[ac++] = "column";
-       strbuf_addf(&sb_colopt, "--raw-mode=%d", colopts);
-       av[ac++] = sb_colopt.buf;
-       if (opts && opts->width) {
-               strbuf_addf(&sb_width, "--width=%d", opts->width);
-               av[ac++] = sb_width.buf;
-       }
-       if (opts && opts->indent) {
-               av[ac++] = "--indent";
-               av[ac++] = opts->indent;
-       }
-       if (opts && opts->padding) {
-               strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
-               av[ac++] = sb_padding.buf;
-       }
-       av[ac] = NULL;
+       memset(&column_process, 0, sizeof(column_process));
+       argv = &column_process.args;
+
+       argv_array_push(argv, "column");
+       argv_array_pushf(argv, "--raw-mode=%d", colopts);
+       if (opts && opts->width)
+               argv_array_pushf(argv, "--width=%d", opts->width);
+       if (opts && opts->indent)
+               argv_array_pushf(argv, "--indent=%s", opts->indent);
+       if (opts && opts->padding)
+               argv_array_pushf(argv, "--padding=%d", opts->padding);
 
        fflush(stdout);
-       memset(&column_process, 0, sizeof(column_process));
        column_process.in = -1;
        column_process.out = dup(1);
        column_process.git_cmd = 1;
-       column_process.argv = av;
-
-       ret = start_command(&column_process);
-
-       strbuf_release(&sb_colopt);
-       strbuf_release(&sb_width);
-       strbuf_release(&sb_padding);
 
-       if (ret)
+       if (start_command(&column_process))
                return -2;
 
        fd_out = dup(1);
index 12764fb733f3ca57f12c7fc567eec02d234c889e..f9975d2c2ebc83e8ecfc953c8df907fbff7858f5 100644 (file)
@@ -1396,7 +1396,8 @@ void diff_tree_combined(const unsigned char *sha1,
        if (show_log_first) {
                show_log(rev);
 
-               if (rev->verbose_header && opt->output_format)
+               if (rev->verbose_header && opt->output_format &&
+                   opt->output_format != DIFF_FORMAT_NO_OUTPUT)
                        printf("%s%c", diff_line_prefix(opt),
                               opt->line_termination);
        }
index f4793316a21fe5f3ee1932f7e24cf4ab0920b04c..881be3baa3ccc70afab916331588afb9330258ed 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1031,7 +1031,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
                p->item->object.flags |= STALE;
                num_head++;
        }
-       array = xcalloc(sizeof(*array), num_head);
+       array = xcalloc(num_head, sizeof(*array));
        for (p = heads, i = 0; p; p = p->next) {
                if (p->item->object.flags & STALE) {
                        array[i++] = p->item;
index 120c6c1d3755963285651423cf4b5c791ae64be0..f6fd9a6a6cfdcd723cb5e04efc3af257977e5db5 100644 (file)
@@ -101,19 +101,34 @@ static inline uint64_t git_bswap64(uint64_t x)
 #undef ntohll
 #undef htonll
 
-#if !defined(__BYTE_ORDER)
-# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
-#  define __BYTE_ORDER BYTE_ORDER
-#  define __LITTLE_ENDIAN LITTLE_ENDIAN
-#  define __BIG_ENDIAN BIG_ENDIAN
+#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
+
+# define GIT_BYTE_ORDER __BYTE_ORDER
+# define GIT_LITTLE_ENDIAN __LITTLE_ENDIAN
+# define GIT_BIG_ENDIAN __BIG_ENDIAN
+
+#elif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+
+# define GIT_BYTE_ORDER BYTE_ORDER
+# define GIT_LITTLE_ENDIAN LITTLE_ENDIAN
+# define GIT_BIG_ENDIAN BIG_ENDIAN
+
+#else
+
+# define GIT_BIG_ENDIAN 4321
+# define GIT_LITTLE_ENDIAN 1234
+
+# if defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+#  define GIT_BYTE_ORDER GIT_BIG_ENDIAN
+# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+#  define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN
+# else
+#  error "Cannot determine endianness"
 # endif
-#endif
 
-#if !defined(__BYTE_ORDER)
-# error "Cannot determine endianness"
 #endif
 
-#if __BYTE_ORDER == __BIG_ENDIAN
+#if GIT_BYTE_ORDER == GIT_BIG_ENDIAN
 # define ntohll(n) (n)
 # define htonll(n) (n)
 #else
index e9892f8ee48db4739321d37726cff63dec8c0f13..a0e13bc862c052d8c18a6cba1c47a5a15fd12f7b 100644 (file)
@@ -1823,3 +1823,27 @@ pid_t waitpid(pid_t pid, int *status, int options)
        errno = EINVAL;
        return -1;
 }
+
+int mingw_offset_1st_component(const char *path)
+{
+       int offset = 0;
+       if (has_dos_drive_prefix(path))
+               offset = 2;
+
+       /* unc paths */
+       else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) {
+
+               /* skip server name */
+               char *pos = strpbrk(path + 2, "\\/");
+               if (!pos)
+                       return 0; /* Error: malformed unc path */
+
+               do {
+                       pos++;
+               } while (*pos && !is_dir_sep(*pos));
+
+               offset = pos - path;
+       }
+
+       return offset + is_dir_sep(path[offset]);
+}
index e033e720c900c430a0a74b0223522c396cb66629..3eaf822e28e23e6e46e82a9bb7c9db1ed42d252c 100644 (file)
@@ -339,6 +339,8 @@ static inline char *mingw_find_last_dir_sep(const char *path)
        return ret;
 }
 #define find_last_dir_sep mingw_find_last_dir_sep
+int mingw_offset_1st_component(const char *path);
+#define offset_1st_component mingw_offset_1st_component
 #define PATH_SEP ';'
 #define PRIuMAX "I64u"
 #define PRId64 "I64d"
index 31163f2ae7b71857889f561dea0b3ffecff61989..a9b41d89f465208b6f0e417672077403bf30898f 100644 (file)
@@ -605,7 +605,7 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
 
   if (!rc && timeout == INFTIM)
     {
-      SwitchToThread();
+      SleepEx (1, TRUE);
       goto restart;
     }
 
index c227aa85177724354b3c9740ec62ea2ce521ac62..a1aef1cf3eca01e328b3e319636269236ea91ae0 100644 (file)
--- a/config.c
+++ b/config.c
@@ -147,12 +147,6 @@ int git_config_include(const char *var, const char *value, void *data)
        return ret;
 }
 
-static void lowercase(char *p)
-{
-       for (; *p; p++)
-               *p = tolower(*p);
-}
-
 void git_config_push_parameter(const char *text)
 {
        struct strbuf env = STRBUF_INIT;
@@ -180,7 +174,7 @@ int git_config_parse_parameter(const char *text,
                strbuf_list_free(pair);
                return error("bogus config parameter: %s", text);
        }
-       lowercase(pair[0]->buf);
+       strbuf_tolower(pair[0]);
        if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
                strbuf_list_free(pair);
                return -1;
@@ -826,9 +820,16 @@ static int git_default_core_config(const char *var, const char *value)
        if (!strcmp(var, "core.commentchar")) {
                const char *comment;
                int ret = git_config_string(&comment, var, value);
-               if (!ret)
+               if (ret)
+                       return ret;
+               else if (!strcasecmp(comment, "auto"))
+                       auto_comment_line_char = 1;
+               else if (comment[0] && !comment[1]) {
                        comment_line_char = comment[0];
-               return ret;
+                       auto_comment_line_char = 0;
+               } else
+                       return error("core.commentChar should only be one character");
+               return 0;
        }
 
        if (!strcmp(var, "core.askpass"))
@@ -952,7 +953,7 @@ static int git_default_push_config(const char *var, const char *value)
 static int git_default_mailmap_config(const char *var, const char *value)
 {
        if (!strcmp(var, "mailmap.file"))
-               return git_config_string(&git_mailmap_file, var, value);
+               return git_config_pathname(&git_mailmap_file, var, value);
        if (!strcmp(var, "mailmap.blob"))
                return git_config_string(&git_mailmap_blob, var, value);
 
@@ -1538,7 +1539,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
-       lock = xcalloc(sizeof(struct lock_file), 1);
+       lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
@@ -1801,7 +1802,7 @@ int git_config_rename_section_in_file(const char *config_filename,
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
 
-       lock = xcalloc(sizeof(struct lock_file), 1);
+       lock = xcalloc(1, sizeof(struct lock_file));
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
index a983d061a90f0b720afa1a9b50247da299164d79..94a66502464400d5dfc47a4151cc2fd01780a0a4 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -534,22 +534,18 @@ static int git_use_proxy(const char *host)
 static struct child_process *git_proxy_connect(int fd[2], char *host)
 {
        const char *port = STR(DEFAULT_GIT_PORT);
-       const char **argv;
        struct child_process *proxy;
 
        get_host_and_port(&host, &port);
 
-       argv = xmalloc(sizeof(*argv) * 4);
-       argv[0] = git_proxy_command;
-       argv[1] = host;
-       argv[2] = port;
-       argv[3] = NULL;
        proxy = xcalloc(1, sizeof(*proxy));
-       proxy->argv = argv;
+       argv_array_push(&proxy->args, git_proxy_command);
+       argv_array_push(&proxy->args, host);
+       argv_array_push(&proxy->args, port);
        proxy->in = -1;
        proxy->out = -1;
        if (start_command(proxy))
-               die("cannot start proxy %s", argv[0]);
+               die("cannot start proxy %s", git_proxy_command);
        fd[0] = proxy->out; /* read from proxy stdout */
        fd[1] = proxy->in;  /* write to proxy stdin */
        return proxy;
@@ -663,7 +659,6 @@ struct child_process *git_connect(int fd[2], const char *url,
        char *hostandport, *path;
        struct child_process *conn = &no_fork;
        enum protocol protocol;
-       const char **arg;
        struct strbuf cmd = STRBUF_INIT;
 
        /* Without this we cannot rely on waitpid() to tell
@@ -707,7 +702,6 @@ struct child_process *git_connect(int fd[2], const char *url,
                sq_quote_buf(&cmd, path);
 
                conn->in = conn->out = -1;
-               conn->argv = arg = xcalloc(7, sizeof(*arg));
                if (protocol == PROTO_SSH) {
                        const char *ssh = getenv("GIT_SSH");
                        int putty = ssh && strcasestr(ssh, "plink");
@@ -718,22 +712,21 @@ struct child_process *git_connect(int fd[2], const char *url,
 
                        if (!ssh) ssh = "ssh";
 
-                       *arg++ = ssh;
+                       argv_array_push(&conn->args, ssh);
                        if (putty && !strcasestr(ssh, "tortoiseplink"))
-                               *arg++ = "-batch";
+                               argv_array_push(&conn->args, "-batch");
                        if (port) {
                                /* P is for PuTTY, p is for OpenSSH */
-                               *arg++ = putty ? "-P" : "-p";
-                               *arg++ = port;
+                               argv_array_push(&conn->args, putty ? "-P" : "-p");
+                               argv_array_push(&conn->args, port);
                        }
-                       *arg++ = ssh_host;
+                       argv_array_push(&conn->args, ssh_host);
                } else {
                        /* remove repo-local variables from the environment */
                        conn->env = local_repo_env;
                        conn->use_shell = 1;
                }
-               *arg++ = cmd.buf;
-               *arg = NULL;
+               argv_array_push(&conn->args, cmd.buf);
 
                if (start_command(conn))
                        die("unable to fork");
@@ -759,7 +752,6 @@ int finish_connect(struct child_process *conn)
                return 0;
 
        code = finish_command(conn);
-       free(conn->argv);
        free(conn);
        return code;
 }
index 2c59a76bc2cf2d0fefb633efe805b6c7c15ad7ec..019026efcbc5876d7aa3890eab5078a8bfbe2f7c 100644 (file)
@@ -1472,9 +1472,12 @@ _git_log ()
        __git_complete_revlist
 }
 
+# Common merge options shared by git-merge(1) and git-pull(1).
 __git_merge_options="
        --no-commit --no-stat --log --no-log --squash --strategy
        --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
+       --verify-signatures --no-verify-signatures --gpg-sign
+       --quiet --verbose --progress --no-progress
 "
 
 _git_merge ()
@@ -1483,7 +1486,8 @@ _git_merge ()
 
        case "$cur" in
        --*)
-               __gitcomp "$__git_merge_options"
+               __gitcomp "$__git_merge_options
+                       --rerere-autoupdate --no-rerere-autoupdate --abort"
                return
        esac
        __gitcomp_nl "$(__git_refs)"
index bad45ca47a2379a9226da1ae886e23dff01ca8f4..6e992c08667d0b2b6a3c2d42314e3f500eb7502a 100644 (file)
@@ -1,14 +1,22 @@
 all: git-credential-wincred.exe
 
-CC = gcc
-RM = rm -f
-CFLAGS = -O2 -Wall
-
 -include ../../../config.mak.autogen
 -include ../../../config.mak
 
+CC ?= gcc
+RM ?= rm -f
+CFLAGS ?= -O2 -Wall
+
+prefix ?= /usr/local
+libexecdir ?= $(prefix)/libexec/git-core
+
+INSTALL ?= install
+
 git-credential-wincred.exe : git-credential-wincred.c
        $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
 
+install: git-credential-wincred.exe
+       $(INSTALL) -m 755 $^ $(libexecdir)
+
 clean:
        $(RM) git-credential-wincred.exe
diff --git a/contrib/diffall/README b/contrib/diffall/README
deleted file mode 100644 (file)
index 507f17d..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-The git-diffall script provides a directory based diff mechanism
-for git.
-
-To determine what diff viewer is used, the script requires either
-the 'diff.tool' or 'merge.tool' configuration option to be set.
-
-This script is compatible with most common forms used to specify a
-range of revisions to diff:
-
-  1. git diffall: shows diff between working tree and staged changes
-  2. git diffall --cached [<commit>]: shows diff between staged
-     changes and HEAD (or other named commit)
-  3. git diffall <commit>: shows diff between working tree and named
-     commit
-  4. git diffall <commit> <commit>: show diff between two named commits
-  5. git diffall <commit>..<commit>: same as above
-  6. git diffall <commit>...<commit>: show the changes on the branch
-     containing and up to the second, starting at a common ancestor
-     of both <commit>
-
-Note: all forms take an optional path limiter [-- <path>*]
-
-The '--extcmd=<command>' option allows the user to specify a custom
-command for viewing diffs.  When given, configured defaults are
-ignored and the script runs $command $LOCAL $REMOTE.  Additionally,
-$BASE is set in the environment.
-
-This script is based on an example provided by Thomas Rast on the
-Git list [1]:
-
-[1] http://thread.gmane.org/gmane.comp.version-control.git/124807
diff --git a/contrib/diffall/git-diffall b/contrib/diffall/git-diffall
deleted file mode 100755 (executable)
index 84f2b65..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/bin/sh
-# Copyright 2010 - 2012, Tim Henigan <tim.henigan@gmail.com>
-#
-# Perform a directory diff between commits in the repository using
-# the external diff or merge tool specified in the user's config.
-
-USAGE='[--cached] [--copy-back] [-x|--extcmd=<command>] <commit>{0,2} [-- <path>*]
-
-    --cached     Compare to the index rather than the working tree.
-
-    --copy-back  Copy files back to the working tree when the diff
-                 tool exits (in case they were modified by the
-                 user).  This option is only valid if the diff
-                 compared with the working tree.
-
-    -x=<command>
-    --extcmd=<command>  Specify a custom command for viewing diffs.
-                 git-diffall ignores the configured defaults and
-                 runs $command $LOCAL $REMOTE when this option is
-                 specified. Additionally, $BASE is set in the
-                 environment.
-'
-
-SUBDIRECTORY_OK=1
-. "$(git --exec-path)/git-sh-setup"
-
-TOOL_MODE=diff
-. "$(git --exec-path)/git-mergetool--lib"
-
-merge_tool="$(get_merge_tool)"
-if test -z "$merge_tool"
-then
-       echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set."
-       usage
-fi
-
-start_dir=$(pwd)
-
-# All the file paths returned by the diff command are relative to the root
-# of the working copy. So if the script is called from a subdirectory, it
-# must switch to the root of working copy before trying to use those paths.
-cdup=$(git rev-parse --show-cdup) &&
-cd "$cdup" || {
-       echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
-       exit 1
-}
-
-# set up temp dir
-tmp=$(perl -e 'use File::Temp qw(tempdir);
-       $t=tempdir("/tmp/git-diffall.XXXXX") or exit(1);
-       print $t') || exit 1
-trap 'rm -rf "$tmp"' EXIT
-
-left=
-right=
-paths=
-dashdash_seen=
-compare_staged=
-merge_base=
-left_dir=
-right_dir=
-diff_tool=
-copy_back=
-
-while test $# != 0
-do
-       case "$1" in
-       -h|--h|--he|--hel|--help)
-               usage
-               ;;
-       --cached)
-               compare_staged=1
-               ;;
-       --copy-back)
-               copy_back=1
-               ;;
-       -x|--e|--ex|--ext|--extc|--extcm|--extcmd)
-               if test $# = 1
-               then
-                       echo You must specify the tool for use with --extcmd
-                       usage
-               else
-                       diff_tool=$2
-                       shift
-               fi
-               ;;
-       --)
-               dashdash_seen=1
-               ;;
-       -*)
-               echo Invalid option: "$1"
-               usage
-               ;;
-       *)
-               # could be commit, commit range or path limiter
-               case "$1" in
-               *...*)
-                       left=${1%...*}
-                       right=${1#*...}
-                       merge_base=1
-                       ;;
-               *..*)
-                       left=${1%..*}
-                       right=${1#*..}
-                       ;;
-               *)
-                       if test -n "$dashdash_seen"
-                       then
-                               paths="$paths$1 "
-                       elif test -z "$left"
-                       then
-                               left=$1
-                       elif test -z "$right"
-                       then
-                               right=$1
-                       else
-                               paths="$paths$1 "
-                       fi
-                       ;;
-               esac
-               ;;
-       esac
-       shift
-done
-
-# Determine the set of files which changed
-if test -n "$left" && test -n "$right"
-then
-       left_dir="cmt-$(git rev-parse --short $left)"
-       right_dir="cmt-$(git rev-parse --short $right)"
-
-       if test -n "$compare_staged"
-       then
-               usage
-       elif test -n "$merge_base"
-       then
-               git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist"
-       else
-               git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist"
-       fi
-elif test -n "$left"
-then
-       left_dir="cmt-$(git rev-parse --short $left)"
-
-       if test -n "$compare_staged"
-       then
-               right_dir="staged"
-               git diff --name-only --cached "$left" -- $paths >"$tmp/filelist"
-       else
-               right_dir="working_tree"
-               git diff --name-only "$left" -- $paths >"$tmp/filelist"
-       fi
-else
-       left_dir="HEAD"
-
-       if test -n "$compare_staged"
-       then
-               right_dir="staged"
-               git diff --name-only --cached -- $paths >"$tmp/filelist"
-       else
-               right_dir="working_tree"
-               git diff --name-only -- $paths >"$tmp/filelist"
-       fi
-fi
-
-# Exit immediately if there are no diffs
-if test ! -s "$tmp/filelist"
-then
-       exit 0
-fi
-
-if test -n "$copy_back" && test "$right_dir" != "working_tree"
-then
-       echo "--copy-back is only valid when diff includes the working tree."
-       exit 1
-fi
-
-# Create the named tmp directories that will hold the files to be compared
-mkdir -p "$tmp/$left_dir" "$tmp/$right_dir"
-
-# Populate the tmp/right_dir directory with the files to be compared
-while read name
-do
-       if test -n "$right"
-       then
-               ls_list=$(git ls-tree $right "$name")
-               if test -n "$ls_list"
-               then
-                       mkdir -p "$tmp/$right_dir/$(dirname "$name")"
-                       git show "$right":"$name" >"$tmp/$right_dir/$name" || true
-               fi
-       elif test -n "$compare_staged"
-       then
-               ls_list=$(git ls-files -- "$name")
-               if test -n "$ls_list"
-               then
-                       mkdir -p "$tmp/$right_dir/$(dirname "$name")"
-                       git show :"$name" >"$tmp/$right_dir/$name"
-               fi
-       else
-               if test -e "$name"
-               then
-                       mkdir -p "$tmp/$right_dir/$(dirname "$name")"
-                       cp "$name" "$tmp/$right_dir/$name"
-               fi
-       fi
-done < "$tmp/filelist"
-
-# Populate the tmp/left_dir directory with the files to be compared
-while read name
-do
-       if test -n "$left"
-       then
-               ls_list=$(git ls-tree $left "$name")
-               if test -n "$ls_list"
-               then
-                       mkdir -p "$tmp/$left_dir/$(dirname "$name")"
-                       git show "$left":"$name" >"$tmp/$left_dir/$name" || true
-               fi
-       else
-               if test -n "$compare_staged"
-               then
-                       ls_list=$(git ls-tree HEAD "$name")
-                       if test -n "$ls_list"
-                       then
-                               mkdir -p "$tmp/$left_dir/$(dirname "$name")"
-                               git show HEAD:"$name" >"$tmp/$left_dir/$name"
-                       fi
-               else
-                       mkdir -p "$tmp/$left_dir/$(dirname "$name")"
-                       git show :"$name" >"$tmp/$left_dir/$name"
-               fi
-       fi
-done < "$tmp/filelist"
-
-LOCAL="$tmp/$left_dir"
-REMOTE="$tmp/$right_dir"
-
-if test -n "$diff_tool"
-then
-       export BASE
-       eval $diff_tool '"$LOCAL"' '"$REMOTE"'
-else
-       run_merge_tool "$merge_tool" false
-fi
-
-# Copy files back to the working dir, if requested
-if test -n "$copy_back" && test "$right_dir" = "working_tree"
-then
-       cd "$start_dir"
-       git_top_dir=$(git rev-parse --show-toplevel)
-       find "$tmp/$right_dir" -type f |
-       while read file
-       do
-               cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}"
-       done
-fi
index 4030a168986d29bdbb2cd982a2e819ce009580f6..d888d4516114c719e46ab7f2aeb24e63618b7fac 100644 (file)
@@ -3,17 +3,23 @@
 
 prefix ?= /usr/local
 mandir ?= $(prefix)/share/man
-libexecdir ?= $(prefix)/libexec/git-core
-gitdir ?= $(shell git --exec-path)
+gitexecdir ?= $(prefix)/libexec/git-core
 man1dir ?= $(mandir)/man1
 
-gitver ?= $(word 3,$(shell git --version))
+../../GIT-VERSION-FILE: FORCE
+       $(MAKE) -C ../../ GIT-VERSION-FILE
+
+-include ../../GIT-VERSION-FILE
 
 # this should be set to a 'standard' bsd-type install program
-INSTALL ?= install
+INSTALL  ?= install
+RM       ?= rm -f
+
+ASCIIDOC = asciidoc
+XMLTO    = xmlto
 
-ASCIIDOC_CONF      = ../../Documentation/asciidoc.conf
-MANPAGE_NORMAL_XSL =  ../../Documentation/manpage-normal.xsl
+ASCIIDOC_CONF = ../../Documentation/asciidoc.conf
+MANPAGE_XSL   = ../../Documentation/manpage-normal.xsl
 
 GIT_SUBTREE_SH := git-subtree.sh
 GIT_SUBTREE    := git-subtree
@@ -31,8 +37,8 @@ $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
 doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
 
 install: $(GIT_SUBTREE)
-       $(INSTALL) -d -m 755 $(DESTDIR)$(libexecdir)
-       $(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(libexecdir)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(gitexecdir)
+       $(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(gitexecdir)
 
 install-doc: install-man
 
@@ -41,19 +47,21 @@ install-man: $(GIT_SUBTREE_DOC)
        $(INSTALL) -m 644 $^ $(DESTDIR)$(man1dir)
 
 $(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML)
-       xmlto -m $(MANPAGE_NORMAL_XSL)  man $^
+       $(XMLTO) -m $(MANPAGE_XSL) man $^
 
 $(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT)
-       asciidoc -b docbook -d manpage -f $(ASCIIDOC_CONF) \
-               -agit_version=$(gitver) $^
+       $(ASCIIDOC) -b docbook -d manpage -f $(ASCIIDOC_CONF) \
+               -agit_version=$(GIT_VERSION) $^
 
 $(GIT_SUBTREE_HTML): $(GIT_SUBTREE_TXT)
-       asciidoc -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \
-               -agit_version=$(gitver) $^
+       $(ASCIIDOC) -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \
+               -agit_version=$(GIT_VERSION) $^
 
 test:
        $(MAKE) -C t/ test
 
 clean:
-       rm -f *~ *.xml *.html *.1
-       rm -rf subproj mainline
+       $(RM) $(GIT_SUBTREE)
+       $(RM) *.xml *.html *.1
+
+.PHONY: FORCE
index db925ca76991c635ea53fe7f38d6608d08dfc4d5..fa1a5839af2d0c423adb51238fb330f555dc4dff 100755 (executable)
@@ -558,8 +558,9 @@ cmd_add_commit()
                commit=$(add_squashed_msg "$rev" "$dir" |
                         git commit-tree $tree $headp -p "$rev") || exit $?
        else
+               revp=$(peel_committish "$rev") &&
                commit=$(add_msg "$dir" "$headrev" "$rev" |
-                        git commit-tree $tree $headp -p "$rev") || exit $?
+                        git commit-tree $tree $headp -p "$revp") || exit $?
        fi
        git reset "$commit" || exit $?
        
index b22b710c262ed6c55e97859aac8a2fd1c5adb219..6309d124ca2c97b2b1b9262c7b9470c576870069 100755 (executable)
@@ -8,7 +8,8 @@ This test verifies the basic operation of the merge, pull, add
 and split subcommands of git subtree.
 '
 
-export TEST_DIRECTORY=$(pwd)/../../../t
+TEST_DIRECTORY=$(pwd)/../../../t
+export TEST_DIRECTORY
 
 . ../../../t/test-lib.sh
 
index 4e78a1c3cda15659256c7aa64eb98cab7737c61f..11ac6f692733fbfde96a44cc553002ad748065db 100755 (executable)
@@ -5,53 +5,64 @@
 To simulate incremental imports the environment variable SVNRMAX can be set
 to the highest revision that should be available.
 """
-import sys, os
+import sys
+import os
 
 if sys.hexversion < 0x02040000:
-        # The limiter is the ValueError() calls. This may be too conservative
-        sys.stderr.write("svnrdump-sim.py: requires Python 2.4 or later.\n")
-        sys.exit(1)
+    # The limiter is the ValueError() calls. This may be too conservative
+    sys.stderr.write("svnrdump-sim.py: requires Python 2.4 or later.\n")
+    sys.exit(1)
+
 
 def getrevlimit():
-        var = 'SVNRMAX'
-        if var in os.environ:
-                return os.environ[var]
-        return None
+    var = 'SVNRMAX'
+    if var in os.environ:
+        return os.environ[var]
+    return None
+
 
 def writedump(url, lower, upper):
-        if url.startswith('sim://'):
-                filename = url[6:]
-                if filename[-1] == '/': filename = filename[:-1] #remove terminating slash
-        else:
-                raise ValueError('sim:// url required')
-        f = open(filename, 'r');
-        state = 'header'
-        wroterev = False
-        while(True):
-                l = f.readline()
-                if l == '': break
-                if state == 'header' and l.startswith('Revision-number: '):
-                        state = 'prefix'
-                if state == 'prefix' and l == 'Revision-number: %s\n' % lower:
-                        state = 'selection'
-                if not upper == 'HEAD' and state == 'selection' and l == 'Revision-number: %s\n' % upper:
-                        break;
+    if url.startswith('sim://'):
+        filename = url[6:]
+        if filename[-1] == '/':
+            filename = filename[:-1]  # remove terminating slash
+    else:
+        raise ValueError('sim:// url required')
+    f = open(filename, 'r')
+    state = 'header'
+    wroterev = False
+    while(True):
+        l = f.readline()
+        if l == '':
+            break
+        if state == 'header' and l.startswith('Revision-number: '):
+            state = 'prefix'
+        if state == 'prefix' and l == 'Revision-number: %s\n' % lower:
+            state = 'selection'
+        if not upper == 'HEAD' and state == 'selection' and \
+                l == 'Revision-number: %s\n' % upper:
+            break
 
-                if state == 'header' or state == 'selection':
-                        if state == 'selection': wroterev = True
-                        sys.stdout.write(l)
-        return wroterev
+        if state == 'header' or state == 'selection':
+            if state == 'selection':
+                wroterev = True
+            sys.stdout.write(l)
+    return wroterev
 
 if __name__ == "__main__":
-        if not (len(sys.argv) in (3, 4, 5)):
-                print("usage: %s dump URL -rLOWER:UPPER")
-                sys.exit(1)
-        if not sys.argv[1] == 'dump': raise NotImplementedError('only "dump" is suppported.')
-        url = sys.argv[2]
-        r = ('0', 'HEAD')
-        if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
-                r = sys.argv[3][2:].lstrip().split(':')
-        if not getrevlimit() is None: r[1] = getrevlimit()
-        if writedump(url, r[0], r[1]): ret = 0
-        else: ret = 1
-        sys.exit(ret)
+    if not (len(sys.argv) in (3, 4, 5)):
+        print("usage: %s dump URL -rLOWER:UPPER")
+        sys.exit(1)
+    if not sys.argv[1] == 'dump':
+        raise NotImplementedError('only "dump" is suppported.')
+    url = sys.argv[2]
+    r = ('0', 'HEAD')
+    if len(sys.argv) == 4 and sys.argv[3][0:2] == '-r':
+        r = sys.argv[3][2:].lstrip().split(':')
+    if not getrevlimit() is None:
+        r[1] = getrevlimit()
+    if writedump(url, r[0], r[1]):
+        ret = 0
+    else:
+        ret = 1
+    sys.exit(ret)
diff --git a/contrib/vim/README b/contrib/vim/README
deleted file mode 100644 (file)
index 8f16d06..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Syntax highlighting for git commit messages, config files, etc. is
-included with the vim distribution as of vim 7.2, and should work
-automatically.
-
-If you have an older version of vim, you can get the latest syntax
-files from the vim project:
-
-  http://ftp.vim.org/pub/vim/runtime/syntax/git.vim
-  http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim
-  http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim
-  http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim
-  http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim
-
-These files are also available via FTP at the same location.
-
-To install:
-
-  1. Copy these files to vim's syntax directory $HOME/.vim/syntax
-  2. To auto-detect the editing of various git-related filetypes:
-
-       $ curl http://ftp.vim.org/pub/vim/runtime/filetype.vim |
-               sed -ne '/^" Git$/, /^$/ p' >>$HOME/.vim/filetype.vim
index eba12556848e975cd6f1a55ae25760823e90cdc8..f9c63e96137aff6b5ae2376af3fca789ff03e975 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -475,14 +475,6 @@ static void make_service_overridable(const char *name, int ena)
        die("No such service %s", name);
 }
 
-static char *xstrdup_tolower(const char *str)
-{
-       char *p, *dup = xstrdup(str);
-       for (p = dup; *p; p++)
-               *p = tolower(*p);
-       return dup;
-}
-
 static void parse_host_and_port(char *hostport, char **host,
        char **port)
 {
index 044872935c30d84affb1955a7ae03c0986c90bda..875aff864391ed037627462ac39c314f248b5ae6 100644 (file)
@@ -97,7 +97,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                diff_unmerged_stage = 2;
        entries = active_nr;
        for (i = 0; i < entries; i++) {
-               struct stat st;
                unsigned int oldmode, newmode;
                struct cache_entry *ce = active_cache[i];
                int changed;
@@ -115,6 +114,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        unsigned int wt_mode = 0;
                        int num_compare_stages = 0;
                        size_t path_len;
+                       struct stat st;
 
                        path_len = ce_namelen(ce);
 
@@ -195,26 +195,35 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        continue;
 
                /* If CE_VALID is set, don't look at workdir for file removal */
-               changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
-               if (changed) {
-                       if (changed < 0) {
-                               perror(ce->name);
+               if (ce->ce_flags & CE_VALID) {
+                       changed = 0;
+                       newmode = ce->ce_mode;
+               } else {
+                       struct stat st;
+
+                       changed = check_removed(ce, &st);
+                       if (changed) {
+                               if (changed < 0) {
+                                       perror(ce->name);
+                                       continue;
+                               }
+                               diff_addremove(&revs->diffopt, '-', ce->ce_mode,
+                                              ce->sha1, !is_null_sha1(ce->sha1),
+                                              ce->name, 0);
                                continue;
                        }
-                       diff_addremove(&revs->diffopt, '-', ce->ce_mode,
-                                      ce->sha1, !is_null_sha1(ce->sha1),
-                                      ce->name, 0);
-                       continue;
+
+                       changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
+                                                           ce_option, &dirty_submodule);
+                       newmode = ce_mode_from_stat(ce, st.st_mode);
                }
-               changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
-                                                   ce_option, &dirty_submodule);
+
                if (!changed && !dirty_submodule) {
                        ce_mark_uptodate(ce);
                        if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                                continue;
                }
                oldmode = ce->ce_mode;
-               newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
                            ce->sha1, (changed ? null_sha1 : ce->sha1),
                            !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)),
diff --git a/diff.c b/diff.c
index f66716fab4b477c58434126b4aaa4b4372ce163a..bba9a558a60a490b7edd6dec370181a0941bf985 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1361,7 +1361,7 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
                                          const char *name_b)
 {
        struct diffstat_file *x;
-       x = xcalloc(sizeof (*x), 1);
+       x = xcalloc(1, sizeof(*x));
        ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
        diffstat->files[diffstat->nr++] = x;
        if (name_b) {
@@ -3320,6 +3320,9 @@ void diff_setup_done(struct diff_options *options)
        }
 
        options->diff_path_counter = 0;
+
+       if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
+               die(_("--follow requires exactly one pathspec"));
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
diff --git a/dir.c b/dir.c
index eb6f581270f81a2e844e3396f966e67d06d27f41..797805d6a1d33ef66ccb61155135cdcd1a53d9d0 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -508,21 +508,25 @@ void clear_exclude_list(struct exclude_list *el)
 
 static void trim_trailing_spaces(char *buf)
 {
-       int i, last_space = -1, nr_spaces, len = strlen(buf);
-       for (i = 0; i < len; i++)
-               if (buf[i] == '\\')
-                       i++;
-               else if (buf[i] == ' ') {
-                       if (last_space == -1) {
-                               last_space = i;
-                               nr_spaces = 1;
-                       } else
-                               nr_spaces++;
-               } else
-                       last_space = -1;
-
-       if (last_space != -1 && last_space + nr_spaces == len)
-               buf[last_space] = '\0';
+       char *p, *last_space = NULL;
+
+       for (p = buf; *p; p++)
+               switch (*p) {
+               case ' ':
+                       if (!last_space)
+                               last_space = p;
+                       break;
+               case '\\':
+                       p++;
+                       if (!*p)
+                               return;
+                       /* fallthrough */
+               default:
+                       last_space = NULL;
+               }
+
+       if (last_space)
+               *last_space = '\0';
 }
 
 int add_excludes_from_file_to_list(const char *fname,
index 5c4815dbe132fc358aa55eeb20d86d867c97c37f..4dac5e9edd75499ccf52debc847807d2fc180730 100644 (file)
@@ -37,7 +37,7 @@ int core_compression_seen;
 int fsync_object_files;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
-size_t delta_base_cache_limit = 16 * 1024 * 1024;
+size_t delta_base_cache_limit = 96 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
 const char *pager_program;
 int pager_use_color = 1;
@@ -69,9 +69,10 @@ unsigned long pack_size_limit_cfg;
  * that is subject to stripspace.
  */
 char comment_line_char = '#';
+int auto_comment_line_char;
 
 /* Parallel index stat data preload? */
-int core_preload_index = 0;
+int core_preload_index = 1;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
index fb4738d373a5a1048c2ff388a729e6088515ca9b..6707a66471f2038a0c826c840a201a7362de95ed 100644 (file)
@@ -248,6 +248,7 @@ struct branch {
        uintmax_t last_commit;
        uintmax_t num_notes;
        unsigned active : 1;
+       unsigned delete : 1;
        unsigned pack_id : PACK_ID_BITS;
        unsigned char sha1[20];
 };
@@ -1681,10 +1682,13 @@ static int update_branch(struct branch *b)
        struct ref_lock *lock;
        unsigned char old_sha1[20];
 
-       if (is_null_sha1(b->sha1))
-               return 0;
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
+       if (is_null_sha1(b->sha1)) {
+               if (b->delete)
+                       delete_ref(b->name, old_sha1, 0);
+               return 0;
+       }
        lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
        if (!lock)
                return error("Unable to lock %s", b->name);
@@ -2611,8 +2615,11 @@ static int parse_from(struct branch *b)
                        free(buf);
                } else
                        parse_from_existing(b);
-       } else if (!get_sha1(from, b->sha1))
+       } else if (!get_sha1(from, b->sha1)) {
                parse_from_existing(b);
+               if (is_null_sha1(b->sha1))
+                       b->delete = 1;
+       }
        else
                die("Invalid ref name or SHA1 expression: %s", from);
 
index 76910e6cd16f128be9430ac86f35a8e06d390e0f..b6f03b36dc762b891e77a5ca44e7ca5f270c5af9 100644 (file)
@@ -267,6 +267,10 @@ extern char *gitbasename(char *);
 #define has_dos_drive_prefix(path) 0
 #endif
 
+#ifndef offset_1st_component
+#define offset_1st_component(path) (is_dir_sep((path)[0]))
+#endif
+
 #ifndef is_dir_sep
 #define is_dir_sep(c) ((c) == '/')
 #endif
@@ -330,8 +334,12 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
  * trying to help gcc, anyway, it's OK; other compilers will fall back to
  * using the function as usual.
  */
-#if defined(__GNUC__) && ! defined(__clang__)
-#define error(...) (error(__VA_ARGS__), -1)
+#if defined(__GNUC__)
+static inline int const_error(void)
+{
+       return -1;
+}
+#define error(...) (error(__VA_ARGS__), const_error())
 #endif
 
 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
index 4aa3eb80fd25a803d291631965d009a49cfe79dc..513efa662e86cb157b8690bfb025556fa0c836f0 100755 (executable)
@@ -345,7 +345,17 @@ PidFile "$fqgitdir/pid"
 Listen $bind$port
 EOF
 
-       for mod in mime dir env log_config
+       for mod in mpm_event mpm_prefork mpm_worker
+       do
+               if test -e $module_path/mod_${mod}.so
+               then
+                       echo "LoadModule ${mod}_module " \
+                            "$module_path/mod_${mod}.so" >> "$conf"
+                       # only one mpm module permitted
+                       break
+               fi
+       done
+       for mod in mime dir env log_config authz_core
        do
                if test -e $module_path/mod_${mod}.so
                then
index 773cafcd89b6cd89d413e3d51056c4aaf53c1f2c..7bb0f7313bc9d4d2ad1ae43ec68afe761334a59a 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1238,6 +1238,28 @@ def edit_template(self, template_file):
             if response == 'n':
                 return False
 
+    def get_diff_description(self, editedFiles):
+        # diff
+        if os.environ.has_key("P4DIFF"):
+            del(os.environ["P4DIFF"])
+        diff = ""
+        for editedFile in editedFiles:
+            diff += p4_read_pipe(['diff', '-du',
+                                  wildcard_encode(editedFile)])
+
+        # new file diff
+        newdiff = ""
+        for newFile in filesToAdd:
+            newdiff += "==== new file ====\n"
+            newdiff += "--- /dev/null\n"
+            newdiff += "+++ %s\n" % newFile
+            f = open(newFile, "r")
+            for line in f.readlines():
+                newdiff += "+" + line
+            f.close()
+
+        return diff + newdiff
+
     def applyCommit(self, id):
         """Apply one commit, return True if it succeeded."""
 
@@ -1398,34 +1420,15 @@ def applyCommit(self, id):
             submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n"
 
         separatorLine = "######## everything below this line is just the diff #######\n"
+        if not self.prepare_p4_only:
+            submitTemplate += separatorLine
+            submitTemplate += self.get_diff_description(editedFiles)
 
-        # diff
-        if os.environ.has_key("P4DIFF"):
-            del(os.environ["P4DIFF"])
-        diff = ""
-        for editedFile in editedFiles:
-            diff += p4_read_pipe(['diff', '-du',
-                                  wildcard_encode(editedFile)])
-
-        # new file diff
-        newdiff = ""
-        for newFile in filesToAdd:
-            newdiff += "==== new file ====\n"
-            newdiff += "--- /dev/null\n"
-            newdiff += "+++ %s\n" % newFile
-            f = open(newFile, "r")
-            for line in f.readlines():
-                newdiff += "+" + line
-            f.close()
-
-        # change description file: submitTemplate, separatorLine, diff, newdiff
         (handle, fileName) = tempfile.mkstemp()
         tmpFile = os.fdopen(handle, "w+")
         if self.isWindows:
             submitTemplate = submitTemplate.replace("\n", "\r\n")
-            separatorLine = separatorLine.replace("\n", "\r\n")
-            newdiff = newdiff.replace("\n", "\r\n")
-        tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
+        tmpFile.write(submitTemplate)
         tmpFile.close()
 
         if self.prepare_p4_only:
index cfc589dc15061c8ed1797c2d91ec420b1d6f33fe..18a394fcc4e704621c8b5e8bc92b7cbee2fd21fe 100755 (executable)
@@ -58,11 +58,9 @@ pull_ff=$(git config pull.ff)
 case "$pull_ff" in
 false)
        no_ff=--no-ff
-       break
        ;;
 only)
        ff_only=--ff-only
-       break
        ;;
 esac
 
index 6ec9d3cb40b1e96f75132a11eb625fd42e64885d..f267d8b6c3eea5c557e63814e40a9ef38b776354 100644 (file)
@@ -1049,14 +1049,14 @@ fi
 
 
 has_action "$todo" ||
-       die_abort "Nothing to do"
+       return 2
 
 cp "$todo" "$todo".backup
 git_sequence_editor "$todo" ||
        die_abort "Could not execute editor"
 
 has_action "$todo" ||
-       die_abort "Nothing to do"
+       return 2
 
 expand_todo_ids
 
index 5c7a0a1a583552de49ee2e094e80f80ee18fd68c..06c810b64fe04d3675820e38d7eeabe7589dab56 100755 (executable)
@@ -155,7 +155,7 @@ move_to_original_branch () {
        esac
 }
 
-finish_rebase () {
+apply_autostash () {
        if test -f "$state_dir/autostash"
        then
                stash_sha1=$(cat "$state_dir/autostash")
@@ -171,6 +171,10 @@ You can run "git stash pop" or "git stash drop" at any time.
 '
                fi
        fi
+}
+
+finish_rebase () {
+       apply_autostash &&
        git gc --auto &&
        rm -rf "$state_dir"
 }
@@ -186,6 +190,11 @@ run_specific_rebase () {
        if test $ret -eq 0
        then
                finish_rebase
+       elif test $ret -eq 2 # special exit status for rebase -i
+       then
+               apply_autostash &&
+               rm -rf "$state_dir" &&
+               die "Nothing to do"
        fi
        exit $ret
 }
index 1c006a0518e247d58759df98602f6dae3935caf6..a9c75a2360fec7a9ce5f22877a471f70bc405ff1 100755 (executable)
@@ -13,7 +13,8 @@ refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
 
 test -z "$refspec" && prefix="refs"
 
-export GIT_DIR="$url/.git"
+GIT_DIR="$url/.git"
+export GIT_DIR
 
 force=
 
index 5f28b32dc7ff75830bde919b2f4776f7a9b017e2..9447980330ce7892757f9b11fa45cfeb3e6fcb34 100644 (file)
@@ -160,7 +160,7 @@ git_pager() {
        else
                GIT_PAGER=cat
        fi
-       : ${LESS=-FRSX}
+       : ${LESS=-FRX}
        : ${LV=-c}
        export LESS LV
 
index af549c731779db02db29fef446014a58bc9a3996..bcc757b3900b02595e5b432edcfd590e312b48ce 100755 (executable)
@@ -94,7 +94,8 @@ create_stash () {
                # ease of unpacking later.
                u_commit=$(
                        untracked_files | (
-                               export GIT_INDEX_FILE="$TMPindex"
+                               GIT_INDEX_FILE="$TMPindex" &&
+                               export GIT_INDEX_FILE &&
                                rm -f "$TMPindex" &&
                                git update-index -z --add --remove --stdin &&
                                u_tree=$(git write-tree) &&
index b55d83ac46a96ba15fb7ac8cb43b8c279b40d119..e146b833d163ab58984fec3d6fb9f45b148e0171 100755 (executable)
@@ -291,9 +291,6 @@ module_clone()
        # resolve any symlinks that might be present in $PWD
        a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
        b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
-       # normalize Windows-style absolute paths to POSIX-style absolute paths
-       case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac
-       case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac
        # Remove all common leading directories after a sanity check
        if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
                die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
index f2c56c84544be36414686ed2e27c8b08e8946e53..de00d1693af902713ab5562e9d87fe621b02f955 100644 (file)
@@ -1732,7 +1732,7 @@ int main(int argc, char **argv)
 
        git_extract_argv0_path(argv[0]);
 
-       repo = xcalloc(sizeof(*repo), 1);
+       repo = xcalloc(1, sizeof(*repo));
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
diff --git a/http.c b/http.c
index 94e1afdee7890825dfda0946b999f524265603fc..2b4f6a357c39f3685107da3cf63a9891470a6de9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -906,6 +906,83 @@ static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
        return ret;
 }
 
+/*
+ * Check for and extract a content-type parameter. "raw"
+ * should be positioned at the start of the potential
+ * parameter, with any whitespace already removed.
+ *
+ * "name" is the name of the parameter. The value is appended
+ * to "out".
+ */
+static int extract_param(const char *raw, const char *name,
+                        struct strbuf *out)
+{
+       size_t len = strlen(name);
+
+       if (strncasecmp(raw, name, len))
+               return -1;
+       raw += len;
+
+       if (*raw != '=')
+               return -1;
+       raw++;
+
+       while (*raw && !isspace(*raw))
+               strbuf_addch(out, *raw++);
+       return 0;
+}
+
+/*
+ * Extract a normalized version of the content type, with any
+ * spaces suppressed, all letters lowercased, and no trailing ";"
+ * or parameters.
+ *
+ * Note that we will silently remove even invalid whitespace. For
+ * example, "text / plain" is specifically forbidden by RFC 2616,
+ * but "text/plain" is the only reasonable output, and this keeps
+ * our code simple.
+ *
+ * If the "charset" argument is not NULL, store the value of any
+ * charset parameter there.
+ *
+ * Example:
+ *   "TEXT/PLAIN; charset=utf-8" -> "text/plain", "utf-8"
+ *   "text / plain" -> "text/plain"
+ */
+static void extract_content_type(struct strbuf *raw, struct strbuf *type,
+                                struct strbuf *charset)
+{
+       const char *p;
+
+       strbuf_reset(type);
+       strbuf_grow(type, raw->len);
+       for (p = raw->buf; *p; p++) {
+               if (isspace(*p))
+                       continue;
+               if (*p == ';') {
+                       p++;
+                       break;
+               }
+               strbuf_addch(type, tolower(*p));
+       }
+
+       if (!charset)
+               return;
+
+       strbuf_reset(charset);
+       while (*p) {
+               while (isspace(*p))
+                       p++;
+               if (!extract_param(p, "charset", charset))
+                       return;
+               while (*p && !isspace(*p))
+                       p++;
+       }
+
+       if (!charset->len && starts_with(type->buf, "text/"))
+               strbuf_addstr(charset, "ISO-8859-1");
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
@@ -957,9 +1034,13 @@ static int http_request(const char *url,
 
        ret = run_one_slot(slot, &results);
 
-       if (options && options->content_type)
-               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
-                               options->content_type);
+       if (options && options->content_type) {
+               struct strbuf raw = STRBUF_INIT;
+               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw);
+               extract_content_type(&raw, options->content_type,
+                                    options->charset);
+               strbuf_release(&raw);
+       }
 
        if (options && options->effective_url)
                curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
diff --git a/http.h b/http.h
index e64084fe6d455652bde1b02012b52879327e2c0a..473179b14d5648635853af4c5bf425116c32b6fe 100644 (file)
--- a/http.h
+++ b/http.h
@@ -143,6 +143,13 @@ struct http_get_options {
        /* If non-NULL, returns the content-type of the response. */
        struct strbuf *content_type;
 
+       /*
+        * If non-NULL, and content_type above is non-NULL, returns
+        * the charset parameter from the content-type. If none is
+        * present, returns an empty string.
+        */
+       struct strbuf *charset;
+
        /*
         * If non-NULL, returns the URL we ended up at, including any
         * redirects we followed.
index 0bc6f7fae151ea364e9d141b30b8e68d544a4448..83a6ed2ac338d7fc82f3bed6fcb93ffe8dae5739 100644 (file)
@@ -23,9 +23,9 @@
  */
 
 #include "cache.h"
+#include "credential.h"
 #include "exec_cmd.h"
 #include "run-command.h"
-#include "prompt.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
@@ -946,12 +946,13 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha
 
 static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
 {
+       struct credential cred = CREDENTIAL_INIT;
        struct imap_store *ctx;
        struct imap *imap;
        char *arg, *rsp;
        int s = -1, preauth;
 
-       ctx = xcalloc(sizeof(*ctx), 1);
+       ctx = xcalloc(1, sizeof(*ctx));
 
        ctx->imap = imap = xcalloc(sizeof(*imap), 1);
        imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
@@ -1096,25 +1097,23 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
                }
 #endif
                imap_info("Logging in...\n");
-               if (!srvc->user) {
-                       fprintf(stderr, "Skipping server %s, no user\n", srvc->host);
-                       goto bail;
-               }
-               if (!srvc->pass) {
-                       struct strbuf prompt = STRBUF_INIT;
-                       strbuf_addf(&prompt, "Password (%s@%s): ", srvc->user, srvc->host);
-                       arg = git_getpass(prompt.buf);
-                       strbuf_release(&prompt);
-                       if (!*arg) {
-                               fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
-                               goto bail;
-                       }
-                       /*
-                        * getpass() returns a pointer to a static buffer.  make a copy
-                        * for long term storage.
-                        */
-                       srvc->pass = xstrdup(arg);
+               if (!srvc->user || !srvc->pass) {
+                       cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
+                       cred.host = xstrdup(srvc->host);
+
+                       if (srvc->user)
+                               cred.username = xstrdup(srvc->user);
+                       if (srvc->pass)
+                               cred.password = xstrdup(srvc->pass);
+
+                       credential_fill(&cred);
+
+                       if (!srvc->user)
+                               srvc->user = xstrdup(cred.username);
+                       if (!srvc->pass)
+                               srvc->pass = xstrdup(cred.password);
                }
+
                if (CAP(NOLOGIN)) {
                        fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
                        goto bail;
@@ -1153,10 +1152,18 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
                }
        } /* !preauth */
 
+       if (cred.username)
+               credential_approve(&cred);
+       credential_clear(&cred);
+
        ctx->prefix = "";
        return ctx;
 
 bail:
+       if (cred.username)
+               credential_reject(&cred);
+       credential_clear(&cred);
+
        imap_close_store(ctx);
        return NULL;
 }
index 41770929420da4cba934b0b86b9cd2f55f9cab31..cab16fafb5c2b7792c78d3a7fbace1f43ab099cc 100644 (file)
@@ -589,6 +589,12 @@ static int remove_file(struct merge_options *o, int clean,
                        return -1;
        }
        if (update_working_directory) {
+               if (ignore_case) {
+                       struct cache_entry *ce;
+                       ce = cache_file_exists(path, strlen(path), ignore_case);
+                       if (ce && ce_stage(ce) == 0)
+                               return 0;
+               }
                if (remove_path(path))
                        return -1;
        }
diff --git a/notes.c b/notes.c
index 5f07c0b7f28e09bd2f7969ea08b00b319a226920..5fe691dbcdfb9e3f9fa3c768f243234d5bb2e816 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -303,7 +303,7 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree,
                free(entry);
                return 0;
        }
-       new_node = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+       new_node = (struct int_node *) xcalloc(1, sizeof(struct int_node));
        ret = note_tree_insert(t, new_node, n + 1, l, GET_PTR_TYPE(*p),
                               combine_notes);
        if (ret)
@@ -443,7 +443,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
                if (len <= 20) {
                        type = PTR_TYPE_NOTE;
                        l = (struct leaf_node *)
-                               xcalloc(sizeof(struct leaf_node), 1);
+                               xcalloc(1, sizeof(struct leaf_node));
                        hashcpy(l->key_sha1, object_sha1);
                        hashcpy(l->val_sha1, entry.sha1);
                        if (len < 20) {
@@ -1003,7 +1003,7 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
        if (!combine_notes)
                combine_notes = combine_notes_concatenate;
 
-       t->root = (struct int_node *) xcalloc(sizeof(struct int_node), 1);
+       t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node));
        t->first_non_note = NULL;
        t->prev_non_note = NULL;
        t->ref = notes_ref ? xstrdup(notes_ref) : NULL;
index d01d851ce957c779ff98d243fea4476fa7a1008c..4f36c3204544c40ada8e6ce584deb6063b055518 100644 (file)
@@ -47,8 +47,8 @@ static void rehash_objects(struct packing_data *pdata)
        if (pdata->index_size < 1024)
                pdata->index_size = 1024;
 
-       pdata->index = xrealloc(pdata->index, sizeof(uint32_t) * pdata->index_size);
-       memset(pdata->index, 0, sizeof(int) * pdata->index_size);
+       free(pdata->index);
+       pdata->index = xcalloc(pdata->index_size, sizeof(*pdata->index));
 
        entry = pdata->objects;
 
index 5bd7c619803c8457a8c1a1a69a11758d34b49eac..5c8376e9789301b921188e3bf19fba85960a7967 100644 (file)
@@ -45,7 +45,7 @@ static void init_pack_revindex(void)
        if (!num)
                return;
        pack_revindex_hashsz = num * 11;
-       pack_revindex = xcalloc(sizeof(*pack_revindex), pack_revindex_hashsz);
+       pack_revindex = xcalloc(pack_revindex_hashsz, sizeof(*pack_revindex));
        for (p = packed_git; p; p = p->next) {
                num = pack_revindex_ix(p);
                num = - 1 - num;
diff --git a/pager.c b/pager.c
index 0cc75a8eee32a0195a74c6819a04745f341e45e3..8b5cbc56e4fd57a1b9d2cad3e668df4c508a7b17 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -64,7 +64,7 @@ void setup_pager(void)
 {
        const char *pager = git_pager(isatty(1));
 
-       if (!pager || pager_in_use())
+       if (!pager)
                return;
 
        /*
@@ -85,7 +85,7 @@ void setup_pager(void)
                int i = 0;
 
                if (!getenv("LESS"))
-                       env[i++] = "LESS=FRSX";
+                       env[i++] = "LESS=FRX";
                if (!getenv("LV"))
                        env[i++] = "LV=-c";
                env[i] = NULL;
index 3189676695bc173cf6475445cfb6365d08a37906..7940bc71af4c6dacd65d042091454c8fa83c53c7 100644 (file)
@@ -176,8 +176,8 @@ extern NORETURN void usage_msg_opt(const char *msg,
 
 extern int optbug(const struct option *opt, const char *reason);
 extern int opterror(const struct option *opt, const char *reason, int flags);
-#if defined(__GNUC__) && ! defined(__clang__)
-#define opterror(o,r,f) (opterror((o),(r),(f)), -1)
+#if defined(__GNUC__)
+#define opterror(o,r,f) (opterror((o),(r),(f)), const_error())
 #endif
 
 /*----- incremental advanced APIs -----*/
diff --git a/path.c b/path.c
index f9c5062427e7d8170a1e2e597fcf22ae517a865e..bc804a31b3382e689dd6ff5b4a727b109a691f7a 100644 (file)
--- a/path.c
+++ b/path.c
@@ -823,10 +823,3 @@ int daemon_avoid_alias(const char *p)
                }
        }
 }
-
-int offset_1st_component(const char *path)
-{
-       if (has_dos_drive_prefix(path))
-               return 2 + is_dir_sep(path[2]);
-       return is_dir_sep(path[0]);
-}
index 34f2869ab5995e3f9143af8cc2c46a4717ae8be8..664105357c0031e869b3348a09d91777a42c1cdf 100644 (file)
@@ -116,7 +116,7 @@ sub run_pager {
                return;
        }
        open STDIN, '<&', $rfd or fatal "Can't redirect stdin: $!";
-       $ENV{LESS} ||= 'FRSX';
+       $ENV{LESS} ||= 'FRX';
        $ENV{LV} ||= '-c';
        exec $pager or fatal "Can't run pager: $! ($pager)";
 }
index e1e2cad36d6d2d2773f05c6e69b0013ed8d01240..4f512876ceb20f538256bac30dd98288abe2d682 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1506,13 +1506,18 @@ void format_commit_message(const struct commit *commit,
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
+       /*
+        * convert a commit message to UTF-8 first
+        * as far as 'format_commit_item' assumes it in UTF-8
+        */
        context.message = logmsg_reencode(commit,
                                          &context.commit_encoding,
-                                         output_enc);
+                                         utf8);
 
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
 
+       /* then convert a commit message to an actual output encoding */
        if (output_enc) {
                if (same_encoding(utf8, output_enc))
                        output_enc = NULL;
index 0dd5084fe5c80fe9504cfe4ec4bff3722b733eaf..9ce8b53ccc85abcf27d7aff8f86a350ef2cd36b2 100644 (file)
@@ -41,7 +41,7 @@ static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
        struct complete_reflogs *reflogs =
-               xcalloc(sizeof(struct complete_reflogs), 1);
+               xcalloc(1, sizeof(struct complete_reflogs));
        reflogs->ref = xstrdup(ref);
        for_each_reflog_ent(ref, read_one_reflog, reflogs);
        if (reflogs->nr == 0) {
@@ -135,7 +135,7 @@ struct reflog_walk_info {
 
 void init_reflog_walk(struct reflog_walk_info** info)
 {
-       *info = xcalloc(sizeof(struct reflog_walk_info), 1);
+       *info = xcalloc(1, sizeof(struct reflog_walk_info));
 }
 
 int add_reflog_for_walk(struct reflog_walk_info *info,
@@ -199,7 +199,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                        = reflogs;
        }
 
-       commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
+       commit_reflog = xcalloc(1, sizeof(struct commit_reflog));
        if (recno < 0) {
                commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
                if (commit_reflog->recno < 0) {
@@ -242,7 +242,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
                return;
        }
 
-       commit->parents = xcalloc(sizeof(struct commit_list), 1);
+       commit->parents = xcalloc(1, sizeof(struct commit_list));
        commit->parents->item = commit_info->commit;
 }
 
diff --git a/refs.c b/refs.c
index 637a93d695a51872ad2b04289898421ca906af54..dc457742eade241ec6b0001580d1ce1a880fee31 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1622,6 +1622,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
 struct warn_if_dangling_data {
        FILE *fp;
        const char *refname;
+       const struct string_list *refnames;
        const char *msg_fmt;
 };
 
@@ -1636,8 +1637,12 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
                return 0;
 
        resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
-       if (!resolves_to || strcmp(resolves_to, d->refname))
+       if (!resolves_to
+           || (d->refname
+               ? strcmp(resolves_to, d->refname)
+               : !string_list_has_string(d->refnames, resolves_to))) {
                return 0;
+       }
 
        fprintf(d->fp, d->msg_fmt, refname);
        fputc('\n', d->fp);
@@ -1650,6 +1655,18 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 
        data.fp = fp;
        data.refname = refname;
+       data.refnames = NULL;
+       data.msg_fmt = msg_fmt;
+       for_each_rawref(warn_if_dangling_symref, &data);
+}
+
+void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+{
+       struct warn_if_dangling_data data;
+
+       data.fp = fp;
+       data.refname = NULL;
+       data.refnames = refnames;
        data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
 }
@@ -2010,7 +2027,6 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
                const char *ref, *it;
@@ -2019,12 +2035,9 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
-               if (!stat(git_path("logs/%s", path), &st) &&
-                   S_ISREG(st.st_mode))
+               if (reflog_exists(path))
                        it = path;
-               else if (strcmp(ref, path) &&
-                        !stat(git_path("logs/%s", ref), &st) &&
-                        S_ISREG(st.st_mode))
+               else if (strcmp(ref, path) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@ -2442,7 +2455,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
-static int repack_without_refs(const char **refnames, int n)
+int repack_without_refs(const char **refnames, int n)
 {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
@@ -2941,122 +2954,133 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        return 0;
 }
 
-static char *ref_msg(const char *line, const char *endp)
-{
-       const char *ep;
-       line += 82;
-       ep = memchr(line, '\n', endp - line);
-       if (!ep)
-               ep = endp;
-       return xmemdupz(line, ep - line);
+struct read_ref_at_cb {
+       const char *refname;
+       unsigned long at_time;
+       int cnt;
+       int reccnt;
+       unsigned char *sha1;
+       int found_it;
+
+       unsigned char osha1[20];
+       unsigned char nsha1[20];
+       int tz;
+       unsigned long date;
+       char **msg;
+       unsigned long *cutoff_time;
+       int *cutoff_tz;
+       int *cutoff_cnt;
+};
+
+static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
+{
+       struct read_ref_at_cb *cb = cb_data;
+
+       cb->reccnt++;
+       cb->tz = tz;
+       cb->date = timestamp;
+
+       if (timestamp <= cb->at_time || cb->cnt == 0) {
+               if (cb->msg)
+                       *cb->msg = xstrdup(message);
+               if (cb->cutoff_time)
+                       *cb->cutoff_time = timestamp;
+               if (cb->cutoff_tz)
+                       *cb->cutoff_tz = tz;
+               if (cb->cutoff_cnt)
+                       *cb->cutoff_cnt = cb->reccnt - 1;
+               /*
+                * we have not yet updated cb->[n|o]sha1 so they still
+                * hold the values for the previous record.
+                */
+               if (!is_null_sha1(cb->osha1)) {
+                       hashcpy(cb->sha1, nsha1);
+                       if (hashcmp(cb->osha1, nsha1))
+                               warning("Log for ref %s has gap after %s.",
+                                       cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
+               }
+               else if (cb->date == cb->at_time)
+                       hashcpy(cb->sha1, nsha1);
+               else if (hashcmp(nsha1, cb->sha1))
+                       warning("Log for ref %s unexpectedly ended on %s.",
+                               cb->refname, show_date(cb->date, cb->tz,
+                                                  DATE_RFC2822));
+               hashcpy(cb->osha1, osha1);
+               hashcpy(cb->nsha1, nsha1);
+               cb->found_it = 1;
+               return 1;
+       }
+       hashcpy(cb->osha1, osha1);
+       hashcpy(cb->nsha1, nsha1);
+       if (cb->cnt > 0)
+               cb->cnt--;
+       return 0;
+}
+
+static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
+                                 const char *email, unsigned long timestamp,
+                                 int tz, const char *message, void *cb_data)
+{
+       struct read_ref_at_cb *cb = cb_data;
+
+       if (cb->msg)
+               *cb->msg = xstrdup(message);
+       if (cb->cutoff_time)
+               *cb->cutoff_time = timestamp;
+       if (cb->cutoff_tz)
+               *cb->cutoff_tz = tz;
+       if (cb->cutoff_cnt)
+               *cb->cutoff_cnt = cb->reccnt;
+       hashcpy(cb->sha1, osha1);
+       if (is_null_sha1(cb->sha1))
+               hashcpy(cb->sha1, nsha1);
+       /* We just want the first entry */
+       return 1;
 }
 
 int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                unsigned char *sha1, char **msg,
                unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
-       const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
-       char *tz_c;
-       int logfd, tz, reccnt = 0;
-       struct stat st;
-       unsigned long date;
-       unsigned char logged_sha1[20];
-       void *log_mapped;
-       size_t mapsz;
+       struct read_ref_at_cb cb;
 
-       logfile = git_path("logs/%s", refname);
-       logfd = open(logfile, O_RDONLY, 0);
-       if (logfd < 0)
-               die_errno("Unable to read log '%s'", logfile);
-       fstat(logfd, &st);
-       if (!st.st_size)
-               die("Log %s is empty.", logfile);
-       mapsz = xsize_t(st.st_size);
-       log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);
-       logdata = log_mapped;
-       close(logfd);
+       memset(&cb, 0, sizeof(cb));
+       cb.refname = refname;
+       cb.at_time = at_time;
+       cb.cnt = cnt;
+       cb.msg = msg;
+       cb.cutoff_time = cutoff_time;
+       cb.cutoff_tz = cutoff_tz;
+       cb.cutoff_cnt = cutoff_cnt;
+       cb.sha1 = sha1;
+
+       for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
+
+       if (!cb.reccnt)
+               die("Log for %s is empty.", refname);
+       if (cb.found_it)
+               return 0;
+
+       for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
 
-       lastrec = NULL;
-       rec = logend = logdata + st.st_size;
-       while (logdata < rec) {
-               reccnt++;
-               if (logdata < rec && *(rec-1) == '\n')
-                       rec--;
-               lastgt = NULL;
-               while (logdata < rec && *(rec-1) != '\n') {
-                       rec--;
-                       if (*rec == '>')
-                               lastgt = rec;
-               }
-               if (!lastgt)
-                       die("Log %s is corrupt.", logfile);
-               date = strtoul(lastgt + 1, &tz_c, 10);
-               if (date <= at_time || cnt == 0) {
-                       tz = strtoul(tz_c, NULL, 10);
-                       if (msg)
-                               *msg = ref_msg(rec, logend);
-                       if (cutoff_time)
-                               *cutoff_time = date;
-                       if (cutoff_tz)
-                               *cutoff_tz = tz;
-                       if (cutoff_cnt)
-                               *cutoff_cnt = reccnt - 1;
-                       if (lastrec) {
-                               if (get_sha1_hex(lastrec, logged_sha1))
-                                       die("Log %s is corrupt.", logfile);
-                               if (get_sha1_hex(rec + 41, sha1))
-                                       die("Log %s is corrupt.", logfile);
-                               if (hashcmp(logged_sha1, sha1)) {
-                                       warning("Log %s has gap after %s.",
-                                               logfile, show_date(date, tz, DATE_RFC2822));
-                               }
-                       }
-                       else if (date == at_time) {
-                               if (get_sha1_hex(rec + 41, sha1))
-                                       die("Log %s is corrupt.", logfile);
-                       }
-                       else {
-                               if (get_sha1_hex(rec + 41, logged_sha1))
-                                       die("Log %s is corrupt.", logfile);
-                               if (hashcmp(logged_sha1, sha1)) {
-                                       warning("Log %s unexpectedly ended on %s.",
-                                               logfile, show_date(date, tz, DATE_RFC2822));
-                               }
-                       }
-                       munmap(log_mapped, mapsz);
-                       return 0;
-               }
-               lastrec = rec;
-               if (cnt > 0)
-                       cnt--;
-       }
-
-       rec = logdata;
-       while (rec < logend && *rec != '>' && *rec != '\n')
-               rec++;
-       if (rec == logend || *rec == '\n')
-               die("Log %s is corrupt.", logfile);
-       date = strtoul(rec + 1, &tz_c, 10);
-       tz = strtoul(tz_c, NULL, 10);
-       if (get_sha1_hex(logdata, sha1))
-               die("Log %s is corrupt.", logfile);
-       if (is_null_sha1(sha1)) {
-               if (get_sha1_hex(logdata + 41, sha1))
-                       die("Log %s is corrupt.", logfile);
-       }
-       if (msg)
-               *msg = ref_msg(logdata, logend);
-       munmap(log_mapped, mapsz);
-
-       if (cutoff_time)
-               *cutoff_time = date;
-       if (cutoff_tz)
-               *cutoff_tz = tz;
-       if (cutoff_cnt)
-               *cutoff_cnt = reccnt;
        return 1;
 }
 
+int reflog_exists(const char *refname)
+{
+       struct stat st;
+
+       return !lstat(git_path("logs/%s", refname), &st) &&
+               S_ISREG(st.st_mode);
+}
+
+int delete_reflog(const char *refname)
+{
+       return remove_path(git_path("logs/%s", refname));
+}
+
 static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
 {
        unsigned char osha1[20], nsha1[20];
diff --git a/refs.h b/refs.h
index 0f08def21020a166902de169dd46c63c85fdfd0e..4e3050d976dcdae2e98a50b75e065ea086b9cb25 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -77,6 +77,7 @@ static inline const char *has_glob_specials(const char *pattern)
 extern int for_each_rawref(each_ref_fn, void *);
 
 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
+extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames);
 
 /*
  * Lock the packed-refs file for writing.  Flags is passed to
@@ -120,6 +121,8 @@ extern void rollback_packed_refs(void);
  */
 int pack_refs(unsigned int flags);
 
+extern int repack_without_refs(const char **refnames, int n);
+
 extern int ref_exists(const char *);
 
 /*
@@ -161,6 +164,12 @@ extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                       unsigned char *sha1, char **msg,
                       unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
+/** Check if a particular reflog exists */
+extern int reflog_exists(const char *refname);
+
+/** Delete a reflog */
+extern int delete_reflog(const char *refname);
+
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
index 52c2d96ce6183ca8b3498794304fa8dc98f8f42a..4493b389ded6a814bd8c40481f3d06855c963bd7 100644 (file)
@@ -194,19 +194,19 @@ static void free_discovery(struct discovery *d)
        }
 }
 
-static int show_http_message(struct strbuf *type, struct strbuf *msg)
+static int show_http_message(struct strbuf *type, struct strbuf *charset,
+                            struct strbuf *msg)
 {
        const char *p, *eol;
 
        /*
         * We only show text/plain parts, as other types are likely
         * to be ugly to look at on the user's terminal.
-        *
-        * TODO should handle "; charset=XXX", and re-encode into
-        * logoutputencoding
         */
-       if (strcasecmp(type->buf, "text/plain"))
+       if (strcmp(type->buf, "text/plain"))
                return -1;
+       if (charset->len)
+               strbuf_reencode(msg, charset->buf, get_log_output_encoding());
 
        strbuf_trim(msg);
        if (!msg->len)
@@ -225,6 +225,7 @@ static struct discovery* discover_refs(const char *service, int for_push)
 {
        struct strbuf exp = STRBUF_INIT;
        struct strbuf type = STRBUF_INIT;
+       struct strbuf charset = STRBUF_INIT;
        struct strbuf buffer = STRBUF_INIT;
        struct strbuf refs_url = STRBUF_INIT;
        struct strbuf effective_url = STRBUF_INIT;
@@ -249,6 +250,7 @@ static struct discovery* discover_refs(const char *service, int for_push)
 
        memset(&options, 0, sizeof(options));
        options.content_type = &type;
+       options.charset = &charset;
        options.effective_url = &effective_url;
        options.base_url = &url;
        options.no_cache = 1;
@@ -259,13 +261,13 @@ static struct discovery* discover_refs(const char *service, int for_push)
        case HTTP_OK:
                break;
        case HTTP_MISSING_TARGET:
-               show_http_message(&type, &buffer);
+               show_http_message(&type, &charset, &buffer);
                die("repository '%s' not found", url.buf);
        case HTTP_NOAUTH:
-               show_http_message(&type, &buffer);
+               show_http_message(&type, &charset, &buffer);
                die("Authentication failed for '%s'", url.buf);
        default:
-               show_http_message(&type, &buffer);
+               show_http_message(&type, &charset, &buffer);
                die("unable to access '%s': %s", url.buf, curl_errorstr);
        }
 
@@ -310,6 +312,7 @@ static struct discovery* discover_refs(const char *service, int for_push)
        strbuf_release(&refs_url);
        strbuf_release(&exp);
        strbuf_release(&type);
+       strbuf_release(&charset);
        strbuf_release(&effective_url);
        strbuf_release(&buffer);
        last_discovery = last;
index eea2c8de45052e3e3a59fb150eef3f54c6086800..0e9459cc0675d2f232b1c1fda62080d04aa88ec5 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -523,7 +523,7 @@ static void free_refspecs(struct refspec *refspec, int nr_refspec)
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
        int i;
-       struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
+       struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));
 
        for (i = 0; i < nr_refspec; i++) {
                size_t llen;
index 71e233742331a435edeadacc7c6a52226bb18b7a..3818b4628d0b5b4fa74428decaa940bef2804856 100644 (file)
@@ -1633,6 +1633,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+           starts_with(arg, "--exclude=") ||
            starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
            starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
index 75abc478c6da75471e5fdbb55c74efdfcf3ad160..be07d4ad335ba6df4653239b7ccd6930c91c9879 100644 (file)
@@ -279,6 +279,9 @@ int start_command(struct child_process *cmd)
        int failed_errno;
        char *str;
 
+       if (!cmd->argv)
+               cmd->argv = cmd->args.argv;
+
        /*
         * In case of errors we must keep the promise to close FDs
         * that have been passed in via ->in and ->out.
@@ -328,6 +331,7 @@ int start_command(struct child_process *cmd)
 fail_pipe:
                        error("cannot create %s pipe for %s: %s",
                                str, cmd->argv[0], strerror(failed_errno));
+                       argv_array_clear(&cmd->args);
                        errno = failed_errno;
                        return -1;
                }
@@ -519,6 +523,7 @@ int start_command(struct child_process *cmd)
                        close_pair(fderr);
                else if (cmd->err)
                        close(cmd->err);
+               argv_array_clear(&cmd->args);
                errno = failed_errno;
                return -1;
        }
@@ -543,7 +548,9 @@ int start_command(struct child_process *cmd)
 
 int finish_command(struct child_process *cmd)
 {
-       return wait_or_whine(cmd->pid, cmd->argv[0]);
+       int ret = wait_or_whine(cmd->pid, cmd->argv[0]);
+       argv_array_clear(&cmd->args);
+       return ret;
 }
 
 int run_command(struct child_process *cmd)
index 3653bfa6e123ca8497571a2ca1c47200c1b544a2..ea73de309bc65c3d00bb34ad84824ba72d85cbfe 100644 (file)
@@ -5,8 +5,11 @@
 #include <pthread.h>
 #endif
 
+#include "argv-array.h"
+
 struct child_process {
        const char **argv;
+       struct argv_array args;
        pid_t pid;
        /*
         * Using .in, .out, .err:
index 3e9f55f1bb19a33908be174b2fa463f8142d4cae..34d527f6708fe242f75b7fa0fa98e36a5c96a3be 100644 (file)
@@ -1437,19 +1437,23 @@ static int open_sha1_file(const unsigned char *sha1)
 {
        int fd;
        struct alternate_object_database *alt;
+       int most_interesting_errno;
 
        fd = git_open_noatime(sha1_file_name(sha1));
        if (fd >= 0)
                return fd;
+       most_interesting_errno = errno;
 
        prepare_alt_odb();
-       errno = ENOENT;
        for (alt = alt_odb_list; alt; alt = alt->next) {
                fill_sha1_path(alt->name, sha1);
                fd = git_open_noatime(alt->base);
                if (fd >= 0)
                        return fd;
+               if (most_interesting_errno == ENOENT)
+                       most_interesting_errno = errno;
        }
+       errno = most_interesting_errno;
        return -1;
 }
 
index d1125f5c528b680f915bb2fbf54231cc8c3b2ec0..7f9dc229fbc82d005b5c2b417c2af2fc9f6b497a 100644 (file)
@@ -30,7 +30,7 @@ int recv_sideband(const char *me, int in_stream, int out)
 
        memcpy(buf, PREFIX, pf);
        term = getenv("TERM");
-       if (term && strcmp(term, "dumb"))
+       if (isatty(2) && term && strcmp(term, "dumb"))
                suffix = ANSI_SUFFIX;
        else
                suffix = DUMB_SUFFIX;
index 4d31567a1a6a67ae9697816d78da442f87ab5803..ac62982e672c22457468f601b35716dc9a52a81c 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "refs.h"
+#include "utf8.h"
 
 int starts_with(const char *str, const char *prefix)
 {
@@ -99,6 +100,29 @@ void strbuf_ltrim(struct strbuf *sb)
        sb->buf[sb->len] = '\0';
 }
 
+int strbuf_reencode(struct strbuf *sb, const char *from, const char *to)
+{
+       char *out;
+       int len;
+
+       if (same_encoding(from, to))
+               return 0;
+
+       out = reencode_string_len(sb->buf, sb->len, to, from, &len);
+       if (!out)
+               return -1;
+
+       strbuf_attach(sb, out, len, len);
+       return 0;
+}
+
+void strbuf_tolower(struct strbuf *sb)
+{
+       char *p = sb->buf, *end = sb->buf + sb->len;
+       for (; p < end; p++)
+               *p = tolower(*p);
+}
+
 struct strbuf **strbuf_split_buf(const char *str, size_t slen,
                                 int terminator, int max)
 {
@@ -563,3 +587,16 @@ int fprintf_ln(FILE *fp, const char *fmt, ...)
                return -1;
        return ret + 1;
 }
+
+char *xstrdup_tolower(const char *string)
+{
+       char *result;
+       size_t len, i;
+
+       len = strlen(string);
+       result = xmalloc(len + 1);
+       for (i = 0; i < len; i++)
+               result[i] = tolower(string[i]);
+       result[i] = '\0';
+       return result;
+}
index 39c14cfa384c5154abd96899a7c02417295f8555..e9ad03eabe72dc2ee6e7e0086baca71d343264ce 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -45,6 +45,8 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len)
 extern void strbuf_trim(struct strbuf *);
 extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
+extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
+extern void strbuf_tolower(struct strbuf *sb);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
 /*
@@ -183,4 +185,6 @@ extern int printf_ln(const char *fmt, ...);
 __attribute__((format (printf,2,3)))
 extern int fprintf_ln(FILE *fp, const char *fmt, ...);
 
+char *xstrdup_tolower(const char *);
+
 #endif /* STRBUF_H */
index caeeb9dedc46940132dceb65f2ea976709945e2d..8a9d4995f5197f2c841dd1c71107e3045c1790ce 100644 (file)
--- a/t/README
+++ b/t/README
@@ -596,6 +596,27 @@ library for your script to use.
                ...
        '
 
+ - test_write_lines <lines>
+
+   Write <lines> on standard output, one line per argument.
+   Useful to prepare multi-line files in a compact form.
+
+   Example:
+
+       test_write_lines a b c d e f g >foo
+
+   Is a more compact equivalent of:
+       cat >foo <<-EOF
+       a
+       b
+       c
+       d
+       e
+       f
+       g
+       EOF
+
+
  - test_pause
 
        This command is useful for writing and debugging tests and must be
index 45971f43b77142fe1bb90262974c83c786cfe43d..b170cbc0452ca1b5a624f5cf330d420c397cb760 100755 (executable)
@@ -16,12 +16,12 @@ sub err {
 
 while (<>) {
        chomp;
-       /^\s*sed\s+-i/ and err 'sed -i is not portable';
-       /^\s*echo\s+-n/ and err 'echo -n is not portable (please use printf)';
+       /\bsed\s+-i/ and err 'sed -i is not portable';
+       /\becho\s+-n/ and err 'echo -n is not portable (please use printf)';
        /^\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)';
+       /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+       /\bexport\s+[A-Za-z0-9_]*=/ 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 252cbf163bcff90aabe39a48219e00565f577ffe..272fceef96c06e6e104d6341f36b1764e7d0c8db 100644 (file)
@@ -37,6 +37,11 @@ then
        test_done
 fi
 
+if ! test_have_prereq SANITY; then
+       test_skip_or_die $GIT_TEST_HTTPD \
+               "Cannot run httpd tests as root"
+fi
+
 HTTPD_PARA=""
 
 for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
@@ -105,10 +110,15 @@ else
                "Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
+install_script () {
+       write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
+}
+
 prepare_httpd() {
        mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
-       cp "$TEST_PATH"/broken-smart-http.sh "$HTTPD_ROOT_PATH"
+       install_script broken-smart-http.sh
+       install_script error.sh
 
        ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
index 3a03e8263db7e1c632bf0552ec94ae6834de4bf9..b384d7993545365c17f3c8bded756b4e74faf794 100644 (file)
@@ -97,12 +97,16 @@ Alias /auth/dumb/ www/auth/dumb/
 </LocationMatch>
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
+ScriptAlias /error/ error.sh/
 <Directory ${GIT_EXEC_PATH}>
        Options FollowSymlinks
 </Directory>
 <Files broken-smart-http.sh>
        Options ExecCGI
 </Files>
+<Files error.sh>
+  Options ExecCGI
+</Files>
 <Files ${GIT_EXEC_PATH}/git-http-backend>
        Options ExecCGI
 </Files>
old mode 100755 (executable)
new mode 100644 (file)
index f7ebfff..82cc610
@@ -1,4 +1,3 @@
-#!/bin/sh
 printf "Content-Type: text/%s\n" "html"
 echo
 printf "%s\n" "001e# service=git-upload-pack"
diff --git a/t/lib-httpd/error.sh b/t/lib-httpd/error.sh
new file mode 100755 (executable)
index 0000000..eafc9d2
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+printf "Status: 500 Intentional Breakage\n"
+
+printf "Content-Type: "
+charset=iso-8859-1
+case "$PATH_INFO" in
+*html*)
+       printf "text/html"
+       ;;
+*text*)
+       printf "text/plain"
+       ;;
+*charset*)
+       printf "text/plain; charset=utf-8"
+       charset=utf-8
+       ;;
+*utf16*)
+       printf "text/plain; charset=utf-16"
+       charset=utf-16
+       ;;
+esac
+printf "\n"
+
+printf "\n"
+printf "this is the error message\n" |
+iconv -f us-ascii -t $charset
index 63beb99828cc17d2ec8218a6470b62c02a78806b..5ef5ad3db4f6c307e6bcf71f57dcdc4deaafc082 100755 (executable)
@@ -806,4 +806,27 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' '
        test_cmp err.expect err
 '
 
+test_expect_success NOT_MINGW,NOT_CYGWIN 'correct handling of backslashes' '
+       rm -rf whitespace &&
+       mkdir whitespace &&
+       >"whitespace/trailing 1  " &&
+       >"whitespace/trailing 2 \\\\" &&
+       >"whitespace/trailing 3 \\\\" &&
+       >"whitespace/trailing 4   \\ " &&
+       >"whitespace/trailing 5 \\ \\ " &&
+       >"whitespace/trailing 6 \\a\\" &&
+       >whitespace/untracked &&
+       echo "whitespace/trailing 1 \\    " >ignore  &&
+       echo "whitespace/trailing 2 \\\\\\\\\\\\\\\\" >>ignore &&
+       echo "whitespace/trailing 3 \\\\\\\\\\\\\\\\ " >>ignore &&
+       echo "whitespace/trailing 4   \\\\\\\\\\\\    " >>ignore &&
+       echo "whitespace/trailing 5 \\\\\\\\ \\\\\\\\\\\\   " >>ignore &&
+       echo "whitespace/trailing 6 \\\\\\\\a\\\\\\\\" >>ignore &&
+       echo whitespace/untracked >expect &&
+       >err.expect &&
+       git ls-files -o -X ignore whitespace >actual 2>err &&
+       test_cmp expect actual &&
+       test_cmp err.expect err
+'
+
 test_done
index 4e2459afc549d5d2985024e8c23bf5fa117d570a..0218e96366ddd8659d5a78b42ffcfd415013114c 100755 (executable)
@@ -235,7 +235,7 @@ test_expect_success \
        'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
         test '"$B"' = $(cat o) &&
-        test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
+        test "warning: Log for ref '"$m has gap after $gd"'." = "$(cat e)"'
 test_expect_success \
        'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
        'rm -f o e &&
@@ -253,7 +253,7 @@ test_expect_success \
        'rm -f o e &&
         git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
         test '"$D"' = $(cat o) &&
-        test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
+        test "warning: Log for ref '"$m unexpectedly ended on $ld"'." = "$(cat e)"'
 
 
 rm -f .git/$m .git/logs/$m expect
index 236b13a3ab27f54808fa88574738747e3c3936b1..8cab06f90a52b82518aca6f7baa0530eb77ab986 100755 (executable)
@@ -245,4 +245,12 @@ test_expect_success 'gc.reflogexpire=false' '
 
 '
 
+test_expect_success 'checkout should not delete log for packed ref' '
+       test $(git reflog master | wc -l) = 4 &&
+       git branch foo &&
+       git pack-refs --all &&
+       git checkout foo &&
+       test $(git reflog master | wc -l) = 4
+'
+
 test_done
index 178694ee63937a9ec1f8d182d54441c2d20bd4a3..1978947c4196fb666d55395bedc53ae149b6d13d 100755 (executable)
@@ -121,7 +121,7 @@ test_expect_success 'merge my-side@{u} records the correct name' '
        git branch -D new ;# can fail but is ok
        git branch -t new my-side@{u} &&
        git merge -s ours new@{u} &&
-       git show -s --pretty=format:%s >actual &&
+       git show -s --pretty=tformat:%s >actual &&
        echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect &&
        test_cmp expect actual
 )
index fe2fb17102cdc17168ae2d683d7d51a0b3e3f3bc..1bafb9098c7fae33969e9231fbdee1c0370830a1 100755 (executable)
@@ -29,6 +29,10 @@ test_expect_success 'update-index -h with corrupt index' '
        test_i18ngrep "[Uu]sage: git update-index" broken/usage
 '
 
+test_expect_success '--cacheinfo complains of missing arguments' '
+       test_must_fail git update-index --cacheinfo
+'
+
 test_expect_success '--cacheinfo does not accept blob null sha1' '
        echo content >file &&
        git add file &&
index 5fd7bbb65244bdf6470508b6bc79aef58e79e86d..4029c9c8c06d24887e7d9fa7c97c2c8c5826eb42 100755 (executable)
@@ -14,7 +14,11 @@ test_description='merge-recursive options
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
-test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
+if test_have_prereq GREP_STRIPS_CR
+then
+       GREP_OPTIONS=-U
+       export GREP_OPTIONS
+fi
 
 test_expect_success 'setup' '
        conflict_hunks () {
index c0023a5b4ff6ef60f5d77e9e54a64adf387bce6e..8197ed29a9ecedb43679200f1485a7b50984fdff 100755 (executable)
@@ -67,6 +67,14 @@ test_expect_success 'setup' '
 SHELL=
 export SHELL
 
+test_expect_success 'rebase --keep-empty' '
+       git checkout -b emptybranch master &&
+       git commit --allow-empty -m "empty" &&
+       git rebase --keep-empty -i HEAD~2 &&
+       git log --oneline >actual &&
+       test_line_count = 6 actual
+'
+
 test_expect_success 'rebase -i with the exec command' '
        git checkout master &&
        (
index 90eb26493cd309da34687dffc939166069cd88c6..d783f03d3fc581eed08d8d2593e20ced3cbae200 100755 (executable)
@@ -167,4 +167,19 @@ testrebase "" .git/rebase-apply
 testrebase " --merge" .git/rebase-merge
 testrebase " --interactive" .git/rebase-merge
 
+test_expect_success 'abort rebase -i with --autostash' '
+       test_when_finished "git reset --hard" &&
+       echo uncommited-content >file0 &&
+       (
+               write_script abort-editor.sh <<-\EOF &&
+                       echo >"$1"
+               EOF
+               test_set_editor "$(pwd)/abort-editor.sh" &&
+               test_must_fail git rebase -i --autostash HEAD^ &&
+               rm -f abort-editor.sh
+       ) &&
+       echo uncommited-content >expected &&
+       test_cmp expected file0
+'
+
 test_done
index 96941e94cc3b50d0f65a441316e577dee2adc7e2..831935665e012c04bbe4c57feee92c0310df5c04 100755 (executable)
@@ -140,6 +140,16 @@ test_expect_success "Add long precomposed filename" '
        git add * &&
        git commit -m "Long filename"
 '
+
+test_expect_failure 'handle existing decomposed filenames' '
+       echo content >"verbatim.$Adiarnfd" &&
+       git -c core.precomposeunicode=false add "verbatim.$Adiarnfd" &&
+       git commit -m "existing decomposed file" &&
+       >expect &&
+       git ls-files --exclude-standard -o "verbatim*" >untracked &&
+       test_cmp expect untracked
+'
+
 # Test if the global core.precomposeunicode stops autosensing
 # Must be the last test case
 test_expect_success "respect git config --global core.precomposeunicode" '
index 282bee4a54c924b8168b6a28dfdbeb7ab300d7b7..256affce89517a707f9054d2c9643db4f5398832 100755 (executable)
@@ -762,6 +762,67 @@ test_expect_success 'format-patch --signature="" suppresses signatures' '
        ! grep "^-- \$" output
 '
 
+test_expect_success 'prepare mail-signature input' '
+       cat >mail-signature <<-\EOF
+
+       Test User <test.email@kernel.org>
+       http://git.kernel.org/cgit/git/git.git
+
+       git.kernel.org/?p=git/git.git;a=summary
+
+       EOF
+'
+
+test_expect_success '--signature-file=file works' '
+       git format-patch --stdout --signature-file=mail-signature -1 >output &&
+       check_patch output &&
+       sed -e "1,/^-- \$/d" <output >actual &&
+       {
+               cat mail-signature && echo
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'format.signaturefile works' '
+       test_config format.signaturefile mail-signature &&
+       git format-patch --stdout -1 >output &&
+       check_patch output &&
+       sed -e "1,/^-- \$/d" <output >actual &&
+       {
+               cat mail-signature && echo
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--no-signature suppresses format.signaturefile ' '
+       test_config format.signaturefile mail-signature &&
+       git format-patch --stdout --no-signature -1 >output &&
+       check_patch output &&
+       ! grep "^-- \$" output
+'
+
+test_expect_success '--signature-file overrides format.signaturefile' '
+       cat >other-mail-signature <<-\EOF
+       Use this other signature instead of mail-signature.
+       EOF
+       test_config format.signaturefile mail-signature &&
+       git format-patch --stdout \
+                       --signature-file=other-mail-signature -1 >output &&
+       check_patch output &&
+       sed -e "1,/^-- \$/d" <output >actual &&
+       {
+               cat other-mail-signature && echo
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success '--signature overrides format.signaturefile' '
+       test_config format.signaturefile mail-signature &&
+       git format-patch --stdout --signature="my sig" -1 >output &&
+       check_patch output &&
+       grep "my sig" output
+'
+
 test_expect_success TTY 'format-patch --stdout paginates' '
        rm -f pager_used &&
        test_terminal env GIT_PAGER="wc >pager_used" git format-patch --stdout --all &&
index 9d9498bd95c525b008bd3719c2debf87c66ebef2..23c0e357a765f00d807938fccf81a250c371ec4f 100755 (executable)
@@ -28,4 +28,15 @@ test_expect_success 'diff-files does not examine assume-unchanged entries' '
        test -z "$(git diff-files -- one)"
 '
 
+test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits' '
+       echo content >exec &&
+       chmod +x exec &&
+       git add exec &&
+       git commit -m exec &&
+       git update-index --assume-unchanged exec &&
+       >expect &&
+       git diff-files --find-copies-harder -- exec >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 1751c8330709c56a4b7019513e30434741847b95..463d63bde0f72bb5add9edff762e38f0b5b85ae5 100755 (executable)
@@ -11,6 +11,9 @@ This test tries to verify the sanity of the --submodule option of git diff.
 
 . ./test-lib.sh
 
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
 # 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")
@@ -23,8 +26,8 @@ add_file () {
                        echo "$name" >"$name" &&
                        git add "$name" &&
                        test_tick &&
-                       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"
+                       msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding) &&
+                       git -c "i18n.commitEncoding=$test_encoding" commit -m "$msg_added_iso88591"
                done >/dev/null &&
                git rev-parse --short --verify HEAD
        )
index 42866992cfe65657019dced508d3490b3851a6c7..97fcb31d6eb516b195e9e26a8716dbf484fcc616 100755 (executable)
@@ -188,4 +188,10 @@ test_expect_success 'shortlog ignores commits with missing authors' '
        test_cmp expect actual
 '
 
+test_expect_success 'shortlog with revision pseudo options' '
+       git shortlog --all &&
+       git shortlog --branches &&
+       git shortlog --exclude=refs/heads/m* --all
+'
+
 test_done
index 7940f6f0b952b5accd3f1263f08e131a018de140..baa9d3c82e8a54dc7501d90bc9da0527f2a72b2e 100755 (executable)
@@ -5,27 +5,44 @@ test_description='git patch-id'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-       test_commit initial foo a &&
-       test_commit first foo b &&
-       git checkout -b same HEAD^ &&
-       test_commit same-msg foo b &&
-       git checkout -b notsame HEAD^ &&
-       test_commit notsame-msg foo c
+       as="a a a a a a a a" && # eight a
+       test_write_lines $as >foo &&
+       test_write_lines $as >bar &&
+       git add foo bar &&
+       git commit -a -m initial &&
+       test_write_lines $as b >foo &&
+       test_write_lines $as b >bar &&
+       git commit -a -m first &&
+       git checkout -b same master &&
+       git commit --amend -m same-msg &&
+       git checkout -b notsame master &&
+       echo c >foo &&
+       echo c >bar &&
+       git commit --amend -a -m notsame-msg &&
+       test_write_lines bar foo >bar-then-foo &&
+       test_write_lines foo bar >foo-then-bar
 '
 
 test_expect_success 'patch-id output is well-formed' '
-       git log -p -1 | git patch-id > output &&
+       git log -p -1 | git patch-id >output &&
        grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
 '
 
+#calculate patch id. Make sure output is not empty.
 calc_patch_id () {
-       git patch-id |
-               sed "s# .*##" > patch-id_"$1"
+       name="$1"
+       shift
+       git patch-id "$@" |
+       sed "s/ .*//" >patch-id_"$name" &&
+       test_line_count -gt 0 patch-id_"$name"
+}
+
+get_top_diff () {
+       git log -p -1 "$@" -O bar-then-foo --
 }
 
 get_patch_id () {
-       git log -p -1 "$1" | git patch-id |
-               sed "s# .*##" > patch-id_"$1"
+       get_top_diff "$1" | calc_patch_id "$@"
 }
 
 test_expect_success 'patch-id detects equality' '
@@ -56,6 +73,69 @@ test_expect_success 'whitespace is irrelevant in footer' '
        test_cmp patch-id_master patch-id_same
 '
 
+cmp_patch_id () {
+       if
+               test "$1" = "relevant"
+       then
+               ! test_cmp patch-id_"$2" patch-id_"$3"
+       else
+               test_cmp patch-id_"$2" patch-id_"$3"
+       fi
+}
+
+test_patch_id_file_order () {
+       relevant="$1"
+       shift
+       name="order-${1}-$relevant"
+       shift
+       get_top_diff "master" | calc_patch_id "$name" "$@" &&
+       git checkout same &&
+       git format-patch -1 --stdout -O foo-then-bar |
+               calc_patch_id "ordered-$name" "$@" &&
+       cmp_patch_id $relevant "$name" "ordered-$name"
+
+}
+
+# combined test for options: add more tests here to make them
+# run with all options
+test_patch_id () {
+       test_patch_id_file_order "$@"
+}
+
+# small tests with detailed diagnostic for basic options.
+test_expect_success 'file order is irrelevant with --stable' '
+       test_patch_id_file_order irrelevant --stable --stable
+'
+
+test_expect_success 'file order is relevant with --unstable' '
+       test_patch_id_file_order relevant --unstable --unstable
+'
+
+#Now test various option combinations.
+test_expect_success 'default is unstable' '
+       test_patch_id relevant default
+'
+
+test_expect_success 'patchid.stable = true is stable' '
+       test_config patchid.stable true &&
+       test_patch_id irrelevant patchid.stable=true
+'
+
+test_expect_success 'patchid.stable = false is unstable' '
+       test_config patchid.stable false &&
+       test_patch_id relevant patchid.stable=false
+'
+
+test_expect_success '--unstable overrides patchid.stable = true' '
+       test_config patchid.stable true &&
+       test_patch_id relevant patchid.stable=true--unstable --unstable
+'
+
+test_expect_success '--stable overrides patchid.stable = false' '
+       test_config patchid.stable false &&
+       test_patch_id irrelevant patchid.stable=false--stable --stable
+'
+
 test_expect_success 'patch-id supports git-format-patch MIME output' '
        get_patch_id master &&
        git checkout same &&
index 2a6278bb333d2843ee617ff69227feae115aefea..c84ec9ae6139be752831365244f5580e46138884 100755 (executable)
@@ -7,6 +7,9 @@
 test_description='Test pretty formats'
 . ./test-lib.sh
 
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
 sample_utf8_part=$(printf "f\303\244ng")
 
 commit_msg () {
@@ -27,8 +30,8 @@ test_expect_success 'set up basic repos' '
        >bar &&
        git add foo &&
        test_tick &&
-       git config i18n.commitEncoding iso8859-1 &&
-       git commit -m "$(commit_msg iso8859-1)" &&
+       git config i18n.commitEncoding $test_encoding &&
+       git commit -m "$(commit_msg $test_encoding)" &&
        git add bar &&
        test_tick &&
        git commit -m "add bar" &&
@@ -56,8 +59,8 @@ 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 &&
+test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
+       git config i18n.logOutputEncoding $test_encoding &&
        git log --oneline >expected-s &&
        git log --pretty="tformat:%h %s" >actual-s &&
        git config --unset i18n.logOutputEncoding &&
@@ -141,9 +144,7 @@ test_expect_success 'setup more commits' '
 '
 
 test_expect_success 'left alignment formatting' '
-       git log --pretty="format:%<(40)%s" >actual &&
-       # complete the incomplete line at the end
-       echo >>actual &&
+       git log --pretty="tformat:%<(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 message two                            Z
 message one                            Z
@@ -153,10 +154,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two                            Z
+message one                            Z
+add bar                                Z
+$(commit_msg)                    Z
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%h %<|(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 $head1 message two                    Z
 $head2 message one                    Z
@@ -166,10 +176,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting at the nth column. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %<|(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1 message two                    Z
+$head2 message one                    Z
+$head3 add bar                        Z
+$head4 $(commit_msg)            Z
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%<(1)%s" >actual &&
        cat <<EOF >expected &&
 message two
 message one
@@ -179,10 +198,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting with no padding. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(1)%s" >actual &&
+       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%<(10,trunc)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 message ..
 message ..
@@ -192,10 +220,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting with trunc. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message ..
+message ..
+add bar  Z
+initial...
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 ..sage two
 ..sage one
@@ -205,10 +242,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting with ltrunc. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,ltrunc)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+..sage two
+..sage one
+add bar  Z
+..${sample_utf8_part}lich
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 mess.. two
 mess.. one
@@ -218,10 +264,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'left alignment formatting with mtrunc. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,mtrunc)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+mess.. two
+mess.. one
+add bar  Z
+init..lich
+EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'right alignment formatting' '
-       git log --pretty="format:%>(40)%s" >actual &&
-       # complete the incomplete line at the end
-       echo >>actual &&
+       git log --pretty="tformat:%>(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 Z                            message two
 Z                            message one
@@ -231,10 +286,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'right alignment formatting. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+Z                            message two
+Z                            message one
+Z                                add bar
+Z                    $(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%h %>|(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 $head1                      message two
 $head2                      message one
@@ -244,10 +308,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'right alignment formatting at the nth column. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %>|(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1                      message two
+$head2                      message one
+$head3                          add bar
+$head4              $(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%>(1)%s" >actual &&
        cat <<EOF >expected &&
 message two
 message one
@@ -257,10 +330,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'right alignment formatting with no padding. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%>(1)%s" >actual &&
+       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'center alignment formatting' '
-       git log --pretty="format:%><(40)%s" >actual &&
-       # complete the incomplete line at the end
-       echo >>actual &&
+       git log --pretty="tformat:%><(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 Z             message two              Z
 Z             message one              Z
@@ -270,10 +352,18 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'center alignment formatting. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+Z             message two              Z
+Z             message one              Z
+Z               add bar                Z
+Z         $(commit_msg)          Z
+EOF
+       test_cmp expected actual
+'
 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 &&
+       git log --pretty="tformat:%h %><|(40)%s" >actual &&
        qz_to_tab_space <<EOF >expected &&
 $head1           message two          Z
 $head2           message one          Z
@@ -283,10 +373,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'center alignment formatting at the nth column. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%h %><|(40)%s" >actual &&
+       qz_to_tab_space <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+$head1           message two          Z
+$head2           message one          Z
+$head3             add bar            Z
+$head4       $(commit_msg)      Z
+EOF
+       test_cmp expected actual
+'
+
 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 &&
+       git log --pretty="tformat:%><(1)%s" >actual &&
        cat <<EOF >expected &&
 message two
 message one
@@ -296,11 +395,23 @@ EOF
        test_cmp expected actual
 '
 
+# save HEAD's SHA-1 digest (with no abbreviations) to use it below
+# as far as the next test amends HEAD
+old_head1=$(git rev-parse --verify HEAD~0)
+test_expect_success 'center alignment formatting with no padding. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%><(1)%s" >actual &&
+       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'left/right alignment formatting with stealing' '
        git commit --amend -m short --author "long long long <long@me.com>" &&
-       git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
-       # complete the incomplete line at the end
-       echo >>actual &&
+       git log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
        cat <<EOF >expected &&
 short long  long long
 message ..   A U Thor
@@ -309,6 +420,20 @@ initial...   A U Thor
 EOF
        test_cmp expected actual
 '
+test_expect_success 'left/right alignment formatting with stealing. i18n.logOutputEncoding' '
+       git -c i18n.logOutputEncoding=$test_encoding log --pretty="tformat:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
+       cat <<EOF | iconv -f utf-8 -t $test_encoding >expected &&
+short long  long long
+message ..   A U Thor
+add bar      A U Thor
+initial...   A U Thor
+EOF
+       test_cmp expected actual
+'
+
+# get new digests (with no abbreviations)
+head1=$(git rev-parse --verify HEAD~0) &&
+head2=$(git rev-parse --verify HEAD~1) &&
 
 test_expect_success 'log decoration properly follows tag chain' '
        git tag -a tag1 -m tag1 &&
@@ -317,9 +442,9 @@ test_expect_success 'log decoration properly follows tag chain' '
        git commit --amend -m shorter &&
        git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
        cat <<EOF >expected &&
-6a908c10688b2503073c39c9ba26322c73902bb5  (tag: refs/tags/tag2)
-9f716384d92283fb915a4eee5073f030638e05f9  (tag: refs/tags/message-one)
-b87e4cccdb77336ea79d89224737be7ea8e95367  (tag: refs/tags/message-two)
+$head1  (tag: refs/tags/tag2)
+$head2  (tag: refs/tags/message-one)
+$old_head1  (tag: refs/tags/message-two)
 EOF
        sort actual >actual1 &&
        test_cmp expected actual1
index 74fc5a88ecdaf66e34449df1a3429f8ee22cb1da..bad84a36e81dbebbad89bf66cb1411ebf27fe66a 100755 (executable)
@@ -164,7 +164,7 @@ check_tar with_olde-prefix olde-
 test_expect_success 'git archive on large files' '
     test_config core.bigfilethreshold 1 &&
     git archive HEAD >b3.tar &&
-    test_cmp b.tar b3.tar
+    test_cmp_bin b.tar b3.tar
 '
 
 test_expect_success \
@@ -173,15 +173,15 @@ test_expect_success \
 
 test_expect_success \
     'git archive vs. the same in a bare repo' \
-    'test_cmp b.tar b3.tar'
+    'test_cmp_bin b.tar b3.tar'
 
 test_expect_success 'git archive with --output' \
     'git archive --output=b4.tar HEAD &&
-    test_cmp b.tar b4.tar'
+    test_cmp_bin b.tar b4.tar'
 
 test_expect_success 'git archive --remote' \
     'git archive --remote=. HEAD >b5.tar &&
-    test_cmp b.tar b5.tar'
+    test_cmp_bin b.tar b5.tar'
 
 test_expect_success \
     'validate file modification time' \
@@ -198,7 +198,7 @@ test_expect_success \
 
 test_expect_success 'git archive with --output, override inferred format' '
        git archive --format=tar --output=d4.zip HEAD &&
-       test_cmp b.tar d4.zip
+       test_cmp_bin b.tar d4.zip
 '
 
 test_expect_success \
@@ -244,34 +244,34 @@ test_expect_success 'archive --list shows only enabled remote filters' '
 test_expect_success 'invoke tar filter by format' '
        git archive --format=tar.foo HEAD >config.tar.foo &&
        tr ab ba <config.tar.foo >config.tar &&
-       test_cmp b.tar config.tar &&
+       test_cmp_bin b.tar config.tar &&
        git archive --format=bar HEAD >config.bar &&
        tr ab ba <config.bar >config.tar &&
-       test_cmp b.tar config.tar
+       test_cmp_bin b.tar config.tar
 '
 
 test_expect_success 'invoke tar filter by extension' '
        git archive -o config-implicit.tar.foo HEAD &&
-       test_cmp config.tar.foo config-implicit.tar.foo &&
+       test_cmp_bin config.tar.foo config-implicit.tar.foo &&
        git archive -o config-implicit.bar HEAD &&
-       test_cmp config.tar.foo config-implicit.bar
+       test_cmp_bin config.tar.foo config-implicit.bar
 '
 
 test_expect_success 'default output format remains tar' '
        git archive -o config-implicit.baz HEAD &&
-       test_cmp b.tar config-implicit.baz
+       test_cmp_bin b.tar config-implicit.baz
 '
 
 test_expect_success 'extension matching requires dot' '
        git archive -o config-implicittar.foo HEAD &&
-       test_cmp b.tar config-implicittar.foo
+       test_cmp_bin b.tar config-implicittar.foo
 '
 
 test_expect_success 'only enabled filters are available remotely' '
        test_must_fail git archive --remote=. --format=tar.foo HEAD \
                >remote.tar.foo &&
        git archive --remote=. --format=bar >remote.bar HEAD &&
-       test_cmp remote.bar config.bar
+       test_cmp_bin remote.bar config.bar
 '
 
 test_expect_success GZIP 'git archive --format=tgz' '
@@ -280,27 +280,27 @@ test_expect_success GZIP 'git archive --format=tgz' '
 
 test_expect_success GZIP 'git archive --format=tar.gz' '
        git archive --format=tar.gz HEAD >j1.tar.gz &&
-       test_cmp j.tgz j1.tar.gz
+       test_cmp_bin j.tgz j1.tar.gz
 '
 
 test_expect_success GZIP 'infer tgz from .tgz filename' '
        git archive --output=j2.tgz HEAD &&
-       test_cmp j.tgz j2.tgz
+       test_cmp_bin j.tgz j2.tgz
 '
 
 test_expect_success GZIP 'infer tgz from .tar.gz filename' '
        git archive --output=j3.tar.gz HEAD &&
-       test_cmp j.tgz j3.tar.gz
+       test_cmp_bin j.tgz j3.tar.gz
 '
 
 test_expect_success GZIP 'extract tgz file' '
        gzip -d -c <j.tgz >j.tar &&
-       test_cmp b.tar j.tar
+       test_cmp_bin b.tar j.tar
 '
 
 test_expect_success GZIP 'remote tar.gz is allowed by default' '
        git archive --remote=. --format=tar.gz HEAD >remote.tar.gz &&
-       test_cmp j.tgz remote.tar.gz
+       test_cmp_bin j.tgz remote.tar.gz
 '
 
 test_expect_success GZIP 'remote tar.gz can be disabled' '
index 51dedab29b682749990fb29a3f11134a2629f941..b04d955bfa8229a9c864ad53d1f9f1ec06260637 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_missing   worktree2/ignored-by-worktree
 
 test_expect_success 'git archive vs. bare' '
        (cd bare && git archive HEAD) >bare-archive.tar &&
-       test_cmp archive.tar bare-archive.tar
+       test_cmp_bin archive.tar bare-archive.tar
 '
 
 test_expect_success 'git archive with worktree attributes, bare' '
index c72f71eb18ee90b7885ea2af574e1115154a5549..21a5c93f41e28845a540637eb11bd6eec830a063 100755 (executable)
@@ -97,15 +97,15 @@ test_expect_success \
 
 test_expect_success \
     'git archive --format=zip vs. the same in a bare repo' \
-    'test_cmp d.zip d1.zip'
+    'test_cmp_bin d.zip d1.zip'
 
 test_expect_success 'git archive --format=zip with --output' \
     'git archive --format=zip --output=d2.zip HEAD &&
-    test_cmp d.zip d2.zip'
+    test_cmp_bin d.zip d2.zip'
 
 test_expect_success 'git archive with --output, inferring format' '
        git archive --output=d3.zip HEAD &&
-       test_cmp d.zip d3.zip
+       test_cmp_bin d.zip d3.zip
 '
 
 test_expect_success \
index 67f3b54bed3545417d1f6186f6f42452cb55dc87..305bcac6b765111106887d4d24d99657c0554b5b 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' '
 test_expect_success 'tar archive of empty tree is empty' '
        git archive --format=tar HEAD: >empty.tar &&
        perl -e "print \"\\0\" x 10240" >10knuls.tar &&
-       test_cmp 10knuls.tar empty.tar
+       test_cmp_bin 10knuls.tar empty.tar
 '
 
 test_expect_success 'tar archive of empty tree with prefix' '
index 93e2c65de65f8dd8544332dd512e6d61dad1746c..82c33b88e710b5fb6163a7bdcd9ecb783633aa83 100755 (executable)
@@ -229,7 +229,7 @@ test_expect_success 'pull request format' '
                cd local &&
                git request-pull initial "$downstream_url" full
        ) >request &&
-       grep ' tags/full$'
+       grep " tags/full\$" request
 '
 
 test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
index 8e54ac57462987567b11623d502ffc68fca3d9a0..ceee95b8a48455e15769168fe67af17deec7e179 100755 (executable)
@@ -120,63 +120,4 @@ EOF
        git cat-file blob `echo 1|git hash-object --stdin` >/dev/null
        )
 '
-
-if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
-       say 'skipping remaining tests, git built without http support'
-       test_done
-fi
-
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-test_expect_success 'push to shallow repo via http' '
-       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git config http.receivepack true
-       ) &&
-       (
-       cd full &&
-       commit 9 &&
-       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master
-       ) &&
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git fsck &&
-       git log --format=%s top/master >actual &&
-       cat <<EOF >expect &&
-9
-4
-3
-EOF
-       test_cmp expect actual
-       )
-'
-
-test_expect_success 'push from shallow repo via http' '
-       mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git &&
-       git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git config http.receivepack true
-       ) &&
-       commit 10 &&
-       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master &&
-       (
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git fsck &&
-       git log --format=%s top/master >actual &&
-       cat <<EOF >expect &&
-10
-1
-4
-3
-2
-1
-EOF
-       test_cmp expect actual
-       )
-'
-
-stop_httpd
 test_done
diff --git a/t/t5542-push-http-shallow.sh b/t/t5542-push-http-shallow.sh
new file mode 100755 (executable)
index 0000000..2a691e0
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='push from/to a shallow clone over http'
+
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+commit() {
+       echo "$1" >tracked &&
+       git add tracked &&
+       git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+       git config --global transfer.fsckObjects true &&
+       commit 1 &&
+       commit 2 &&
+       commit 3 &&
+       commit 4 &&
+       git clone . full &&
+       (
+       git init full-abc &&
+       cd full-abc &&
+       commit a &&
+       commit b &&
+       commit c
+       ) &&
+       git clone --no-local --depth=2 .git shallow &&
+       git --git-dir=shallow/.git log --format=%s >actual &&
+       cat <<EOF >expect &&
+4
+3
+EOF
+       test_cmp expect actual &&
+       git clone --no-local --depth=2 full-abc/.git shallow2 &&
+       git --git-dir=shallow2/.git log --format=%s >actual &&
+       cat <<EOF >expect &&
+c
+b
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'push to shallow repo via http' '
+       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git config http.receivepack true
+       ) &&
+       (
+       cd full &&
+       commit 9 &&
+       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master
+       ) &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git fsck &&
+       git log --format=%s top/master >actual &&
+       cat <<EOF >expect &&
+9
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'push from shallow repo via http' '
+       mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git &&
+       git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git config http.receivepack true
+       ) &&
+       commit 10 &&
+       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git fsck &&
+       git log --format=%s top/master >actual &&
+       cat <<EOF >expect &&
+10
+4
+3
+2
+1
+EOF
+       test_cmp expect actual
+       )
+'
+
+stop_httpd
+test_done
index 1a3a2b6c1a20558a019143d1abbd2168af0caf53..01b8aae2ed12eab92ecbfb562cb616e0cfc47202 100755 (executable)
@@ -171,5 +171,25 @@ test_expect_success 'did not use upload-pack service' '
        test_cmp exp act
 '
 
+test_expect_success 'git client shows text/plain errors' '
+       test_must_fail git clone "$HTTPD_URL/error/text" 2>stderr &&
+       grep "this is the error message" stderr
+'
+
+test_expect_success 'git client does not show html errors' '
+       test_must_fail git clone "$HTTPD_URL/error/html" 2>stderr &&
+       ! grep "this is the error message" stderr
+'
+
+test_expect_success 'git client shows text/plain with a charset' '
+       test_must_fail git clone "$HTTPD_URL/error/charset" 2>stderr &&
+       grep "this is the error message" stderr
+'
+
+test_expect_success 'http error messages are reencoded' '
+       test_must_fail git clone "$HTTPD_URL/error/utf16" 2>stderr &&
+       grep "this is the error message" stderr
+'
+
 stop_httpd
 test_done
index 5abd11a5638299f2d3fe41d38f9a3d10050a4e4b..aa73eeaef82adff56083fdc2d5d35d599b73edee 100755 (executable)
@@ -5,7 +5,11 @@ test_description='test git-http-backend-noserver'
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
 
-test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
+if test_have_prereq GREP_STRIPS_CR
+then
+       GREP_OPTIONS=-U
+       export GREP_OPTIONS
+fi
 
 run_backend() {
        echo "$2" |
index a00a660763451d57b2a9537ec1b99fcfd0c9a831..24194075468819881826c24c08b1aa97bacb9897 100755 (executable)
@@ -87,13 +87,29 @@ test_expect_success 'push new branch by name' '
        compare_refs local HEAD server refs/heads/new-name
 '
 
-test_expect_failure 'push new branch with old:new refspec' '
+test_expect_success 'push new branch with old:new refspec' '
        (cd local &&
         git push origin new-name:new-refspec
        ) &&
        compare_refs local HEAD server refs/heads/new-refspec
 '
 
+test_expect_success 'push new branch with HEAD:new refspec' '
+       (cd local &&
+        git checkout new-name
+        git push origin HEAD:new-refspec-2
+       ) &&
+       compare_refs local HEAD server refs/heads/new-refspec-2
+'
+
+test_expect_success 'push delete branch' '
+       (cd local &&
+        git push origin :new-name
+       ) &&
+       test_must_fail git --git-dir="server/.git" \
+        rev-parse --verify refs/heads/new-name
+'
+
 test_expect_success 'forced push' '
        (cd local &&
        git checkout -b force-test &&
index 9d9d9de08e1926525c24fc01c1217a96964fa1ee..c277db64f75ac9f9c0cd617262824d76959fbfbd 100755 (executable)
@@ -9,19 +9,32 @@ test_description='git rev-list --pretty=format test'
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_tick
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
 # 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)
+added_utf8_part=$(printf "\303\274")
+added_utf8_part_iso88591=$(echo "$added_utf8_part" | iconv -f utf-8 -t $test_encoding)
+added=$(printf "added (hinzugef${added_utf8_part}gt) foo")
+added_iso88591=$(echo "$added" | iconv -f utf-8 -t $test_encoding)
 # same but "changed"
-changed=$(printf "changed (ge\303\244ndert) foo")
-changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1)
+changed_utf8_part=$(printf "\303\244")
+changed_utf8_part_iso88591=$(echo "$changed_utf8_part" | iconv -f utf-8 -t $test_encoding)
+changed=$(printf "changed (ge${changed_utf8_part}ndert) foo")
+changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t $test_encoding)
+
+# Count of char to truncate
+# Number is chosen so, that non-ACSII characters
+# (see $added_utf8_part and $changed_utf8_part)
+# fall into truncated parts of appropriate words both from left and right
+truncate_count=20
 
 test_expect_success 'setup' '
        : >foo &&
        git add foo &&
-       git config i18n.commitEncoding iso8859-1 &&
+       git config i18n.commitEncoding $test_encoding &&
        git commit -m "$added_iso88591" &&
        head1=$(git rev-parse --verify HEAD) &&
        head1_short=$(git rev-parse --verify --short $head1) &&
@@ -124,9 +137,9 @@ EOF
 
 test_format encoding %e <<EOF
 commit $head2
-iso8859-1
+$test_encoding
 commit $head1
-iso8859-1
+$test_encoding
 EOF
 
 test_format subject %s <<EOF
@@ -136,6 +149,13 @@ commit $head1
 $added
 EOF
 
+test_format subject-truncated "%<($truncate_count,trunc)%s" <<EOF
+commit $head2
+changed (ge${changed_utf8_part}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part}gt..
+EOF
+
 test_format body %b <<EOF
 commit $head2
 commit $head1
@@ -203,16 +223,16 @@ test_expect_success '%C(auto) respects --color=auto (stdout not tty)' '
        )
 '
 
-iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF
+iconv -f utf-8 -t $test_encoding > 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!
+and it will be encoded in $test_encoding. We should therefore
+include an ISO8859 character: Â¡bueno!
 EOF
 
 test_expect_success 'setup complex body' '
-       git config i18n.commitencoding iso8859-1 &&
+       git config i18n.commitencoding $test_encoding &&
        echo change2 >foo && git commit -a -F commit-msg &&
        head3=$(git rev-parse --verify HEAD) &&
        head3_short=$(git rev-parse --short $head3)
@@ -220,11 +240,11 @@ test_expect_success 'setup complex body' '
 
 test_format complex-encoding %e <<EOF
 commit $head3
-iso8859-1
+$test_encoding
 commit $head2
-iso8859-1
+$test_encoding
 commit $head1
-iso8859-1
+$test_encoding
 EOF
 
 test_format complex-subject %s <<EOF
@@ -236,20 +256,47 @@ commit $head1
 $added_iso88591
 EOF
 
+test_format complex-subject-trunc "%<($truncate_count,trunc)%s" <<EOF
+commit $head3
+Test printing of c..
+commit $head2
+changed (ge${changed_utf8_part_iso88591}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part_iso88591}gt..
+EOF
+
+test_format complex-subject-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF
+commit $head3
+Test prin..ex bodies
+commit $head2
+changed (..dert) foo
+commit $head1
+added (hi..f${added_utf8_part_iso88591}gt) foo
+EOF
+
+test_format complex-subject-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF
+commit $head3
+.. of complex bodies
+commit $head2
+..ged (ge${changed_utf8_part_iso88591}ndert) foo
+commit $head1
+.. (hinzugef${added_utf8_part_iso88591}gt) foo
+EOF
+
 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!
+       and it will be encoded in $test_encoding. 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
+       iconv -f utf-8 -t $test_encoding expected.utf-8 >expected.ISO8859-1
 '
 
-test_format complex-body %b <expected.iso8859-1
+test_format complex-body %b <expected.ISO8859-1
 
 # Git uses i18n.commitEncoding if no i18n.logOutputEncoding set
 # so unset i18n.commitEncoding to test encoding conversion
@@ -264,6 +311,33 @@ commit $head1
 $added
 EOF
 
+test_format complex-subject-commitencoding-unset-trunc "%<($truncate_count,trunc)%s" <<EOF
+commit $head3
+Test printing of c..
+commit $head2
+changed (ge${changed_utf8_part}ndert)..
+commit $head1
+added (hinzugef${added_utf8_part}gt..
+EOF
+
+test_format complex-subject-commitencoding-unset-mtrunc "%<($truncate_count,mtrunc)%s" <<EOF
+commit $head3
+Test prin..ex bodies
+commit $head2
+changed (..dert) foo
+commit $head1
+added (hi..f${added_utf8_part}gt) foo
+EOF
+
+test_format complex-subject-commitencoding-unset-ltrunc "%<($truncate_count,ltrunc)%s" <<EOF
+commit $head3
+.. of complex bodies
+commit $head2
+..ged (ge${changed_utf8_part}ndert) foo
+commit $head1
+.. (hinzugef${added_utf8_part}gt) foo
+EOF
+
 test_format complex-body-commitencoding-unset %b <expected.utf-8
 
 test_expect_success '%x00 shows NUL' '
diff --git a/t/t6039-merge-ignorecase.sh b/t/t6039-merge-ignorecase.sh
new file mode 100755 (executable)
index 0000000..a977653
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='git-merge with case-changing rename on case-insensitive file system'
+
+. ./test-lib.sh
+
+if ! test_have_prereq CASE_INSENSITIVE_FS
+then
+       skip_all='skipping case insensitive tests - case sensitive file system'
+       test_done
+fi
+
+test_expect_success 'merge with case-changing rename' '
+       test $(git config core.ignorecase) = true &&
+       >TestCase &&
+       git add TestCase &&
+       git commit -m "add TestCase" &&
+       git tag baseline
+       git checkout -b with-camel &&
+       >foo &&
+       git add foo &&
+       git commit -m "intervening commit" &&
+       git checkout master &&
+       git rm TestCase &&
+       >testcase &&
+       git add testcase &&
+       git commit -m "rename to testcase" &&
+       git checkout with-camel &&
+       git merge master -m "merge" &&
+       test_path_is_file testcase
+'
+
+test_expect_success 'merge with case-changing rename on both sides' '
+       git checkout master &&
+       git reset --hard baseline &&
+       git branch -D with-camel &&
+       git checkout -b with-camel &&
+       git mv TestCase testcase &&
+       git commit -m "recase on branch" &&
+       >foo &&
+       git add foo &&
+       git commit -m "intervening commit" &&
+       git checkout master &&
+       git rm TestCase &&
+       >testcase &&
+       git add testcase &&
+       git commit -m "rename to testcase" &&
+       git checkout with-camel &&
+       git merge master -m "merge" &&
+       test_path_is_file testcase
+'
+
+test_done
index 719a11673bfc93463870d7d601eb3a0634b59b48..68b3cb26d90c5b88d28bbd03e8d50d4527f8a19b 100755 (executable)
@@ -318,6 +318,33 @@ test_expect_success 'test --format long' '
        test_cmp expected actual
 '
 
+test_expect_success 'setup a fake editor' '
+       write_script fakeeditor <<-\EOF
+               sed -e "s/A U Thor/A fake Thor/" "$1" >"$1.new"
+               mv "$1.new" "$1"
+       EOF
+'
+
+test_expect_success '--edit with and without already replaced object' '
+       test_must_fail env GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+       GIT_EDITOR=./fakeeditor git replace --force --edit "$PARA3" &&
+       git replace -l | grep "$PARA3" &&
+       git cat-file commit "$PARA3" | grep "A fake Thor" &&
+       git replace -d "$PARA3" &&
+       GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+       git replace -l | grep "$PARA3" &&
+       git cat-file commit "$PARA3" | grep "A fake Thor"
+'
+
+test_expect_success '--edit and change nothing or command failed' '
+       git replace -d "$PARA3" &&
+       test_must_fail env GIT_EDITOR=true git replace --edit "$PARA3" &&
+       test_must_fail env GIT_EDITOR="./fakeeditor;false" git replace --edit "$PARA3" &&
+       GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" &&
+       git replace -l | grep "$PARA3" &&
+       git cat-file commit "$PARA3" | grep "A fake Thor"
+'
+
 test_expect_success 'replace ref cleanup' '
        test -n "$(git replace)" &&
        git replace -d $(git replace) &&
index e41fa00b80e9b3eb47e45be848397ccda0f47c53..1b824fe5ede3b933350696b78b6f53c1c69d0fff 100755 (executable)
@@ -24,7 +24,8 @@ test_expect_success 'set up a bit of history' '
        git tag -m "annotated tag" annotated &&
        git checkout -b side HEAD^^ &&
        test_commit side2 &&
-       test_commit side3
+       test_commit side3 &&
+       test_merge merge main3
 '
 
 test_expect_success 'showing two commits' '
@@ -109,8 +110,11 @@ test_expect_success 'showing range' '
 '
 
 test_expect_success '-s suppresses diff' '
-       echo main3 >expect &&
-       git show -s --format=%s main3 >actual &&
+       cat >expect <<-\EOF &&
+       merge
+       main3
+       EOF
+       git show -s --format=%s merge main3 >actual &&
        test_cmp expect actual
 '
 
index 450529404c686469ed5ba5ad465cf8b3ef651ef9..ee703bed64cc7572cb9a86fce2b0157026475709 100755 (executable)
@@ -22,6 +22,9 @@ commit_msg () {
        fi
 }
 
+# Tested non-UTF-8 encoding
+test_encoding="ISO8859-1"
+
 test_expect_success 'creating initial files and commits' '
        test_tick &&
        echo "1st file" >first &&
@@ -41,7 +44,7 @@ test_expect_success 'creating initial files and commits' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
+       git -c "i18n.commitEncoding=$test_encoding" commit -a -m "$(commit_msg $test_encoding)" &&
        head5=$(git rev-parse --verify HEAD)
 '
 # git log --pretty=oneline # to see those SHA1 involved
@@ -64,10 +67,10 @@ test_expect_success 'reset --hard message' '
        test_cmp .expected .actual
 '
 
-test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' '
+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 &&
+       git -c "i18n.logOutputEncoding=$test_encoding" reset --hard > .actual &&
+       echo HEAD is now at $hex $(commit_msg $test_encoding) > .expected &&
        test_cmp .expected .actual
 '
 
@@ -331,7 +334,7 @@ test_expect_success 'redoing the last two commits should succeed' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
+       git -c "i18n.commitEncoding=$test_encoding" commit -a -m "$(commit_msg $test_encoding)" &&
        check_changes $head5
 '
 
index bdc1f295030b61fd6d4fc0ef672dffa99c027d8b..116885a260a56f94550ace2b541498a5e6f8e877 100755 (executable)
@@ -223,7 +223,8 @@ test_expect_success 'Commit without message is allowed with --allow-empty-messag
        git add foo &&
        >empty &&
        git commit --allow-empty-message <empty &&
-       commit_msg_is ""
+       commit_msg_is "" &&
+       git tag empty-message-commit
 '
 
 test_expect_success 'Commit without message is no-no without --allow-empty-message' '
@@ -240,6 +241,14 @@ test_expect_success 'Commit a message with --allow-empty-message' '
        commit_msg_is "hello there"
 '
 
+test_expect_success 'commit -C empty respects --allow-empty-message' '
+       echo more >>foo &&
+       git add foo &&
+       test_must_fail git commit -C empty-message-commit &&
+       git commit -C empty-message-commit --allow-empty-message &&
+       commit_msg_is ""
+'
+
 commit_for_rebase_autosquash_setup () {
        echo "first content line" >>foo &&
        git add foo &&
index 6465cd59afb6dcd4c2e554947da4755a1beb883e..051489ea334c9693c2d19823ad23947703f66d90 100755 (executable)
@@ -570,4 +570,30 @@ test_expect_success 'commit --status with custom comment character' '
        test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
 
+test_expect_success 'switch core.commentchar' '
+       test_commit "#foo" foo &&
+       GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend &&
+       test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
+'
+
+test_expect_success 'switch core.commentchar but out of options' '
+       cat >text <<\EOF &&
+# 1
+; 2
+@ 3
+! 4
+$ 5
+% 6
+^ 7
+& 8
+| 9
+: 10
+EOF
+       git commit --amend -F text &&
+       (
+               test_set_editor .git/FAKE_EDITOR &&
+               test_must_fail git -c core.commentChar=auto commit --amend
+       )
+'
+
 test_done
index c987b5ed652b977cc2169bdd4c64f42eccdf63db..8ed5788808055889208ac9451054f098538a6878 100755 (executable)
@@ -1350,8 +1350,7 @@ test_expect_success "status (core.commentchar with submodule summary)" '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
        test_config core.commentchar ";;" &&
-       git -c status.displayCommentPrefix=true status >output &&
-       test_i18ncmp expect output
+       test_must_fail git -c status.displayCommentPrefix=true status
 '
 
 test_expect_success "--ignore-submodules=all suppresses submodule summary" '
@@ -1380,7 +1379,32 @@ EOF
        test_i18ncmp expect output
 '
 
-test_expect_success '.gitmodules ignore=all suppresses submodule summary' '
+test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '
+       cat > expect << EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+       modified:   sm
+
+Changes not staged for commit:
+  (use "git add <file>..." to update what will be committed)
+  (use "git checkout -- <file>..." to discard changes in working directory)
+
+       modified:   dir1/modified
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+       .gitmodules
+       dir1/untracked
+       dir2/modified
+       dir2/untracked
+       expect
+       output
+       untracked
+
+EOF
        git config --add -f .gitmodules submodule.subname.ignore all &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status > output &&
@@ -1388,7 +1412,7 @@ test_expect_success '.gitmodules ignore=all suppresses submodule summary' '
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
-test_expect_success '.git/config ignore=all suppresses submodule summary' '
+test_expect_success '.git/config ignore=all suppresses unstaged submodule summary' '
        git config --add -f .gitmodules submodule.subname.ignore none &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git config --add submodule.subname.ignore all &&
@@ -1461,4 +1485,49 @@ test_expect_success 'Restore default test environment' '
        git config --unset status.showUntrackedFiles
 '
 
+test_expect_success 'git commit will commit a staged but ignored submodule' '
+       git config --add -f .gitmodules submodule.subname.ignore all &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore all &&
+       git status -s --ignore-submodules=dirty >output &&
+       test_i18ngrep "^M. sm" output &&
+       GIT_EDITOR="echo hello >>\"\$1\"" &&
+       export GIT_EDITOR &&
+       git commit -uno &&
+       git status -s --ignore-submodules=dirty >output &&
+       test_i18ngrep ! "^M. sm" output
+'
+
+test_expect_success 'git commit --dry-run will show a staged but ignored submodule' '
+       git reset HEAD^ &&
+       git add sm &&
+       cat >expect << EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+       modified:   sm
+
+Changes not staged for commit:
+  (use "git add <file>..." to update what will be committed)
+  (use "git checkout -- <file>..." to discard changes in working directory)
+
+       modified:   dir1/modified
+
+Untracked files not listed (use -u option to show untracked files)
+EOF
+       git commit -uno --dry-run >output &&
+       test_i18ncmp expect output &&
+       git status -s --ignore-submodules=dirty >output &&
+       test_i18ngrep "^M. sm" output
+'
+
+test_expect_success 'git commit -m will commit a staged but ignored submodule' '
+       git commit -uno -m message &&
+       git status -s --ignore-submodules=dirty >output &&
+        test_i18ngrep ! "^M. sm" output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_done
index 10aa028017833479c43bbb551741dfd36d9741aa..b16462132fd01f21cfd2af40a3c769803b0b7c50 100755 (executable)
@@ -57,11 +57,10 @@ create_merge_msgs () {
                git log --no-merges ^HEAD c2 c3
        } >squash.1-5-9 &&
        : >msg.nologff &&
-       echo >msg.nolognoff &&
+       : >msg.nolognoff &&
        {
                echo "* tag 'c3':" &&
-               echo "  commit 3" &&
-               echo
+               echo "  commit 3"
        } >msg.log
 }
 
@@ -71,7 +70,7 @@ verify_merge () {
        git diff --exit-code &&
        if test -n "$3"
        then
-               git show -s --pretty=format:%s HEAD >msg.act &&
+               git show -s --pretty=tformat:%s HEAD >msg.act &&
                test_cmp "$3" msg.act
        fi
 }
@@ -620,10 +619,10 @@ test_expect_success 'merge early part of c2' '
        git tag c6 &&
        git branch -f c5-branch c5 &&
        git merge c5-branch~1 &&
-       git show -s --pretty=format:%s HEAD >actual.branch &&
+       git show -s --pretty=tformat:%s HEAD >actual.branch &&
        git reset --keep HEAD^ &&
        git merge c5~1 &&
-       git show -s --pretty=format:%s HEAD >actual.tag &&
+       git show -s --pretty=tformat:%s HEAD >actual.tag &&
        test_cmp expected.branch actual.branch &&
        test_cmp expected.tag actual.tag
 '
index e7cac1db55113188c7149686399292bded9c07dd..2a3469bcbea39e832ab44280bb1223fc4ee20b61 100755 (executable)
@@ -191,4 +191,13 @@ test_expect_success 'indent of line numbers, ten lines' '
        test $(grep -c "  " actual) = 9
 '
 
+test_expect_success 'blaming files with CRLF newlines' '
+       git config core.autocrlf false &&
+       printf "testcase\r\n" >crlffile &&
+       git add crlffile &&
+       git commit -m testcase &&
+       git -c core.autocrlf=input blame crlffile >actual &&
+       grep "A U Thor" actual
+'
+
 test_done
index 83cc5fc9d1a381ecd006fa0a203f22dea0825a82..2937f4c265b26a92f9ce0634c6e7df578c6c781a 100755 (executable)
@@ -7,40 +7,39 @@ test_description='git svn authors prog tests'
 
 . ./lib-git-svn.sh
 
-cat > svn-authors-prog <<'EOF'
-#!/usr/bin/perl
-$_ = shift;
-if (s/-sub$//)  {
-       print "$_ <$_\@sub.example.com>\n";
-}
-else {
-       print "$_ <$_\@example.com>\n";
-}
+write_script svn-authors-prog "$PERL_PATH" <<-\EOF
+       $_ = shift;
+       if (s/-sub$//)  {
+               print "$_ <$_\@sub.example.com>\n";
+       } else {
+               print "$_ <$_\@example.com>\n";
+       }
 EOF
-chmod +x svn-authors-prog
 
-cat > svn-authors <<'EOF'
-ff = FFFFFFF FFFFFFF <fFf@other.example.com>
-EOF
+test_expect_success 'svn-authors setup' '
+       cat >svn-authors <<-\EOF
+       ff = FFFFFFF FFFFFFF <fFf@other.example.com>
+       EOF
+'
 
 test_expect_success 'setup svnrepo' '
        for i in aa bb cc-sub dd-sub ee-foo ff
        do
                svn mkdir -m $i --username $i "$svnrepo"/$i
        done
-       '
+'
 
 test_expect_success 'import authors with prog and file' '
        git svn clone --authors-prog=./svn-authors-prog \
            --authors-file=svn-authors "$svnrepo" x
-       '
+'
 
 test_expect_success 'imported 6 revisions successfully' '
        (
                cd x
                test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 6
        )
-       '
+'
 
 test_expect_success 'authors-prog ran correctly' '
        (
@@ -56,7 +55,7 @@ test_expect_success 'authors-prog ran correctly' '
                git rev-list -1 --pretty=raw refs/remotes/git-svn~5 | \
                  grep "^author aa <aa@example\.com> "
        )
-       '
+'
 
 test_expect_success 'authors-file overrode authors-prog' '
        (
@@ -64,7 +63,7 @@ test_expect_success 'authors-file overrode authors-prog' '
                git rev-list -1 --pretty=raw refs/remotes/git-svn | \
                  grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> "
        )
-       '
+'
 
 git --git-dir=x/.git config --unset svn.authorsfile
 git --git-dir=x/.git config --unset svn.authorsprog
index 27263dfb80420a417bb0f33aa8731ef2fb7bc7b6..5fc9ef262ac1297d9442ed08208cb183ecc0e5c1 100755 (executable)
@@ -2999,4 +2999,22 @@ test_expect_success 'T: ls root tree' '
        test_cmp expect actual
 '
 
+test_expect_success 'T: delete branch' '
+       git branch to-delete &&
+       git fast-import <<-EOF &&
+       reset refs/heads/to-delete
+       from 0000000000000000000000000000000000000000
+       EOF
+       test_must_fail git rev-parse --verify refs/heads/to-delete
+'
+
+test_expect_success 'T: empty reset doesnt delete branch' '
+       git branch not-to-delete &&
+       git fast-import <<-EOF &&
+       reset refs/heads/not-to-delete
+       EOF
+       git show-ref &&
+       git rev-parse --verify refs/heads/not-to-delete
+'
+
 test_done
index 2312dec8f096fe7e36feeabd0e1cd85b5e421d4a..66c8b0a371e37f90cff8da0c92f09c8784ec45a2 100755 (executable)
@@ -504,4 +504,22 @@ test_expect_success 'refs are updated even if no commits need to be exported' '
        test_cmp expected actual
 '
 
+test_expect_success 'use refspec' '
+       git fast-export --refspec refs/heads/master:refs/heads/foobar master | \
+               grep "^commit " | sort | uniq > actual &&
+       echo "commit refs/heads/foobar" > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'delete refspec' '
+       git branch to-delete &&
+       git fast-export --refspec :refs/heads/to-delete to-delete ^to-delete > actual &&
+       cat > expected <<-EOF &&
+       reset refs/heads/to-delete
+       from 0000000000000000000000000000000000000000
+
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index 4caf36e0066313f5402f61645c4330093ba23fcf..7fab2ed977a529c8754b9e5acd44b7f7c2cfaf2f 100755 (executable)
@@ -403,7 +403,8 @@ test_expect_success 'submit --prepare-p4-only' '
                git commit -m "prep only add" &&
                git p4 submit --prepare-p4-only >out &&
                test_i18ngrep "prepared for submission" out &&
-               test_i18ngrep "must be deleted" out
+               test_i18ngrep "must be deleted" out &&
+               ! test_i18ngrep "everything below this line is just the diff" out
        ) &&
        (
                cd "$cli" &&
index 158e10a67e5878bb9031af2f28439b099b65fd50..c617c826db94f2e3c0ed01fe079d3ae14ac3b883 100644 (file)
@@ -617,6 +617,12 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# test_cmp_bin - helper to compare binary files
+
+test_cmp_bin() {
+       cmp "$@"
+}
+
 # Check if the file expected to be empty is indeed empty, and barfs
 # otherwise.
 
@@ -717,6 +723,11 @@ test_ln_s_add () {
        fi
 }
 
+# This function writes out its parameters, one per line
+test_write_lines () {
+       printf "%s\n" "$@"
+}
+
 perl () {
        command "$PERL_PATH" "$@"
 }
index c081668dfe16c85dda29bace564f7c1c3da77cb0..f7e55b1001f21774b9bab0107b68b59e45a9d79e 100644 (file)
@@ -91,6 +91,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
                VALGRIND
                UNZIP
                PERF_
+               CURL_VERBOSE
        ));
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
index b468e4f88e730f88a1d231a574585ace56e7066a..84c616f180bebc730d05f306906558dd0abad2a4 100644 (file)
@@ -101,7 +101,6 @@ static void do_take_over(struct transport *transport)
 static struct child_process *get_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
-       struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct child_process *helper;
        const char **refspecs = NULL;
@@ -123,10 +122,9 @@ static struct child_process *get_helper(struct transport *transport)
        helper->in = -1;
        helper->out = -1;
        helper->err = 0;
-       argv_array_pushf(&argv, "git-remote-%s", data->name);
-       argv_array_push(&argv, transport->remote->name);
-       argv_array_push(&argv, remove_ext_force(transport->url));
-       helper->argv = argv_array_detach(&argv, NULL);
+       argv_array_pushf(&helper->args, "git-remote-%s", data->name);
+       argv_array_push(&helper->args, transport->remote->name);
+       argv_array_push(&helper->args, remove_ext_force(transport->url));
        helper->git_cmd = 0;
        helper->silent_exec_failure = 1;
 
@@ -245,7 +243,6 @@ static int disconnect_helper(struct transport *transport)
                close(data->helper->out);
                fclose(data->out);
                res = finish_command(data->helper);
-               argv_array_free_detached(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
@@ -397,18 +394,16 @@ static int get_importer(struct transport *transport, struct child_process *fasti
 {
        struct child_process *helper = get_helper(transport);
        struct helper_data *data = transport->data;
-       struct argv_array argv = ARGV_ARRAY_INIT;
        int cat_blob_fd, code;
        memset(fastimport, 0, sizeof(*fastimport));
        fastimport->in = helper->out;
-       argv_array_push(&argv, "fast-import");
-       argv_array_push(&argv, debug ? "--stats" : "--quiet");
+       argv_array_push(&fastimport->args, "fast-import");
+       argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
 
        if (data->bidi_import) {
                cat_blob_fd = xdup(helper->in);
-               argv_array_pushf(&argv, "--cat-blob-fd=%d", cat_blob_fd);
+               argv_array_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd);
        }
-       fastimport->argv = argv.argv;
        fastimport->git_cmd = 1;
 
        code = start_command(fastimport);
@@ -421,30 +416,24 @@ static int get_exporter(struct transport *transport,
 {
        struct helper_data *data = transport->data;
        struct child_process *helper = get_helper(transport);
-       int argc = 0, i;
-       struct strbuf tmp = STRBUF_INIT;
+       int i;
 
        memset(fastexport, 0, sizeof(*fastexport));
 
        /* we need to duplicate helper->in because we want to use it after
         * fastexport is done with it. */
        fastexport->out = dup(helper->in);
-       fastexport->argv = xcalloc(6 + revlist_args->nr, sizeof(*fastexport->argv));
-       fastexport->argv[argc++] = "fast-export";
-       fastexport->argv[argc++] = "--use-done-feature";
-       fastexport->argv[argc++] = data->signed_tags ?
-               "--signed-tags=verbatim" : "--signed-tags=warn-strip";
-       if (data->export_marks) {
-               strbuf_addf(&tmp, "--export-marks=%s.tmp", data->export_marks);
-               fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);
-       }
-       if (data->import_marks) {
-               strbuf_addf(&tmp, "--import-marks=%s", data->import_marks);
-               fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);
-       }
+       argv_array_push(&fastexport->args, "fast-export");
+       argv_array_push(&fastexport->args, "--use-done-feature");
+       argv_array_push(&fastexport->args, data->signed_tags ?
+               "--signed-tags=verbatim" : "--signed-tags=warn-strip");
+       if (data->export_marks)
+               argv_array_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks);
+       if (data->import_marks)
+               argv_array_pushf(&fastexport->args, "--import-marks=%s", data->import_marks);
 
        for (i = 0; i < revlist_args->nr; i++)
-               fastexport->argv[argc++] = revlist_args->items[i].string;
+               argv_array_push(&fastexport->args, revlist_args->items[i].string);
 
        fastexport->git_cmd = 1;
        return start_command(fastexport);
@@ -485,7 +474,6 @@ static int fetch_with_import(struct transport *transport,
 
        if (finish_command(&fastimport))
                die("Error while running fast-import");
-       argv_array_free_detached(fastimport.argv);
 
        /*
         * The fast-import stream of a remote helper that advertises
@@ -862,7 +850,7 @@ static int push_refs_with_export(struct transport *transport,
        struct ref *ref;
        struct child_process *helper, exporter;
        struct helper_data *data = transport->data;
-       struct string_list revlist_args = STRING_LIST_INIT_NODUP;
+       struct string_list revlist_args = STRING_LIST_INIT_DUP;
        struct strbuf buf = STRBUF_INIT;
 
        if (!data->refspecs)
@@ -882,15 +870,10 @@ static int push_refs_with_export(struct transport *transport,
 
        write_constant(helper->in, "export\n");
 
-       strbuf_reset(&buf);
-
        for (ref = remote_refs; ref; ref = ref->next) {
                char *private;
                unsigned char sha1[20];
 
-               if (ref->deletion)
-                       die("remote-helpers do not support ref deletion");
-
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (private && !get_sha1(private, sha1)) {
                        strbuf_addf(&buf, "^%s", private);
@@ -900,15 +883,34 @@ static int push_refs_with_export(struct transport *transport,
                free(private);
 
                if (ref->peer_ref) {
-                       if (strcmp(ref->peer_ref->name, ref->name))
-                               die("remote-helpers do not support old:new syntax");
-                       string_list_append(&revlist_args, ref->peer_ref->name);
+                       if (strcmp(ref->name, ref->peer_ref->name)) {
+                               if (!ref->deletion) {
+                                       const char *name;
+                                       int flag;
+
+                                       /* Follow symbolic refs (mainly for HEAD). */
+                                       name = resolve_ref_unsafe(ref->peer_ref->name, sha1, 1, &flag);
+                                       if (!name || !(flag & REF_ISSYMREF))
+                                               name = ref->peer_ref->name;
+
+                                       strbuf_addf(&buf, "%s:%s", name, ref->name);
+                               } else
+                                       strbuf_addf(&buf, ":%s", ref->name);
+
+                               string_list_append(&revlist_args, "--refspec");
+                               string_list_append(&revlist_args, buf.buf);
+                               strbuf_release(&buf);
+                       }
+                       if (!ref->deletion)
+                               string_list_append(&revlist_args, ref->peer_ref->name);
                }
        }
 
        if (get_exporter(transport, &exporter, &revlist_args))
                die("Couldn't run fast-export");
 
+       string_list_clear(&revlist_args, 1);
+
        if (finish_command(&exporter))
                die("Error while running fast-export");
        if (push_update_refs_status(data, remote_refs, flags))
@@ -1026,7 +1028,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
 int transport_helper_init(struct transport *transport, const char *name)
 {
-       struct helper_data *data = xcalloc(sizeof(*data), 1);
+       struct helper_data *data = xcalloc(1, sizeof(*data));
        data->name = name;
 
        if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
diff --git a/unicode_width.h b/unicode_width.h
new file mode 100644 (file)
index 0000000..4db7803
--- /dev/null
@@ -0,0 +1,288 @@
+static const struct interval zero_width[] = {
+{ 0x0300, 0x036F },
+{ 0x0483, 0x0489 },
+{ 0x0591, 0x05BD },
+{ 0x05BF, 0x05BF },
+{ 0x05C1, 0x05C2 },
+{ 0x05C4, 0x05C5 },
+{ 0x05C7, 0x05C7 },
+{ 0x0600, 0x0604 },
+{ 0x0610, 0x061A },
+{ 0x061C, 0x061C },
+{ 0x064B, 0x065F },
+{ 0x0670, 0x0670 },
+{ 0x06D6, 0x06DD },
+{ 0x06DF, 0x06E4 },
+{ 0x06E7, 0x06E8 },
+{ 0x06EA, 0x06ED },
+{ 0x070F, 0x070F },
+{ 0x0711, 0x0711 },
+{ 0x0730, 0x074A },
+{ 0x07A6, 0x07B0 },
+{ 0x07EB, 0x07F3 },
+{ 0x0816, 0x0819 },
+{ 0x081B, 0x0823 },
+{ 0x0825, 0x0827 },
+{ 0x0829, 0x082D },
+{ 0x0859, 0x085B },
+{ 0x08E4, 0x08FE },
+{ 0x0900, 0x0902 },
+{ 0x093A, 0x093A },
+{ 0x093C, 0x093C },
+{ 0x0941, 0x0948 },
+{ 0x094D, 0x094D },
+{ 0x0951, 0x0957 },
+{ 0x0962, 0x0963 },
+{ 0x0981, 0x0981 },
+{ 0x09BC, 0x09BC },
+{ 0x09C1, 0x09C4 },
+{ 0x09CD, 0x09CD },
+{ 0x09E2, 0x09E3 },
+{ 0x0A01, 0x0A02 },
+{ 0x0A3C, 0x0A3C },
+{ 0x0A41, 0x0A42 },
+{ 0x0A47, 0x0A48 },
+{ 0x0A4B, 0x0A4D },
+{ 0x0A51, 0x0A51 },
+{ 0x0A70, 0x0A71 },
+{ 0x0A75, 0x0A75 },
+{ 0x0A81, 0x0A82 },
+{ 0x0ABC, 0x0ABC },
+{ 0x0AC1, 0x0AC5 },
+{ 0x0AC7, 0x0AC8 },
+{ 0x0ACD, 0x0ACD },
+{ 0x0AE2, 0x0AE3 },
+{ 0x0B01, 0x0B01 },
+{ 0x0B3C, 0x0B3C },
+{ 0x0B3F, 0x0B3F },
+{ 0x0B41, 0x0B44 },
+{ 0x0B4D, 0x0B4D },
+{ 0x0B56, 0x0B56 },
+{ 0x0B62, 0x0B63 },
+{ 0x0B82, 0x0B82 },
+{ 0x0BC0, 0x0BC0 },
+{ 0x0BCD, 0x0BCD },
+{ 0x0C3E, 0x0C40 },
+{ 0x0C46, 0x0C48 },
+{ 0x0C4A, 0x0C4D },
+{ 0x0C55, 0x0C56 },
+{ 0x0C62, 0x0C63 },
+{ 0x0CBC, 0x0CBC },
+{ 0x0CBF, 0x0CBF },
+{ 0x0CC6, 0x0CC6 },
+{ 0x0CCC, 0x0CCD },
+{ 0x0CE2, 0x0CE3 },
+{ 0x0D41, 0x0D44 },
+{ 0x0D4D, 0x0D4D },
+{ 0x0D62, 0x0D63 },
+{ 0x0DCA, 0x0DCA },
+{ 0x0DD2, 0x0DD4 },
+{ 0x0DD6, 0x0DD6 },
+{ 0x0E31, 0x0E31 },
+{ 0x0E34, 0x0E3A },
+{ 0x0E47, 0x0E4E },
+{ 0x0EB1, 0x0EB1 },
+{ 0x0EB4, 0x0EB9 },
+{ 0x0EBB, 0x0EBC },
+{ 0x0EC8, 0x0ECD },
+{ 0x0F18, 0x0F19 },
+{ 0x0F35, 0x0F35 },
+{ 0x0F37, 0x0F37 },
+{ 0x0F39, 0x0F39 },
+{ 0x0F71, 0x0F7E },
+{ 0x0F80, 0x0F84 },
+{ 0x0F86, 0x0F87 },
+{ 0x0F8D, 0x0F97 },
+{ 0x0F99, 0x0FBC },
+{ 0x0FC6, 0x0FC6 },
+{ 0x102D, 0x1030 },
+{ 0x1032, 0x1037 },
+{ 0x1039, 0x103A },
+{ 0x103D, 0x103E },
+{ 0x1058, 0x1059 },
+{ 0x105E, 0x1060 },
+{ 0x1071, 0x1074 },
+{ 0x1082, 0x1082 },
+{ 0x1085, 0x1086 },
+{ 0x108D, 0x108D },
+{ 0x109D, 0x109D },
+{ 0x1160, 0x11FF },
+{ 0x135D, 0x135F },
+{ 0x1712, 0x1714 },
+{ 0x1732, 0x1734 },
+{ 0x1752, 0x1753 },
+{ 0x1772, 0x1773 },
+{ 0x17B4, 0x17B5 },
+{ 0x17B7, 0x17BD },
+{ 0x17C6, 0x17C6 },
+{ 0x17C9, 0x17D3 },
+{ 0x17DD, 0x17DD },
+{ 0x180B, 0x180E },
+{ 0x18A9, 0x18A9 },
+{ 0x1920, 0x1922 },
+{ 0x1927, 0x1928 },
+{ 0x1932, 0x1932 },
+{ 0x1939, 0x193B },
+{ 0x1A17, 0x1A18 },
+{ 0x1A1B, 0x1A1B },
+{ 0x1A56, 0x1A56 },
+{ 0x1A58, 0x1A5E },
+{ 0x1A60, 0x1A60 },
+{ 0x1A62, 0x1A62 },
+{ 0x1A65, 0x1A6C },
+{ 0x1A73, 0x1A7C },
+{ 0x1A7F, 0x1A7F },
+{ 0x1B00, 0x1B03 },
+{ 0x1B34, 0x1B34 },
+{ 0x1B36, 0x1B3A },
+{ 0x1B3C, 0x1B3C },
+{ 0x1B42, 0x1B42 },
+{ 0x1B6B, 0x1B73 },
+{ 0x1B80, 0x1B81 },
+{ 0x1BA2, 0x1BA5 },
+{ 0x1BA8, 0x1BA9 },
+{ 0x1BAB, 0x1BAB },
+{ 0x1BE6, 0x1BE6 },
+{ 0x1BE8, 0x1BE9 },
+{ 0x1BED, 0x1BED },
+{ 0x1BEF, 0x1BF1 },
+{ 0x1C2C, 0x1C33 },
+{ 0x1C36, 0x1C37 },
+{ 0x1CD0, 0x1CD2 },
+{ 0x1CD4, 0x1CE0 },
+{ 0x1CE2, 0x1CE8 },
+{ 0x1CED, 0x1CED },
+{ 0x1CF4, 0x1CF4 },
+{ 0x1DC0, 0x1DE6 },
+{ 0x1DFC, 0x1DFF },
+{ 0x200B, 0x200F },
+{ 0x202A, 0x202E },
+{ 0x2060, 0x2064 },
+{ 0x2066, 0x206F },
+{ 0x20D0, 0x20F0 },
+{ 0x2CEF, 0x2CF1 },
+{ 0x2D7F, 0x2D7F },
+{ 0x2DE0, 0x2DFF },
+{ 0x302A, 0x302D },
+{ 0x3099, 0x309A },
+{ 0xA66F, 0xA672 },
+{ 0xA674, 0xA67D },
+{ 0xA69F, 0xA69F },
+{ 0xA6F0, 0xA6F1 },
+{ 0xA802, 0xA802 },
+{ 0xA806, 0xA806 },
+{ 0xA80B, 0xA80B },
+{ 0xA825, 0xA826 },
+{ 0xA8C4, 0xA8C4 },
+{ 0xA8E0, 0xA8F1 },
+{ 0xA926, 0xA92D },
+{ 0xA947, 0xA951 },
+{ 0xA980, 0xA982 },
+{ 0xA9B3, 0xA9B3 },
+{ 0xA9B6, 0xA9B9 },
+{ 0xA9BC, 0xA9BC },
+{ 0xAA29, 0xAA2E },
+{ 0xAA31, 0xAA32 },
+{ 0xAA35, 0xAA36 },
+{ 0xAA43, 0xAA43 },
+{ 0xAA4C, 0xAA4C },
+{ 0xAAB0, 0xAAB0 },
+{ 0xAAB2, 0xAAB4 },
+{ 0xAAB7, 0xAAB8 },
+{ 0xAABE, 0xAABF },
+{ 0xAAC1, 0xAAC1 },
+{ 0xAAEC, 0xAAED },
+{ 0xAAF6, 0xAAF6 },
+{ 0xABE5, 0xABE5 },
+{ 0xABE8, 0xABE8 },
+{ 0xABED, 0xABED },
+{ 0xFB1E, 0xFB1E },
+{ 0xFE00, 0xFE0F },
+{ 0xFE20, 0xFE26 },
+{ 0xFEFF, 0xFEFF },
+{ 0xFFF9, 0xFFFB },
+{ 0x101FD, 0x101FD },
+{ 0x10A01, 0x10A03 },
+{ 0x10A05, 0x10A06 },
+{ 0x10A0C, 0x10A0F },
+{ 0x10A38, 0x10A3A },
+{ 0x10A3F, 0x10A3F },
+{ 0x11001, 0x11001 },
+{ 0x11038, 0x11046 },
+{ 0x11080, 0x11081 },
+{ 0x110B3, 0x110B6 },
+{ 0x110B9, 0x110BA },
+{ 0x110BD, 0x110BD },
+{ 0x11100, 0x11102 },
+{ 0x11127, 0x1112B },
+{ 0x1112D, 0x11134 },
+{ 0x11180, 0x11181 },
+{ 0x111B6, 0x111BE },
+{ 0x116AB, 0x116AB },
+{ 0x116AD, 0x116AD },
+{ 0x116B0, 0x116B5 },
+{ 0x116B7, 0x116B7 },
+{ 0x16F8F, 0x16F92 },
+{ 0x1D167, 0x1D169 },
+{ 0x1D173, 0x1D182 },
+{ 0x1D185, 0x1D18B },
+{ 0x1D1AA, 0x1D1AD },
+{ 0x1D242, 0x1D244 },
+{ 0xE0001, 0xE0001 },
+{ 0xE0020, 0xE007F },
+{ 0xE0100, 0xE01EF }
+};
+static const struct interval double_width[] = {
+{ /* plane */ 0x0, 0x1C },
+{ /* plane */ 0x1C, 0x21 },
+{ /* plane */ 0x21, 0x22 },
+{ /* plane */ 0x22, 0x23 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ /* plane */ 0x0, 0x0 },
+{ 0x1100, 0x115F },
+{ 0x2329, 0x232A },
+{ 0x2E80, 0x2E99 },
+{ 0x2E9B, 0x2EF3 },
+{ 0x2F00, 0x2FD5 },
+{ 0x2FF0, 0x2FFB },
+{ 0x3000, 0x303E },
+{ 0x3041, 0x3096 },
+{ 0x3099, 0x30FF },
+{ 0x3105, 0x312D },
+{ 0x3131, 0x318E },
+{ 0x3190, 0x31BA },
+{ 0x31C0, 0x31E3 },
+{ 0x31F0, 0x321E },
+{ 0x3220, 0x3247 },
+{ 0x3250, 0x32FE },
+{ 0x3300, 0x4DBF },
+{ 0x4E00, 0xA48C },
+{ 0xA490, 0xA4C6 },
+{ 0xA960, 0xA97C },
+{ 0xAC00, 0xD7A3 },
+{ 0xF900, 0xFAFF },
+{ 0xFE10, 0xFE19 },
+{ 0xFE30, 0xFE52 },
+{ 0xFE54, 0xFE66 },
+{ 0xFE68, 0xFE6B },
+{ 0xFF01, 0xFF60 },
+{ 0xFFE0, 0xFFE6 },
+{ 0x1B000, 0x1B001 },
+{ 0x1F200, 0x1F202 },
+{ 0x1F210, 0x1F23A },
+{ 0x1F240, 0x1F248 },
+{ 0x1F250, 0x1F251 },
+{ 0x20000, 0x2FFFD },
+{ 0x30000, 0x3FFFD }
+};
diff --git a/update_unicode.sh b/update_unicode.sh
new file mode 100755 (executable)
index 0000000..000b937
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+#See http://www.unicode.org/reports/tr44/
+#
+#Me Enclosing_Mark  an enclosing combining mark
+#Mn Nonspacing_Mark a nonspacing combining mark (zero advance width)
+#Cf Format          a format control character
+#
+UNICODEWIDTH_H=../unicode_width.h
+if ! test -d unicode; then
+       mkdir unicode
+fi &&
+( cd unicode &&
+       if ! test -f UnicodeData.txt; then
+               wget http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
+       fi &&
+       if ! test -f EastAsianWidth.txt; then
+               wget http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
+       fi &&
+       if ! test -d uniset; then
+               git clone https://github.com/depp/uniset.git
+       fi &&
+       (
+               cd uniset &&
+               if ! test -x uniset; then
+                       autoreconf -i &&
+                       ./configure --enable-warnings=-Werror CFLAGS='-O0 -ggdb'
+               fi &&
+               make
+       ) &&
+       echo "static const struct interval zero_width[] = {" >$UNICODEWIDTH_H &&
+       UNICODE_DIR=. ./uniset/uniset --32 cat:Me,Mn,Cf + U+1160..U+11FF - U+00AD |
+       grep -v plane >>$UNICODEWIDTH_H &&
+       echo "};" >>$UNICODEWIDTH_H &&
+       echo "static const struct interval double_width[] = {" >>$UNICODEWIDTH_H &&
+       UNICODE_DIR=. ./uniset/uniset --32 eaw:F,W >>$UNICODEWIDTH_H &&
+       echo "};" >>$UNICODEWIDTH_H
+)
diff --git a/utf8.c b/utf8.c
index 77c28d492cccfcbcb8a302d168a81b21d909ef12..b30790d043aa4b01da00686654dfb615a92e75b6 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -80,52 +80,8 @@ static int git_wcwidth(ucs_char_t ch)
 {
        /*
         * Sorted list of non-overlapping intervals of non-spacing characters,
-        * generated by
-        *   "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c".
         */
-       static const struct interval combining[] = {
-               { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD },
-               { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 },
-               { 0x05C7, 0x05C7 }, { 0x0600, 0x0604 }, { 0x0610, 0x061A },
-               { 0x064B, 0x065F }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 },
-               { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F },
-               { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 },
-               { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 },
-               { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 },
-               { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 },
-               { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 },
-               { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 },
-               { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 },
-               { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 },
-               { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 },
-               { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },
-               { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },
-               { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
-               { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
-               { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 },
-               { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
-               { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
-               { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
-               { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
-               { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
-               { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
-               { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
-               { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
-               { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
-               { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 },
-               { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 },
-               { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
-               { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D },
-               { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 },
-               { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F },
-               { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F },
-               { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A },
-               { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 },
-               { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 },
-               { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B },
-               { 0x1D1AA, 0x1D1AD }, { 0xE0001, 0xE0001 },
-               { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF }
-       };
+#include "unicode_width.h"
 
        /* test for 8-bit control characters */
        if (ch == 0)
@@ -134,34 +90,16 @@ static int git_wcwidth(ucs_char_t ch)
                return -1;
 
        /* binary search in table of non-spacing characters */
-       if (bisearch(ch, combining, sizeof(combining)
+       if (bisearch(ch, zero_width, sizeof(zero_width)
                                / sizeof(struct interval) - 1))
                return 0;
 
-       /*
-        * If we arrive here, ch is neither a combining nor a C0/C1
-        * control character.
-        */
+       /* binary search in table of double width characters */
+       if (bisearch(ch, double_width, sizeof(double_width)
+                               / sizeof(struct interval) - 1))
+               return 2;
 
-       return 1 +
-               (ch >= 0x1100 &&
-                    /* Hangul Jamo init. consonants */
-                (ch <= 0x115f ||
-                 ch == 0x2329 || ch == 0x232a ||
-                  /* CJK ... Yi */
-                 (ch >= 0x2e80 && ch <= 0xa4cf &&
-                  ch != 0x303f) ||
-                 /* Hangul Syllables */
-                 (ch >= 0xac00 && ch <= 0xd7a3) ||
-                 /* CJK Compatibility Ideographs */
-                 (ch >= 0xf900 && ch <= 0xfaff) ||
-                 /* CJK Compatibility Forms */
-                 (ch >= 0xfe30 && ch <= 0xfe6f) ||
-                 /* Fullwidth Forms */
-                 (ch >= 0xff00 && ch <= 0xff60) ||
-                 (ch >= 0xffe0 && ch <= 0xffe6) ||
-                 (ch >= 0x20000 && ch <= 0x2fffd) ||
-                 (ch >= 0x30000 && ch <= 0x3fffd)));
+       return 1;
 }
 
 /*
index ec7344e50834f18821692d7e38f634778d632080..318a191238fb831da8b72f04886b311f1c2c2567 100644 (file)
@@ -188,7 +188,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
        } else {
                status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
        }
-       status_printf_ln(s, c, "");
+       status_printf_ln(s, c, "%s", "");
 }
 
 static void wt_status_print_cached_header(struct wt_status *s)
@@ -204,7 +204,7 @@ static void wt_status_print_cached_header(struct wt_status *s)
                status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
        else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
-       status_printf_ln(s, c, "");
+       status_printf_ln(s, c, "%s", "");
 }
 
 static void wt_status_print_dirty_header(struct wt_status *s,
@@ -223,7 +223,7 @@ static void wt_status_print_dirty_header(struct wt_status *s,
        status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
        if (has_dirty_submodules)
                status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
-       status_printf_ln(s, c, "");
+       status_printf_ln(s, c, "%s", "");
 }
 
 static void wt_status_print_other_header(struct wt_status *s,
@@ -235,12 +235,12 @@ static void wt_status_print_other_header(struct wt_status *s,
        if (!s->hints)
                return;
        status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
-       status_printf_ln(s, c, "");
+       status_printf_ln(s, c, "%s", "");
 }
 
 static void wt_status_print_trailer(struct wt_status *s)
 {
-       status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
+       status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
 }
 
 #define quote_path quote_path_relative
@@ -519,9 +519,19 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
 
+       DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
        if (s->ignore_submodule_arg) {
-               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+       } else {
+               /*
+                * Unless the user did explicitly request a submodule ignore
+                * mode by passing a command line option we do not ignore any
+                * changed submodule SHA-1s when comparing index and HEAD, no
+                * matter what is configured. Otherwise the user won't be
+                * shown any submodules she manually added (and which are
+                * staged to be committed), which would be really confusing.
+                */
+               handle_ignore_submodules_arg(&rev.diffopt, "dirty");
        }
 
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
@@ -826,7 +836,7 @@ static void wt_status_print_other(struct wt_status *s,
        string_list_clear(&output, 0);
        strbuf_release(&buf);
 conclude:
-       status_printf_ln(s, GIT_COLOR_NORMAL, "");
+       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
 }
 
 void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
@@ -913,7 +923,7 @@ static void wt_status_print_tracking(struct wt_status *s)
                color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
                                 comment_line_char);
        else
-               fprintf_ln(s->fp, "");
+               fputs("", s->fp);
 }
 
 static int has_unmerged(struct wt_status *s)
@@ -1329,7 +1339,7 @@ void wt_status_print(struct wt_status *s)
                                on_what = _("Not currently on any branch.");
                        }
                }
-               status_printf(s, color(WT_STATUS_HEADER, s), "");
+               status_printf(s, color(WT_STATUS_HEADER, s), "%s", "");
                status_printf_more(s, branch_status_color, "%s", on_what);
                status_printf_more(s, branch_color, "%s\n", branch_name);
                if (!s->is_initial)
@@ -1342,9 +1352,9 @@ void wt_status_print(struct wt_status *s)
        free(state.detached_from);
 
        if (s->is_initial) {
-               status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
+               status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
                status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
-               status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
+               status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
        }
 
        wt_status_print_updated(s);
@@ -1361,7 +1371,7 @@ void wt_status_print(struct wt_status *s)
                if (s->show_ignored_files)
                        wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
                if (advice_status_u_option && 2000 < s->untracked_in_ms) {
-                       status_printf_ln(s, GIT_COLOR_NORMAL, "");
+                       status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                         _("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
                                           "may speed it up, but you have to be careful not to forget to add\n"