Merge branch 'dt/tree-fsck'
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Oct 2016 20:30:38 +0000 (13:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Oct 2016 20:30:38 +0000 (13:30 -0700)
The codepath in "git fsck" to detect malformed tree objects has
been updated not to die but keep going after detecting them.

* dt/tree-fsck:
fsck: handle bad trees like other errors
tree-walk: be more specific about corrupt tree errors

103 files changed:
.mailmap
.travis.yml
Documentation/RelNotes/2.10.1.txt
Documentation/RelNotes/2.11.0.txt
Documentation/config.txt
Documentation/diff-config.txt
Documentation/diff-heuristic-options.txt [new file with mode: 0644]
Documentation/diff-options.txt
Documentation/git-annotate.txt
Documentation/git-blame.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-cvsimport.txt
Documentation/git-format-patch.txt
Documentation/gitcvs-migration.txt
Documentation/gitweb.conf.txt
Makefile
apply.c
builtin/add.c
builtin/blame.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/fmt-merge-msg.c
builtin/init-db.c
builtin/log.c
builtin/merge.c
builtin/mv.c
builtin/submodule--helper.c
builtin/update-index.c
builtin/worktree.c
cache.h
commit.c
connect.c
contrib/coccinelle/array.cocci [new file with mode: 0644]
contrib/coccinelle/object_id.cocci
contrib/coccinelle/strbuf.cocci [new file with mode: 0644]
diff.c
diff.h
diffcore-pickaxe.c
fetch-pack.c
git-add--interactive.perl
git-compat-util.h
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/po/glossary/pt_pt.po [new file with mode: 0644]
git-gui/po/pt_pt.po [new file with mode: 0644]
git-rebase--interactive.sh
git-stash.sh
git-submodule.sh
gitweb/gitweb.perl
grep.c
ident.c
mailinfo.c
mailinfo.h
merge-recursive.c
notes-merge.c
pack-check.c
pack-revindex.c
parse-options-cb.c
parse-options.h
pathspec.c
pretty.c
read-cache.c
remote.c
sha1_file.c
split-index.c
streaming.c
t/t0001-init.sh
t/t2010-checkout-ambiguous.sh
t/t2024-checkout-dwim.sh
t/t2107-update-index-basic.sh
t/t3310-notes-merge-manual-resolve.sh
t/t3404-rebase-interactive.sh
t/t3700-add.sh
t/t3900-i18n-commit.sh
t/t3901-i18n-patch.sh
t/t4014-format-patch.sh
t/t4061-diff-indent.sh [new file with mode: 0755]
t/t4062-diff-pickaxe.sh [new file with mode: 0755]
t/t4150-am.sh
t/t5100-mailinfo.sh
t/t5100/comment.expect [new file with mode: 0644]
t/t5100/comment.in [new file with mode: 0644]
t/t5100/info0018 [new file with mode: 0644]
t/t5100/info0018--no-inbody-headers [new file with mode: 0644]
t/t5100/msg0015
t/t5100/msg0018 [new file with mode: 0644]
t/t5100/msg0018--no-inbody-headers [new file with mode: 0644]
t/t5100/patch0018 [new file with mode: 0644]
t/t5100/patch0018--no-inbody-headers [new file with mode: 0644]
t/t5100/quoted-string.expect [new file with mode: 0644]
t/t5100/quoted-string.in [new file with mode: 0644]
t/t5100/sample.mbox
t/t5512-ls-remote.sh
t/t6006-rev-list-format.sh
t/t9500-gitweb-standalone-no-errors.sh
unpack-trees.c
wt-status.c
xdiff-interface.c
xdiff/xdiff.h
xdiff/xdiffi.c
index 9441a54b0d5b89ea2c795f8f6ad8a99dfbe8b432..9cc33e925de8adc562daf9b176135aba7b5e4d9b 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -48,6 +48,7 @@ David Kågedal <davidk@lysator.liu.se>
 David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
 David S. Miller <davem@davemloft.net>
 David Turner <novalis@novalis.org> <dturner@twopensource.com>
+David Turner <novalis@novalis.org> <dturner@twosigma.com>
 Deskin Miller <deskinm@umich.edu>
 Dirk Süsserott <newsletter@dirk.my1.cc>
 Eric Blake <eblake@redhat.com> <ebb9@byu.net>
index 477c3d2efb7b94bb34fcda517b1b855e98e1f43a..37a1e1fb6d48935aa22a384797ce82a39340b2ed 100644 (file)
@@ -78,7 +78,7 @@ before_install:
         FORMULA=$1
         SHA=$(brew fetch --force $FORMULA 2>&1 | grep ^SHA256: | cut -d ' ' -f 2)
         sed -E -i.bak "s/sha256 \"[0-9a-f]{64}\"/sha256 \"$SHA\"/g" \
-          /usr/local/Library/Taps/homebrew/homebrew-binary/$FORMULA.rb
+          "$(brew --repository homebrew/homebrew-binary)/$FORMULA.rb"
       }
       brew update --quiet
       brew tap homebrew/binary --quiet
index 75c07e199ef04e8f162e41c5dbdb998e5a62f301..73f3b978cf44f70c0fe7283a4978902a570200ee 100644 (file)
@@ -30,4 +30,91 @@ Fixes since v2.10
  * Update a few tests that used to use GIT_CURL_VERBOSE to use the
    newer GIT_TRACE_CURL.
 
+ * Update Japanese translation for "git-gui".
+
+ * "git fetch http::/site/path" did not die correctly and segfaulted
+   instead.
+
+ * "git commit-tree" stopped reading commit.gpgsign configuration
+   variable that was meant for Porcelain "git commit" in Git 2.9; we
+   forgot to update "git gui" to look at the configuration to match
+   this change.
+
+ * "git log --cherry-pick" used to include merge commits as candidates
+   to be matched up with other commits, resulting a lot of wasted time.
+   The patch-id generation logic has been updated to ignore merges to
+   avoid the wastage.
+
+ * The http transport (with curl-multi option, which is the default
+   these days) failed to remove curl-easy handle from a curlm session,
+   which led to unnecessary API failures.
+
+ * "git diff -W" output needs to extend the context backward to
+   include the header line of the current function and also forward to
+   include the body of the entire current function up to the header
+   line of the next one.  This process may have to merge to adjacent
+   hunks, but the code forgot to do so in some cases.
+
+ * Performance tests done via "t/perf" did not use the same set of
+   build configuration if the user relied on autoconf generated
+   configuration.
+
+ * "git format-patch --base=..." feature that was recently added
+   showed the base commit information after "-- " e-mail signature
+   line, which turned out to be inconvenient.  The base information
+   has been moved above the signature line.
+
+ * Even when "git pull --rebase=preserve" (and the underlying "git
+   rebase --preserve") can complete without creating any new commit
+   (i.e. fast-forwards), it still insisted on having a usable ident
+   information (read: user.email is set correctly), which was less
+   than nice.  As the underlying commands used inside "git rebase"
+   would fail with a more meaningful error message and advice text
+   when the bogus ident matters, this extra check was removed.
+
+ * "git gc --aggressive" used to limit the delta-chain length to 250,
+   which is way too deep for gaining additional space savings and is
+   detrimental for runtime performance.  The limit has been reduced to
+   50.
+
+ * Documentation for individual configuration variables to control use
+   of color (like `color.grep`) said that their default value is
+   'false', instead of saying their default is taken from `color.ui`.
+   When we updated the default value for color.ui from 'false' to
+   'auto' quite a while ago, all of them broke.  This has been
+   corrected.
+
+ * A shell script example in check-ref-format documentation has been
+   fixed.
+
+ * "git checkout <word>" does not follow the usual disambiguation
+   rules when the <word> can be both a rev and a path, to allow
+   checking out a branch 'foo' in a project that happens to have a
+   file 'foo' in the working tree without having to disambiguate.
+   This was poorly documented and the check was incorrect when the
+   command was run from a subdirectory.
+
+ * Some codepaths in "git diff" used regexec(3) on a buffer that was
+   mmap(2)ed, which may not have a terminating NUL, leading to a read
+   beyond the end of the mapped region.  This was fixed by introducing
+   a regexec_buf() helper that takes a <ptr,len> pair with REG_STARTEND
+   extension.
+
+ * The procedure to build Git on Mac OS X for Travis CI hardcoded the
+   internal directory structure we assumed HomeBrew uses, which was a
+   no-no.  The procedure has been updated to ask HomeBrew things we
+   need to know to fix this.
+
+ * When "git rebase -i" is given a broken instruction, it told the
+   user to fix it with "--edit-todo", but didn't say what the step
+   after that was (i.e. "--continue").
+
+ * "git add --chmod=+x" added recently lacked documentation, which has
+   been corrected.
+
+ * "git add --chmod=+x <pathspec>" added recently only toggled the
+   executable bit for paths that are either new or modified. This has
+   been corrected to flip the executable bit for all paths that match
+   the given pathspec.
+
 Also contains minor documentation updates and code clean-ups.
index 8b29f7712debb7b4599f55a2264602c4e506de41..cfe94b99182d86d23e2e14e952e01549a16c5a5a 100644 (file)
@@ -38,6 +38,27 @@ UI, Workflows & Features
    lacked an equivalent mechanism to run the "Git-to-outside-world"
    conversion.  The command learned the "--filters" option to do so.
 
+ * Output from "git diff" can be made easier to read by selecting
+   which lines are common and which lines are added/deleted
+   intelligently when the lines before and after the changed section
+   are the same.  A command line option is added to help with the
+   experiment to find a good heuristics.
+
+ * In some projects, it is common to use "[RFC PATCH]" as the subject
+   prefix for a patch meant for discussion rather than application.  A
+   new option "--rfc" was a short-hand for "--subject-prefix=RFC PATCH"
+   to help the participants of such projects.
+
+ * "git add --chmod=+x <pathspec>" added recently only toggled the
+   executable bit for paths that are either new or modified. This has
+   been corrected to flip the executable bit for all paths that match
+   the given pathspec.
+
+ * When "git format-patch --stdout" output is placed as an in-body
+   header and it uses the RFC2822 header folding, "git am" failed to
+   put the header line back into a single logical line.  The
+   underlying "git mailinfo" was taught to handle this properly.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -196,9 +217,82 @@ notes for details).
    50.
    (merge 07e7dbf jk/reduce-gc-aggressive-depth later to maint).
 
+ * Documentation for individual configuration variables to control use
+   of color (like `color.grep`) said that their default value is
+   'false', instead of saying their default is taken from `color.ui`.
+   When we updated the default value for color.ui from 'false' to
+   'auto' quite a while ago, all of them broke.  This has been
+   corrected.
+   (merge 14d16e2 mm/config-color-ui-default-to-auto later to maint).
+
+ * The pretty-format specifier "%C(auto)" used by the "log" family of
+   commands to enable coloring of the output is taught to also issue a
+   color-reset sequence to the output.
+   (merge c99ad27 rs/c-auto-resets-attributes later to maint).
+
+ * A shell script example in check-ref-format documentation has been
+   fixed.
+   (merge 92dece7 ep/doc-check-ref-format-example later to maint).
+
+ * "git checkout <word>" does not follow the usual disambiguation
+   rules when the <word> can be both a rev and a path, to allow
+   checking out a branch 'foo' in a project that happens to have a
+   file 'foo' in the working tree without having to disambiguate.
+   This was poorly documented and the check was incorrect when the
+   command was run from a subdirectory.
+   (merge b829b94 nd/checkout-disambiguation later to maint).
+
+ * Some codepaths in "git diff" used regexec(3) on a buffer that was
+   mmap(2)ed, which may not have a terminating NUL, leading to a read
+   beyond the end of the mapped region.  This was fixed by introducing
+   a regexec_buf() helper that takes a <ptr,len> pair with REG_STARTEND
+   extension.
+   (merge b7d36ff js/regexec-buf later to maint).
+
+ * The procedure to build Git on Mac OS X for Travis CI hardcoded the
+   internal directory structure we assumed HomeBrew uses, which was a
+   no-no.  The procedure has been updated to ask HomeBrew things we
+   need to know to fix this.
+   (merge f86f49b ls/travis-homebrew-path-fix later to maint).
+
+ * When "git rebase -i" is given a broken instruction, it told the
+   user to fix it with "--edit-todo", but didn't say what the step
+   after that was (i.e. "--continue").
+   (merge 37875b4 rt/rebase-i-broken-insn-advise later to maint).
+
+ * Documentation around tools to import from CVS was fairly outdated.
+   (merge 106b672 jk/doc-cvs-update later to maint).
+
+ * "git clone --recurse-submodules" lost the progress eye-candy in
+   recent update, which has been corrected.
+
+ * A low-level function verify_packfile() was meant to show errors
+   that were detected without dying itself, but under some conditions
+   it didn't and died instead, which has been fixed.
+   (merge a9445d859e jk/verify-packfile-gently later to maint).
+
+ * When "git fetch" tries to find where the history of the repository
+   it runs in has diverged from what the other side has, it has a
+   mechanism to avoid digging too deep into irrelevant side branches.
+   This however did not work well over the "smart-http" transport due
+   to a design bug, which has been fixed.
+   (merge 06b3d386e0 jt/fetch-pack-in-vain-count-with-stateless later to maint).
+
+ * In the codepath that comes up with the hostname to be used in an
+   e-mail when the user didn't tell us, we looked at ai_canonname
+   field in struct addrinfo without making sure it is not NULL first.
+   (merge c375a7efa3 jk/ident-ai-canonname-could-be-null later to maint).
+
+ * "git worktree", even though it used the default_abbrev setting that
+   ought to be affected by core.abbrev configuration variable, ignored
+   the variable setting.  The command has been taught to read the
+   default set of configuration variables to correct this.
+   (merge d49028e6e7 jc/worktree-config later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
    (merge e78d57e bw/pathspec-remove-unused-extern-decl later to maint).
    (merge ce25e4c rs/checkout-some-states-are-const later to maint).
    (merge a8342a4 rs/strbuf-remove-fix later to maint).
    (merge b56aa5b rs/unpack-trees-reduce-file-scope-global later to maint).
    (merge 5efc60c mr/vcs-svn-printf-ulong later to maint).
+   (merge a22ae75 rs/cocci later to maint).
index 6410d7cd983403a35911b75949641fc37e93d16b..e78293b6dbe31cd254cd64c94a045cfe9a2877a9 100644 (file)
@@ -953,7 +953,8 @@ color.branch::
        A boolean to enable/disable color in the output of
        linkgit:git-branch[1]. May be set to `always`,
        `false` (or `never`) or `auto` (or `true`), in which case colors are used
-       only when the output is to a terminal. Defaults to false.
+       only when the output is to a terminal. If unset, then the
+       value of `color.ui` is used (`auto` by default).
 
 color.branch.<slot>::
        Use customized color for branch coloration. `<slot>` is one of
@@ -968,7 +969,8 @@ color.diff::
        linkgit:git-log[1], and linkgit:git-show[1] will use color
        for all patches.  If it is set to `true` or `auto`, those
        commands will only use color when output is to the terminal.
-       Defaults to false.
+       If unset, then the value of `color.ui` is used (`auto` by
+       default).
 +
 This does not affect linkgit:git-format-patch[1] or the
 'git-diff-{asterisk}' plumbing commands.  Can be overridden on the
@@ -991,7 +993,8 @@ color.decorate.<slot>::
 color.grep::
        When set to `always`, always highlight matches.  When `false` (or
        `never`), never.  When set to `true` or `auto`, use color only
-       when the output is written to the terminal.  Defaults to `false`.
+       when the output is written to the terminal.  If unset, then the
+       value of `color.ui` is used (`auto` by default).
 
 color.grep.<slot>::
        Use customized color for grep colorization.  `<slot>` specifies which
@@ -1024,7 +1027,8 @@ color.interactive::
        and displays (such as those used by "git-add --interactive" and
        "git-clean --interactive"). When false (or `never`), never.
        When set to `true` or `auto`, use colors only when the output is
-       to the terminal. Defaults to false.
+       to the terminal. If unset, then the value of `color.ui` is
+       used (`auto` by default).
 
 color.interactive.<slot>::
        Use customized color for 'git add --interactive' and 'git clean
@@ -1040,13 +1044,15 @@ color.showBranch::
        A boolean to enable/disable color in the output of
        linkgit:git-show-branch[1]. May be set to `always`,
        `false` (or `never`) or `auto` (or `true`), in which case colors are used
-       only when the output is to a terminal. Defaults to false.
+       only when the output is to a terminal. If unset, then the
+       value of `color.ui` is used (`auto` by default).
 
 color.status::
        A boolean to enable/disable color in the output of
        linkgit:git-status[1]. May be set to `always`,
        `false` (or `never`) or `auto` (or `true`), in which case colors are used
-       only when the output is to a terminal. Defaults to false.
+       only when the output is to a terminal. If unset, then the
+       value of `color.ui` is used (`auto` by default).
 
 color.status.<slot>::
        Use customized color for status colorization. `<slot>` is
index 0eded24034b57a511425f60c92b5d9119e8950fa..b27a38f8965179fc312531c0500418742cb6641b 100644 (file)
@@ -171,10 +171,11 @@ diff.tool::
 
 include::mergetools-diff.txt[]
 
+diff.indentHeuristic::
 diff.compactionHeuristic::
-       Set this option to `true` to enable an experimental heuristic that
-       shifts the hunk boundary in an attempt to make the resulting
-       patch easier to read.
+       Set one of these options to `true` to enable one of two
+       experimental heuristics that shift diff hunk boundaries to
+       make patches easier to read.
 
 diff.algorithm::
        Choose a diff algorithm.  The variants are as follows:
diff --git a/Documentation/diff-heuristic-options.txt b/Documentation/diff-heuristic-options.txt
new file mode 100644 (file)
index 0000000..36cb549
--- /dev/null
@@ -0,0 +1,7 @@
+--indent-heuristic::
+--no-indent-heuristic::
+--compaction-heuristic::
+--no-compaction-heuristic::
+       These are to help debugging and tuning experimental heuristics
+       (which are off by default) that shift diff hunk boundaries to
+       make patches easier to read.
index 7805a0ccadf27e4fea46dcd95796c570e893e6b3..2d77a196269b2d50de9c661fa8869badf155730a 100644 (file)
@@ -63,12 +63,7 @@ ifndef::git-format-patch[]
        Synonym for `-p --raw`.
 endif::git-format-patch[]
 
---compaction-heuristic::
---no-compaction-heuristic::
-       These are to help debugging and tuning an experimental
-       heuristic (which is off by default) that shifts the hunk
-       boundary in an attempt to make the resulting patch easier
-       to read.
+include::diff-heuristic-options.txt[]
 
 --minimal::
        Spend extra time to make sure the smallest possible
index 05fd482b74ed00445613c7cc9b8a0b019ae31550..94be4b85e099f8b67901a737b8347ee02e6c6a6c 100644 (file)
@@ -23,6 +23,7 @@ familiar command name for people coming from other SCM systems.
 OPTIONS
 -------
 include::blame-options.txt[]
+include::diff-heuristic-options.txt[]
 
 SEE ALSO
 --------
index ba5417567c4df286a95a99cf1ae3e02ee466458f..9dccb3319b2f5faa0ca7fd36b365f30cce5d3e89 100644 (file)
@@ -89,6 +89,8 @@ include::blame-options.txt[]
        abbreviated object name, use <n>+1 digits. Note that 1 column
        is used for a caret to mark the boundary commit.
 
+include::diff-heuristic-options.txt[]
+
 
 THE PORCELAIN FORMAT
 --------------------
index 91a3622ee4e270a11c699e310fb4d544e37845c9..8611a99120eb1c7070b64f80b39b1526225a8019 100644 (file)
@@ -118,8 +118,8 @@ $ git check-ref-format --branch @{-1}
 * Determine the reference name to use for a new branch:
 +
 ------------
-$ ref=$(git check-ref-format --normalize "refs/heads/$newbranch") ||
-die "we do not like '$newbranch' as a branch name."
+$ ref=$(git check-ref-format --normalize "refs/heads/$newbranch")||
+{ echo "we do not like '$newbranch' as a branch name." >&2 ; exit 1 ; }
 ------------
 
 GIT
index 7a2201b0518b2752bcce880e12dd732ed32990d2..8e2c0662ddd72c1bc0765aadbbafd22b62b5adf6 100644 (file)
@@ -419,6 +419,18 @@ $ git reflog -2 HEAD # or
 $ git log -g -2 HEAD
 ------------
 
+ARGUMENT DISAMBIGUATION
+-----------------------
+
+When there is only one argument given and it is not `--` (e.g. "git
+checkout abc"), and when the argument is both a valid `<tree-ish>`
+(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
+or a directory whose name is "abc" exists), Git would usually ask
+you to disambiguate.  Because checking out a branch is so common an
+operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
+in such a situation.  Use `git checkout -- <pathspec>` if you want
+to checkout these paths out of the index.
+
 EXAMPLES
 --------
 
index 41207a24b09cd111a239f4959ab8f121a7459421..de1ebed67d7cbc5ba3859551300b76f20a580ef1 100644 (file)
@@ -22,7 +22,7 @@ DESCRIPTION
 deprecated; it does not work with cvsps version 3 and later.  If you are
 performing a one-shot import of a CVS repository consider using
 http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or
-https://github.com/BartMassey/parsecvs[parsecvs].
+http://www.catb.org/esr/cvs-fast-export/[cvs-fast-export].
 
 Imports a CVS repository into Git. It will either create a new
 repository, or incrementally import into an existing one.
index 9624c84a658c13758cd3b9de55f47d1e89f7b15d..9b200b379bbc980f37033c68342a287637750583 100644 (file)
@@ -19,7 +19,8 @@ SYNOPSIS
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=Message-Id] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
-                  [--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>]
+                  [--rfc] [--subject-prefix=Subject-Prefix]
+                  [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
                   [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
                   [<common diff options>]
@@ -172,6 +173,11 @@ will want to ensure that threading is disabled for `git send-email`.
        allows for useful naming of a patch series, and can be
        combined with the `--numbered` option.
 
+--rfc::
+       Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For
+       Comments"; use this when sending an experimental patch for
+       discussion rather than application.
+
 -v <n>::
 --reroll-count=<n>::
        Mark the series as the <n>-th iteration of the topic. The
index b06e852a85587ffd46820c9dfb832784cbf1dfd9..4c6143c511c06a1fd5f3854445a14533653fd752 100644 (file)
@@ -116,8 +116,12 @@ they create are writable and searchable by other group members.
 Importing a CVS archive
 -----------------------
 
+NOTE: These instructions use the `git-cvsimport` script which ships with
+git, but other importers may provide better results. See the note in
+linkgit:git-cvsimport[1] for other options.
+
 First, install version 2.1 or higher of cvsps from
-http://www.cobite.com/cvsps/[http://www.cobite.com/cvsps/] and make
+https://github.com/andreyvit/cvsps[https://github.com/andreyvit/cvsps] and make
 sure it is in your path.  Then cd to a checked out CVS working directory
 of the project you are interested in and run linkgit:git-cvsimport[1]:
 
index a79e35024623904145c7ac2031f8416bac6e6e23..e6320891b118cc4ddf58293660f8877ac02232ad 100644 (file)
@@ -246,13 +246,20 @@ $highlight_bin::
        Note that 'highlight' feature must be set for gitweb to actually
        use syntax highlighting.
 +
-*NOTE*: if you want to add support for new file type (supported by
-"highlight" but not used by gitweb), you need to modify `%highlight_ext`
-or `%highlight_basename`, depending on whether you detect type of file
-based on extension (for example "sh") or on its basename (for example
-"Makefile").  The keys of these hashes are extension and basename,
-respectively, and value for given key is name of syntax to be passed via
-`--syntax <syntax>` to highlighter.
+*NOTE*: for a file to be highlighted, its syntax type must be detected
+and that syntax must be supported by "highlight".  The default syntax
+detection is minimal, and there are many supported syntax types with no
+detection by default.  There are three options for adding syntax
+detection.  The first and second priority are `%highlight_basename` and
+`%highlight_ext`, which detect based on basename (the full filename, for
+example "Makefile") and extension (for example "sh").  The keys of these
+hashes are the basename and extension, respectively, and the value for a
+given key is the name of the syntax to be passed via `--syntax <syntax>`
+to "highlight".  The last priority is the "highlight" configuration of
+`Shebang` regular expressions to detect the language based on the first
+line in the file, (for example, matching the line "#!/bin/bash").  See
+the highlight documentation and the default config at
+/etc/highlight/filetypes.conf for more details.
 +
 For example if repositories you are hosting use "phtml" extension for
 PHP files, and you want to have correct syntax-highlighting for those
index df4f86bb3039a11eefe3921a969f98fe92b28e4d..1aad150b34f7f2e84bea104538033443644af3a2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -301,7 +301,8 @@ all::
 # crashes due to allocation and free working on different 'heaps'.
 # It's defined automatically if USE_NED_ALLOCATOR is set.
 #
-# Define NO_REGEX if you have no or inferior regex support in your C library.
+# Define NO_REGEX if your C library lacks regex support with REG_STARTEND
+# feature.
 #
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
@@ -461,6 +462,7 @@ CURL_CONFIG = curl-config
 PTHREAD_LIBS = -lpthread
 PTHREAD_CFLAGS =
 GCOV = gcov
+SPATCH = spatch
 
 export TCL_PATH TCLTK_PATH
 
@@ -2308,6 +2310,18 @@ check: common-cmds.h
                exit 1; \
        fi
 
+C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ))
+%.cocci.patch: %.cocci $(C_SOURCES)
+       @echo '    ' SPATCH $<; \
+       for f in $(C_SOURCES); do \
+               $(SPATCH) --sp-file $< $$f; \
+       done >$@ 2>$@.log; \
+       if test -s $@; \
+       then \
+               echo '    ' SPATCH result: $@; \
+       fi
+coccicheck: $(patsubst %.cocci,%.cocci.patch,$(wildcard contrib/coccinelle/*.cocci))
+
 ### Installation rules
 
 ifneq ($(filter /%,$(firstword $(template_dir))),)
@@ -2499,6 +2513,7 @@ clean: profile-clean coverage-clean
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
+       $(RM) contrib/coccinelle/*.cocci.patch*
        $(MAKE) -C Documentation/ clean
 ifndef NO_PERL
        $(MAKE) -C gitweb clean
diff --git a/apply.c b/apply.c
index e32702153c787747ba423fbe60697ab2ce4db3a3..b03d274b522b87e8666bc3703bf0c5f1c9fc9e5b 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -3334,10 +3334,8 @@ static void prepare_fn_table(struct apply_state *state, struct patch *patch)
 static int checkout_target(struct index_state *istate,
                           struct cache_entry *ce, struct stat *st)
 {
-       struct checkout costate;
+       struct checkout costate = CHECKOUT_INIT;
 
-       memset(&costate, 0, sizeof(costate));
-       costate.base_dir = "";
        costate.refresh_cache = 1;
        costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
index b1dddb4ac635ab7e2cc55be546002f8e68efa331..e8fb80b36e7386fa9a9da91a61893c14ec696be4 100644 (file)
@@ -26,10 +26,25 @@ static int patch_interactive, add_interactive, edit_interactive;
 static int take_worktree_changes;
 
 struct update_callback_data {
-       int flags, force_mode;
+       int flags;
        int add_errors;
 };
 
+static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
+{
+       int i;
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+
+               if (pathspec && !ce_path_match(ce, pathspec, NULL))
+                       continue;
+
+               if (chmod_cache_entry(ce, force_mode) < 0)
+                       fprintf(stderr, "cannot chmod '%s'", ce->name);
+       }
+}
+
 static int fix_unmerged_status(struct diff_filepair *p,
                               struct update_callback_data *data)
 {
@@ -65,8 +80,7 @@ static void update_callback(struct diff_queue_struct *q,
                        die(_("unexpected diff status %c"), p->status);
                case DIFF_STATUS_MODIFIED:
                case DIFF_STATUS_TYPE_CHANGED:
-                       if (add_file_to_index(&the_index, path,
-                                       data->flags, data->force_mode)) {
+                       if (add_file_to_index(&the_index, path, data->flags)) {
                                if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
                                        die(_("updating files failed"));
                                data->add_errors++;
@@ -84,15 +98,14 @@ static void update_callback(struct diff_queue_struct *q,
        }
 }
 
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
-       int flags, int force_mode)
+int add_files_to_cache(const char *prefix,
+                      const struct pathspec *pathspec, int flags)
 {
        struct update_callback_data data;
        struct rev_info rev;
 
        memset(&data, 0, sizeof(data));
        data.flags = flags;
-       data.force_mode = force_mode;
 
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
@@ -281,7 +294,7 @@ static int add_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-static int add_files(struct dir_struct *dir, int flags, int force_mode)
+static int add_files(struct dir_struct *dir, int flags)
 {
        int i, exit_status = 0;
 
@@ -294,8 +307,7 @@ static int add_files(struct dir_struct *dir, int flags, int force_mode)
        }
 
        for (i = 0; i < dir->nr; i++)
-               if (add_file_to_index(&the_index, dir->entries[i]->name,
-                               flags, force_mode)) {
+               if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
                        if (!ignore_add_errors)
                                die(_("adding files failed"));
                        exit_status = 1;
@@ -308,7 +320,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int exit_status = 0;
        struct pathspec pathspec;
        struct dir_struct dir;
-       int flags, force_mode;
+       int flags;
        int add_new_files;
        int require_pathspec;
        char *seen = NULL;
@@ -342,13 +354,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        if (!show_only && ignore_missing)
                die(_("Option --ignore-missing can only be used together with --dry-run"));
 
-       if (!chmod_arg)
-               force_mode = 0;
-       else if (!strcmp(chmod_arg, "-x"))
-               force_mode = 0666;
-       else if (!strcmp(chmod_arg, "+x"))
-               force_mode = 0777;
-       else
+       if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
+                         chmod_arg[1] != 'x' || chmod_arg[2]))
                die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
 
        add_new_files = !take_worktree_changes && !refresh_only;
@@ -441,11 +448,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        plug_bulk_checkin();
 
-       exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
+       exit_status |= add_files_to_cache(prefix, &pathspec, flags);
 
        if (add_new_files)
-               exit_status |= add_files(&dir, flags, force_mode);
+               exit_status |= add_files(&dir, flags);
 
+       if (chmod_arg && pathspec.nr)
+               chmod_pathspec(&pathspec, chmod_arg[0]);
        unplug_bulk_checkin();
 
 finish:
index 2ff18b168ecf229ce1a12921c24c2c0bf8ba1a08..a7bd7a6fd80f7d68f58d094bdc5cd6c288fb0e29 100644 (file)
@@ -2220,6 +2220,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (git_diff_heuristic_config(var, value, cb) < 0)
+               return -1;
        if (userdiff_config(var, value) < 0)
                return -1;
 
@@ -2550,6 +2552,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
                OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+
+               /*
+                * The following two options are parsed by parse_revision_opt()
+                * and are only included here to get included in the "-h"
+                * output:
+                */
+               { OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental indent-based heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
+               { OPTION_LOWLEVEL_CALLBACK, 0, "compaction-heuristic", NULL, NULL, N_("Use an experimental blank-line-based heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
+
                OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
                OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
                OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
@@ -2596,6 +2607,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
 parse_done:
        no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
+       xdl_opts |= revs.diffopt.xdl_opts & (XDF_COMPACTION_HEURISTIC | XDF_INDENT_HEURISTIC);
        DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
        argc = parse_options_end(&ctx);
 
index 92c69672e96f6e7fc6bbe7b783c551474224f18b..30a49d9f424c0a1e04b321b5eb0a5e25462a53af 100644 (file)
@@ -16,7 +16,7 @@ static int checkout_stage; /* default to checkout stage0 */
 static int to_tempfile;
 static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
 
-static struct checkout state;
+static struct checkout state = CHECKOUT_INIT;
 
 static void write_tempfile_record(const char *name, const char *prefix)
 {
index 9941abc3acdfa035387606f7ecc8f33905f80e7d..9b2a5b31d423ae2f27756df8deab2d9f2fe18ddc 100644 (file)
@@ -239,7 +239,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                          const char *revision)
 {
        int pos;
-       struct checkout state;
+       struct checkout state = CHECKOUT_INIT;
        static char *ps_matched;
        struct object_id rev;
        struct commit *head;
@@ -352,7 +352,6 @@ static int checkout_paths(const struct checkout_opts *opts,
                return 1;
 
        /* Now we are committed to check them out */
-       memset(&state, 0, sizeof(state));
        state.force = 1;
        state.refresh_cache = 1;
        state.istate = &the_index;
@@ -548,7 +547,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                         * entries in the index.
                         */
 
-                       add_files_to_cache(NULL, NULL, 0, 0);
+                       add_files_to_cache(NULL, NULL, 0);
                        /*
                         * NEEDSWORK: carrying over local changes
                         * when branches have different end-of-line
@@ -985,7 +984,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                int recover_with_dwim = dwim_new_local_branch_ok;
 
                if (!has_dash_dash &&
-                   (check_filename(NULL, arg) || !no_wildcard(arg)))
+                   (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
                        recover_with_dwim = 0;
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
@@ -1038,7 +1037,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 
        if (!*source_tree)                   /* case (1): want a tree */
                die(_("reference is not a tree: %s"), arg);
-       if (!has_dash_dash) {/* case (3).(d) -> (1) */
+       if (!has_dash_dash) {   /* case (3).(d) -> (1) */
                /*
                 * Do not complain the most common case
                 *      git checkout branch
@@ -1046,7 +1045,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                 * it would be extremely annoying.
                 */
                if (argc)
-                       verify_non_filename(NULL, arg);
+                       verify_non_filename(opts->prefix, arg);
        } else {
                argcount++;
                argv++;
index 404c5e80226c81a8f051e63fa8e40286fea95a18..fb75f7ee64a29e4bb79f163e82c73bb8a7bd5280 100644 (file)
@@ -670,7 +670,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
        }
 }
 
-static int checkout(void)
+static int checkout(int submodule_progress)
 {
        unsigned char sha1[20];
        char *head;
@@ -734,6 +734,9 @@ static int checkout(void)
                if (max_jobs != -1)
                        argv_array_pushf(&args, "--jobs=%d", max_jobs);
 
+               if (submodule_progress)
+                       argv_array_push(&args, "--progress");
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
@@ -841,6 +844,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        const char *src_ref_prefix = "refs/heads/";
        struct remote *remote;
        int err = 0, complete_refs_before_fetch = 1;
+       int submodule_progress;
 
        struct refspec *refspec;
        const char *fetch_pattern;
@@ -931,16 +935,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                set_git_work_tree(work_tree);
        }
 
-       junk_git_dir = git_dir;
+       junk_git_dir = real_git_dir ? real_git_dir : git_dir;
        if (safe_create_leading_directories_const(git_dir) < 0)
                die(_("could not create leading directories of '%s'"), git_dir);
 
-       set_git_dir_init(git_dir, real_git_dir, 0);
-       if (real_git_dir) {
-               git_dir = real_git_dir;
-               junk_git_dir = real_git_dir;
-       }
-
        if (0 <= option_verbosity) {
                if (option_bare)
                        fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir);
@@ -966,7 +964,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                }
        }
 
-       init_db(option_template, INIT_DB_QUIET);
+       init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+
+       if (real_git_dir)
+               git_dir = real_git_dir;
+
        write_config(&option_config);
 
        git_config(git_default_config, NULL);
@@ -1099,6 +1101,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
+       /*
+        * We want to show progress for recursive submodule clones iff
+        * we did so for the main clone. But only the transport knows
+        * the final decision for this flag, so we need to rescue the value
+        * before we free the transport.
+        */
+       submodule_progress = transport->progress;
+
        transport_unlock_pack(transport);
        transport_disconnect(transport);
 
@@ -1108,7 +1118,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        junk_mode = JUNK_LEAVE_REPO;
-       err = checkout();
+       err = checkout(submodule_progress);
 
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
index bb9f79b6ef4b5cd01cafac551bb6665a433334aa..1cba3b75c8dc121cf86558aa96726ea99f28eba3 100644 (file)
@@ -397,7 +397,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
         */
        if (all || (also && pathspec.nr)) {
                hold_locked_index(&index_lock, 1);
-               add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
+               add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
index ac84e99f3a6c073409ab7514aacdad7e722c6255..dc2e9e420d6beb47f0e04184dfb7429b6559a789 100644 (file)
@@ -395,7 +395,7 @@ static void shortlog(const char *name,
 
        for (i = 0; i < subjects.nr; i++)
                if (i >= limit)
-                       strbuf_addf(out, "  ...\n");
+                       strbuf_addstr(out, "  ...\n");
                else
                        strbuf_addf(out, "  %s\n", subjects.items[i].string);
 
index 72e81447ae84c8e69799fff5a6fe7050241bea9b..2399b97d902668a08c3c881d544f4d6a88f4e263 100644 (file)
@@ -22,7 +22,6 @@
 static int init_is_bare_repository = 0;
 static int init_shared_repository = -1;
 static const char *init_db_template_dir;
-static const char *git_link;
 
 static void copy_templates_1(struct strbuf *path, struct strbuf *template,
                             DIR *dir)
@@ -138,7 +137,7 @@ static void copy_templates(const char *template_dir)
                goto close_free_return;
        }
 
-       strbuf_addstr(&path, get_git_dir());
+       strbuf_addstr(&path, get_git_common_dir());
        strbuf_complete(&path, '/');
        copy_templates_1(&path, &template_path, dir);
 close_free_return:
@@ -171,7 +170,8 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
        return 1;
 }
 
-static int create_default_files(const char *template_path)
+static int create_default_files(const char *template_path,
+                               const char *original_git_dir)
 {
        struct stat st1;
        struct strbuf buf = STRBUF_INIT;
@@ -264,7 +264,7 @@ static int create_default_files(const char *template_path)
                /* allow template config file to override the default */
                if (log_all_ref_updates == -1)
                        git_config_set("core.logallrefupdates", "true");
-               if (needs_work_tree_config(get_git_dir(), work_tree))
+               if (needs_work_tree_config(original_git_dir, work_tree))
                        git_config_set("core.worktree", work_tree);
        }
 
@@ -312,34 +312,7 @@ static void create_object_directory(void)
        strbuf_release(&path);
 }
 
-int set_git_dir_init(const char *git_dir, const char *real_git_dir,
-                    int exist_ok)
-{
-       if (real_git_dir) {
-               struct stat st;
-
-               if (!exist_ok && !stat(git_dir, &st))
-                       die(_("%s already exists"), git_dir);
-
-               if (!exist_ok && !stat(real_git_dir, &st))
-                       die(_("%s already exists"), real_git_dir);
-
-               /*
-                * make sure symlinks are resolved because we'll be
-                * moving the target repo later on in separate_git_dir()
-                */
-               git_link = xstrdup(real_path(git_dir));
-               set_git_dir(real_path(real_git_dir));
-       }
-       else {
-               set_git_dir(real_path(git_dir));
-               git_link = NULL;
-       }
-       startup_info->have_repository = 1;
-       return 0;
-}
-
-static void separate_git_dir(const char *git_dir)
+static void separate_git_dir(const char *git_dir, const char *git_link)
 {
        struct stat st;
 
@@ -360,13 +333,31 @@ static void separate_git_dir(const char *git_dir)
        write_file(git_link, "gitdir: %s", git_dir);
 }
 
-int init_db(const char *template_dir, unsigned int flags)
+int init_db(const char *git_dir, const char *real_git_dir,
+           const char *template_dir, unsigned int flags)
 {
        int reinit;
-       const char *git_dir = get_git_dir();
+       int exist_ok = flags & INIT_DB_EXIST_OK;
+       char *original_git_dir = xstrdup(real_path(git_dir));
+
+       if (real_git_dir) {
+               struct stat st;
+
+               if (!exist_ok && !stat(git_dir, &st))
+                       die(_("%s already exists"), git_dir);
+
+               if (!exist_ok && !stat(real_git_dir, &st))
+                       die(_("%s already exists"), real_git_dir);
 
-       if (git_link)
-               separate_git_dir(git_dir);
+               set_git_dir(real_path(real_git_dir));
+               git_dir = get_git_dir();
+               separate_git_dir(git_dir, original_git_dir);
+       }
+       else {
+               set_git_dir(real_path(git_dir));
+               git_dir = get_git_dir();
+       }
+       startup_info->have_repository = 1;
 
        safe_create_dir(git_dir, 0);
 
@@ -379,7 +370,7 @@ int init_db(const char *template_dir, unsigned int flags)
         */
        check_repository_format();
 
-       reinit = create_default_files(template_dir);
+       reinit = create_default_files(template_dir, original_git_dir);
 
        create_object_directory();
 
@@ -419,6 +410,7 @@ int init_db(const char *template_dir, unsigned int flags)
                               git_dir, len && git_dir[len-1] != '/' ? "/" : "");
        }
 
+       free(original_git_dir);
        return 0;
 }
 
@@ -586,7 +578,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        set_git_work_tree(work_tree);
        }
 
-       set_git_dir_init(git_dir, real_git_dir, 1);
-
-       return init_db(template_dir, flags);
+       flags |= INIT_DB_EXIST_OK;
+       return init_db(git_dir, real_git_dir, template_dir, flags);
 }
index b8cdf2b9d9cbd30950810eaa5dd5091dc23eb3c6..55d20cc2d88ab03d96f0476b222be3b2f6832ed9 100644 (file)
@@ -1111,6 +1111,11 @@ static int subject_prefix_callback(const struct option *opt, const char *arg,
        return 0;
 }
 
+static int rfc_callback(const struct option *opt, const char *arg, int unset)
+{
+       return subject_prefix_callback(opt, "RFC PATCH", unset);
+}
+
 static int numbered_cmdline_opt = 0;
 
 static int numbered_callback(const struct option *opt, const char *arg,
@@ -1418,6 +1423,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            N_("start numbering patches at <n> instead of 1")),
                OPT_INTEGER('v', "reroll-count", &reroll_count,
                            N_("mark the series as Nth re-roll")),
+               { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
+                           N_("Use [RFC PATCH] instead of [PATCH]"),
+                           PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
                { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
                            N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1556,7 +1564,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (numbered && keep_subject)
                die (_("-n and -k are mutually exclusive."));
        if (keep_subject && subject_prefix)
-               die (_("--subject-prefix and -k are mutually exclusive."));
+               die (_("--subject-prefix/--rfc and -k are mutually exclusive."));
        rev.preserve_subject = keep_subject;
 
        argc = setup_revisions(argc, argv, &rev, &s_r_opt);
index 0ae099f746680e5c2fcc55dff56624da1619f2f1..a8b57c7d9856518fdcd4c3819f0656544eadf022 100644 (file)
@@ -940,7 +940,7 @@ static void write_merge_state(struct commit_list *remoteheads)
 
        strbuf_reset(&buf);
        if (fast_forward == FF_NO)
-               strbuf_addf(&buf, "no-ff");
+               strbuf_addstr(&buf, "no-ff");
        write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
 }
 
index 446a316738e0e739b6a89992879c6e4838f23c8f..2f43877bc9a17c5bef2906a383cc8cdd6f4f5b82 100644 (file)
@@ -26,7 +26,7 @@ static const char **internal_copy_pathspec(const char *prefix,
        int i;
        const char **result;
        ALLOC_ARRAY(result, count + 1);
-       memcpy(result, pathspec, count * sizeof(const char *));
+       COPY_ARRAY(result, pathspec, count);
        result[count] = NULL;
        for (i = 0; i < count; i++) {
                int length = strlen(result[i]);
index 7b8ddfe6cf26c78198c339391fd75836b8bfcd32..e3fdc0aa7883e1050f4fe8a165ec6245ab9abc9e 100644 (file)
@@ -443,7 +443,8 @@ static int module_name(int argc, const char **argv, const char *prefix)
 }
 
 static int clone_submodule(const char *path, const char *gitdir, const char *url,
-                          const char *depth, struct string_list *reference, int quiet)
+                          const char *depth, struct string_list *reference,
+                          int quiet, int progress)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
 
@@ -451,6 +452,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
        argv_array_push(&cp.args, "--no-checkout");
        if (quiet)
                argv_array_push(&cp.args, "--quiet");
+       if (progress)
+               argv_array_push(&cp.args, "--progress");
        if (depth && *depth)
                argv_array_pushl(&cp.args, "--depth", depth, NULL);
        if (reference->nr) {
@@ -575,6 +578,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 {
        const char *name = NULL, *url = NULL, *depth = NULL;
        int quiet = 0;
+       int progress = 0;
        FILE *submodule_dot_git;
        char *p, *path = NULL, *sm_gitdir;
        struct strbuf rel_path = STRBUF_INIT;
@@ -601,6 +605,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
                           N_("string"),
                           N_("depth for shallow clones")),
                OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+               OPT_BOOL(0, "progress", &progress,
+                          N_("force cloning progress")),
                OPT_END()
        };
 
@@ -634,7 +640,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
 
                prepare_possible_alternates(name, &reference);
 
-               if (clone_submodule(path, sm_gitdir, url, depth, &reference, quiet))
+               if (clone_submodule(path, sm_gitdir, url, depth, &reference,
+                                   quiet, progress))
                        die(_("clone of '%s' into submodule path '%s' failed"),
                            url, path);
        } else {
@@ -684,6 +691,7 @@ struct submodule_update_clone {
        struct submodule_update_strategy update;
 
        /* configuration parameters which are passed on to the children */
+       int progress;
        int quiet;
        int recommend_shallow;
        struct string_list references;
@@ -702,7 +710,7 @@ struct submodule_update_clone {
        int failed_clones_nr, failed_clones_alloc;
 };
 #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
-       SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, STRING_LIST_INIT_DUP, \
+       SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
        NULL, NULL, NULL, \
        STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 
@@ -804,6 +812,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        child->err = -1;
        argv_array_push(&child->args, "submodule--helper");
        argv_array_push(&child->args, "clone");
+       if (suc->progress)
+               argv_array_push(&child->args, "--progress");
        if (suc->quiet)
                argv_array_push(&child->args, "--quiet");
        if (suc->prefix)
@@ -860,8 +870,9 @@ static int update_clone_get_next_task(struct child_process *child,
                ce = suc->failed_clones[index];
                if (!prepare_to_clone_next_submodule(ce, child, suc, err)) {
                        suc->current ++;
-                       strbuf_addf(err, "BUG: submodule considered for cloning,"
-                                   "doesn't need cloning any more?\n");
+                       strbuf_addstr(err, "BUG: submodule considered for "
+                                          "cloning, doesn't need cloning "
+                                          "any more?\n");
                        return 0;
                }
                p = xmalloc(sizeof(*p));
@@ -950,6 +961,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
                OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+               OPT_BOOL(0, "progress", &suc.progress,
+                           N_("force cloning progress")),
                OPT_END()
        };
 
index 73f6b3e1bec362fe8ec6ed78dee0edbb5155403c..f3f07e7f1cb2d952144bf98969b481dc331155bf 100644 (file)
@@ -419,30 +419,18 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
        return 0;
 }
 
-static void chmod_path(int flip, const char *path)
+static void chmod_path(char flip, const char *path)
 {
        int pos;
        struct cache_entry *ce;
-       unsigned int mode;
 
        pos = cache_name_pos(path, strlen(path));
        if (pos < 0)
                goto fail;
        ce = active_cache[pos];
-       mode = ce->ce_mode;
-       if (!S_ISREG(mode))
-               goto fail;
-       switch (flip) {
-       case '+':
-               ce->ce_mode |= 0111; break;
-       case '-':
-               ce->ce_mode &= ~0111; break;
-       default:
+       if (chmod_cache_entry(ce, flip) < 0)
                goto fail;
-       }
-       cache_tree_invalidate_path(&the_index, path);
-       ce->ce_flags |= CE_UPDATE_IN_BASE;
-       active_cache_changed |= CE_ENTRY_CHANGED;
+
        report("chmod %cx '%s'", flip, path);
        return;
  fail:
index 6dcf7bd9d27004277fc568989d58a6236ed6867e..5c4854d3e4a679f59f4a7db7cb3f37bd0e210622 100644 (file)
@@ -528,6 +528,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
+
        if (ac < 2)
                usage_with_options(worktree_usage, options);
        if (!prefix)
diff --git a/cache.h b/cache.h
index d0494c85338b9063b3abe6e2081c921617ca0203..6fc0e5ae68a6965c8be3b822400145095b56f755 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -367,8 +367,9 @@ extern void free_name_hash(struct index_state *istate);
 #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
 #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
-#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
-#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
+#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
+#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
+#define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@ -525,9 +526,10 @@ extern void verify_non_filename(const char *prefix, const char *name);
 extern int path_inside_repo(const char *prefix, const char *path);
 
 #define INIT_DB_QUIET 0x0001
+#define INIT_DB_EXIST_OK 0x0002
 
-extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
-extern int init_db(const char *template_dir, unsigned int flags);
+extern int init_db(const char *git_dir, const char *real_git_dir,
+                  const char *template_dir, unsigned int flags);
 
 extern void sanitize_stdfds(void);
 extern int daemonize(void);
@@ -587,9 +589,10 @@ extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_IGNORE_ERRORS        4
 #define ADD_CACHE_IGNORE_REMOVAL 8
 #define ADD_CACHE_INTENT 16
-extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
-extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
+extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
+extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
 extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
 extern int index_name_is_other(const struct index_state *, const char *, int);
@@ -1354,6 +1357,7 @@ struct checkout {
                 not_new:1,
                 refresh_cache:1;
 };
+#define CHECKOUT_INIT { NULL, "" }
 
 #define TEMPORARY_FILENAME_LENGTH 25
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
@@ -1858,7 +1862,7 @@ void packet_trace_identity(const char *prog);
  * return 0 if success, 1 - if addition of a file failed and
  * ADD_FILES_IGNORE_ERRORS was specified in flags
  */
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
index ba6dee37aa36cc7141649deda6407012cd0f2724..856fd4aeeff654c45ba0a6149f9db2c22806e0cb 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -931,7 +931,7 @@ static int remove_redundant(struct commit **array, int cnt)
        }
 
        /* Now collect the result */
-       memcpy(work, array, sizeof(*array) * cnt);
+       COPY_ARRAY(work, array, cnt);
        for (i = filled = 0; i < cnt; i++)
                if (!redundant[i])
                        array[filled++] = work[i];
@@ -1511,9 +1511,9 @@ static int verify_utf8(struct strbuf *buf)
 }
 
 static const char commit_utf8_warn[] =
-"Warning: commit message did not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
+N_("Warning: commit message did not conform to UTF-8.\n"
+   "You may want to amend it after fixing the message, or set the config\n"
+   "variable i18n.commitencoding to the encoding your project uses.\n");
 
 int commit_tree_extended(const char *msg, size_t msg_len,
                         const unsigned char *tree,
@@ -1566,7 +1566,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
 
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
-               fprintf(stderr, commit_utf8_warn);
+               fprintf(stderr, _(commit_utf8_warn));
 
        if (sign_commit && do_sign_commit(&buffer, sign_commit))
                return -1;
index 7224b5ecc79beebe3182b301bdf0e661f6a246fd..d99d6435fd01754e3b0f23537d7e1b385a26f262 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -46,11 +46,11 @@ int check_ref_type(const struct ref *ref, int flags)
 static void die_initial_contact(int unexpected)
 {
        if (unexpected)
-               die("The remote end hung up upon initial contact");
+               die(_("The remote end hung up upon initial contact"));
        else
-               die("Could not read from remote repository.\n\n"
-                   "Please make sure you have the correct access rights\n"
-                   "and the repository exists.");
+               die(_("Could not read from remote repository.\n\n"
+                     "Please make sure you have the correct access rights\n"
+                     "and the repository exists."));
 }
 
 static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
new file mode 100644 (file)
index 0000000..2d7f25d
--- /dev/null
@@ -0,0 +1,26 @@
+@@
+type T;
+T *dst;
+T *src;
+expression n;
+@@
+- memcpy(dst, src, n * sizeof(*dst));
++ COPY_ARRAY(dst, src, n);
+
+@@
+type T;
+T *dst;
+T *src;
+expression n;
+@@
+- memcpy(dst, src, n * sizeof(*src));
++ COPY_ARRAY(dst, src, n);
+
+@@
+type T;
+T *dst;
+T *src;
+expression n;
+@@
+- memcpy(dst, src, n * sizeof(T));
++ COPY_ARRAY(dst, src, n);
index 8ccdbb5666cbc89cf85ce90f63d357b86c37bd30..0307624a03fc4c4fd5642d2b4ad853cfba5b6905 100644 (file)
@@ -23,16 +23,16 @@ expression E1;
 + oid_to_hex(E1)
 
 @@
-expression E1;
+expression E1, E2;
 @@
-- sha1_to_hex_r(E1.hash)
-+ oid_to_hex_r(&E1)
+- sha1_to_hex_r(E1, E2.hash)
++ oid_to_hex_r(E1, &E2)
 
 @@
-expression E1;
+expression E1, E2;
 @@
-- sha1_to_hex_r(E1->hash)
-+ oid_to_hex_r(E1)
+- sha1_to_hex_r(E1, E2->hash)
++ oid_to_hex_r(E1, E2)
 
 @@
 expression E1;
diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci
new file mode 100644 (file)
index 0000000..7932d48
--- /dev/null
@@ -0,0 +1,5 @@
+@@
+expression E1, E2;
+@@
+- strbuf_addf(E1, E2);
++ strbuf_addstr(E1, E2);
diff --git a/diff.c b/diff.c
index c6da383c563c6cf248843e59ccb38c4ea24fd1f7..a178ed39bc77988dcc6282d252002f5446305628 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -27,6 +27,7 @@
 #endif
 
 static int diff_detect_rename_default;
+static int diff_indent_heuristic; /* experimental */
 static int diff_compaction_heuristic; /* experimental */
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
@@ -55,6 +56,11 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL,       /* FUNCINFO */
 };
 
+static NORETURN void die_want_option(const char *option_name)
+{
+       die(_("option '%s' requires a value"), option_name);
+}
+
 static int parse_diff_color_slot(const char *var)
 {
        if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
@@ -177,6 +183,21 @@ void init_diff_ui_defaults(void)
        diff_detect_rename_default = 1;
 }
 
+int git_diff_heuristic_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "diff.indentheuristic")) {
+               diff_indent_heuristic = git_config_bool(var, value);
+               if (diff_indent_heuristic)
+                       diff_compaction_heuristic = 0;
+       }
+       if (!strcmp(var, "diff.compactionheuristic")) {
+               diff_compaction_heuristic = git_config_bool(var, value);
+               if (diff_compaction_heuristic)
+                       diff_indent_heuristic = 0;
+       }
+       return 0;
+}
+
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -193,10 +214,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
-       if (!strcmp(var, "diff.compactionheuristic")) {
-               diff_compaction_heuristic = git_config_bool(var, value);
-               return 0;
-       }
        if (!strcmp(var, "diff.autorefreshindex")) {
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
@@ -237,6 +254,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (git_diff_heuristic_config(var, value, cb) < 0)
+               return -1;
        if (git_color_config(var, value, cb) < 0)
                return -1;
 
@@ -952,7 +971,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
 {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
-               if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+               if (!regexec_buf(word_regex, buffer->ptr + *begin,
+                                buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
@@ -3296,7 +3316,9 @@ void diff_setup(struct diff_options *options)
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
-       if (diff_compaction_heuristic)
+       if (diff_indent_heuristic)
+               DIFF_XDL_SET(options, INDENT_HEURISTIC);
+       else if (diff_compaction_heuristic)
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 
        options->orderfile = diff_order_file_cfg;
@@ -3325,7 +3347,7 @@ void diff_setup_done(struct diff_options *options)
        if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
                count++;
        if (count > 1)
-               die("--name-only, --name-status, --check and -s are mutually exclusive");
+               die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
 
        /*
         * Most of the time we can say "there are changes"
@@ -3521,7 +3543,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-width' requires a value");
+                               die_want_option("--stat-width");
                        else if (!*arg) {
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3530,7 +3552,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-name-width' requires a value");
+                               die_want_option("--stat-name-width");
                        else if (!*arg) {
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3539,7 +3561,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-graph-width' requires a value");
+                               die_want_option("--stat-graph-width");
                        else if (!*arg) {
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3548,7 +3570,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-count' requires a value");
+                               die_want_option("--stat-count");
                        else if (!*arg) {
                                count = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3818,9 +3840,15 @@ int diff_opt_parse(struct diff_options *options,
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
-       else if (!strcmp(arg, "--compaction-heuristic"))
+       else if (!strcmp(arg, "--indent-heuristic")) {
+               DIFF_XDL_SET(options, INDENT_HEURISTIC);
+               DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
+       } else if (!strcmp(arg, "--no-indent-heuristic"))
+               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+       else if (!strcmp(arg, "--compaction-heuristic")) {
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
-       else if (!strcmp(arg, "--no-compaction-heuristic"))
+               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+       } else if (!strcmp(arg, "--no-compaction-heuristic"))
                DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
diff --git a/diff.h b/diff.h
index ec76a90522ea88354882666a5a6d336dfa8963fd..25ae60d5ff6c1267dbb6277b55c1029c4792d3f3 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -273,6 +273,7 @@ extern int parse_long_opt(const char *opt, const char **argv,
                         const char **optarg);
 
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
+extern int git_diff_heuristic_config(const char *var, const char *value, void *cb);
 extern void init_diff_ui_defaults(void);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern void diff_setup(struct diff_options *);
index 55067cab6c2dbfb6abbfffa0aa9d7259502041cb..9795ca1c159a5177b1b7031a87c8f8bd5e5be3d5 100644 (file)
@@ -23,7 +23,6 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
 {
        struct diffgrep_cb *data = priv;
        regmatch_t regmatch;
-       int hold;
 
        if (line[0] != '+' && line[0] != '-')
                return;
@@ -33,11 +32,8 @@ static void diffgrep_consume(void *priv, char *line, unsigned long len)
                 * caller early.
                 */
                return;
-       /* Yuck -- line ought to be "const char *"! */
-       hold = line[len];
-       line[len] = '\0';
-       data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
-       line[len] = hold;
+       data->hit = !regexec_buf(data->regexp, line + 1, len - 1, 1,
+                                &regmatch, 0);
 }
 
 static int diff_grep(mmfile_t *one, mmfile_t *two,
@@ -50,9 +46,11 @@ static int diff_grep(mmfile_t *one, mmfile_t *two,
        xdemitconf_t xecfg;
 
        if (!one)
-               return !regexec(regexp, two->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, two->ptr, two->size,
+                                   1, &regmatch, 0);
        if (!two)
-               return !regexec(regexp, one->ptr, 1, &regmatch, 0);
+               return !regexec_buf(regexp, one->ptr, one->size,
+                                   1, &regmatch, 0);
 
        /*
         * We have both sides; need to run textual diff and see if
@@ -83,8 +81,8 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
                regmatch_t regmatch;
                int flags = 0;
 
-               assert(data[sz] == '\0');
-               while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
+               while (*data &&
+                      !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
                        flags |= REG_NOTBOL;
                        data += regmatch.rm_eo;
                        if (*data && regmatch.rm_so == regmatch.rm_eo)
index 85e77af61d05b492d6448fd4ad33abc2c348eaf1..413937e7404d163883d5d0456ebefc4e1504cd87 100644 (file)
@@ -428,10 +428,17 @@ static int find_common(struct fetch_pack_args *args,
                                                const char *hex = sha1_to_hex(result_sha1);
                                                packet_buf_write(&req_buf, "have %s\n", hex);
                                                state_len = req_buf.len;
-                                       }
+                                               /*
+                                                * Reset in_vain because an ack
+                                                * for this commit has not been
+                                                * seen.
+                                                */
+                                               in_vain = 0;
+                                       } else if (!args->stateless_rpc
+                                                  || ack != ACK_common)
+                                               in_vain = 0;
                                        mark_common(commit, 0, 1);
                                        retval = 0;
-                                       in_vain = 0;
                                        got_continue = 1;
                                        if (ack == ACK_ready) {
                                                clear_prio_queue(&rev_list);
index 642cce1ac6e158207f4273d4e1bf1e7928865b18..ee3d812695fa232f30682b0e546105ca0eea5d49 100755 (executable)
@@ -45,6 +45,7 @@
 my $normal_color = $repo->get_color("", "reset");
 
 my $diff_algorithm = $repo->config('diff.algorithm');
+my $diff_indent_heuristic = $repo->config_bool('diff.indentheuristic');
 my $diff_compaction_heuristic = $repo->config_bool('diff.compactionheuristic');
 my $diff_filter = $repo->config('interactive.difffilter');
 
@@ -750,7 +751,9 @@ sub parse_diff {
        if (defined $diff_algorithm) {
                splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
        }
-       if ($diff_compaction_heuristic) {
+       if ($diff_indent_heuristic) {
+               splice @diff_cmd, 1, 0, "--indent-heuristic";
+       } elsif ($diff_compaction_heuristic) {
                splice @diff_cmd, 1, 0, "--compaction-heuristic";
        }
        if (defined $patch_mode_revision) {
index 37cce0746f460ae6c839e2556c526e0fd41cdf65..0ce2cdfb98d54e35881a94e9037e7380ec5b821c 100644 (file)
@@ -801,6 +801,14 @@ extern FILE *fopen_for_writing(const char *path);
 #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
 #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
 
+#define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
+       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
+{
+       if (n)
+               memcpy(dst, src, st_mult(size, n));
+}
+
 /*
  * These functions help you allocate structs with flex arrays, and copy
  * the data directly into the array. For example, if you had:
@@ -977,6 +985,19 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define qsort git_qsort
 #endif
 
+#ifndef REG_STARTEND
+#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
+#endif
+
+static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
+                             size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+       assert(nmatch > 0 && pmatch);
+       pmatch[0].rm_so = 0;
+       pmatch[0].rm_eo = size;
+       return regexec(preg, buf, nmatch, pmatch, eflags | REG_STARTEND);
+}
+
 #ifndef DIR_HAS_BSD_GROUP_SEMANTICS
 # define FORCE_DIR_SET_GID S_ISGID
 #else
index 74a81a7b42779c006fb0c0060724359fca2b33d9..3a3e534aeff723b080a7c9a758f51e81e88fae23 100644 (file)
@@ -291,7 +291,7 @@ proc do_unstage_selection {} {
 
        if {[array size selected_paths] > 0} {
                unstage_helper \
-                       {Unstaging selected files from commit} \
+                       [mc "Unstaging selected files from commit"] \
                        [array names selected_paths]
        } elseif {$current_diff_path ne {}} {
                unstage_helper \
@@ -343,7 +343,7 @@ proc do_add_selection {} {
 
        if {[array size selected_paths] > 0} {
                add_helper \
-                       {Adding selected files} \
+                       [mc "Adding selected files"] \
                        [array names selected_paths]
        } elseif {$current_diff_path ne {}} {
                add_helper \
@@ -385,7 +385,7 @@ proc do_add_all {} {
                        set paths [concat $paths $untracked_paths]
                }
        }
-       add_helper {Adding all changed files} $paths
+       add_helper [mc "Adding all changed files"] $paths
 }
 
 proc revert_helper {txt paths} {
index 460d32fa22fc77f6621e5ec9a9c6ba6dc98050c2..5ab6f8f10210a0e394315e247a2895d4577569f4 100644 (file)
@@ -112,12 +112,7 @@ method _start {} {
        close $fh
        set _last_merged_branch $branch
 
-       set cmd [list git]
-       lappend cmd merge
-       lappend cmd --strategy=recursive
-       lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]]
-       lappend cmd HEAD
-       lappend cmd $name
+       set cmd [list git merge --strategy=recursive FETCH_HEAD]
 
        ui_status [mc "Merging %s and %s..." $current_branch $stitle]
        set cons [console::new [mc "Merge"] "merge $stitle"]
diff --git a/git-gui/po/glossary/pt_pt.po b/git-gui/po/glossary/pt_pt.po
new file mode 100644 (file)
index 0000000..adc3b54
--- /dev/null
@@ -0,0 +1,293 @@
+# Portuguese translations for git-gui glossary.
+# Copyright (C) 2016 Shawn Pearce, et al.
+# This file is distributed under the same license as the git package.
+# Vasco Almeida <vascomalmeida@sapo.pt>, 2016.
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui glossary\n"
+"POT-Creation-Date: 2016-05-06 10:22+0000\n"
+"PO-Revision-Date: 2016-05-06 12:32+0000\n"
+"Last-Translator: Vasco Almeida <vascomalmeida@sapo.pt>\n"
+"Language-Team: Portuguese\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+
+#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)"
+msgid ""
+"English Term (Dear translator: This file will never be visible to the user!)"
+msgstr ""
+"Outro SCM em português:\n"
+"http://svn.code.sf.net/p/tortoisesvn/code/trunk/Languages/pt/TortoiseUI.po e "
+"\n"
+"http://svn.code.sf.net/p/tortoisesvn/code/trunk/Languages/pt/TortoiseDoc.po\n"
+" em html: https://tortoisesvn.net/docs/release/TortoiseSVN_pt/index.html\n"
+"\n"
+"https://translations.launchpad.net/tortoisehg (medíocre)"
+
+#. ""
+msgid "amend"
+msgstr "emendar"
+
+#. ""
+msgid "annotate"
+msgstr "anotar"
+
+#. "A 'branch' is an active line of development."
+msgid "branch [noun]"
+msgstr "ramo"
+
+#. ""
+msgid "branch [verb]"
+msgstr "criar ramo"
+
+#. ""
+msgid "checkout [noun]"
+msgstr "extração"
+
+#. "The action of updating the working tree to a revision which was stored in the object database."
+msgid "checkout [verb]"
+msgstr "extrair"
+
+#. ""
+msgid "clone [verb]"
+msgstr "clonar"
+
+#. "A single point in the git history."
+msgid "commit [noun]"
+msgstr "commit"
+
+#. "The action of storing a new snapshot of the project's state in the git history."
+msgid "commit [verb]"
+msgstr "submeter"
+
+#. ""
+msgid "diff [noun]"
+msgstr "diferenças"
+
+#. ""
+msgid "diff [verb]"
+msgstr "mostrar diferenças"
+
+#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have."
+msgid "fast forward merge"
+msgstr "integração por avanço rápido"
+
+#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too."
+msgid "fetch"
+msgstr "obter"
+
+#. "One context of consecutive lines in a whole patch, which consists of many such hunks"
+msgid "hunk"
+msgstr "excerto"
+
+#. "A collection of files. The index is a stored version of your working tree."
+msgid "index (in git-gui: staging area)"
+msgstr "índice"
+
+#. "A successful merge results in the creation of a new commit representing the result of the merge."
+msgid "merge [noun]"
+msgstr "integração"
+
+#. "To bring the contents of another branch into the current branch."
+msgid "merge [verb]"
+msgstr "integrar"
+
+#. ""
+msgid "message"
+msgstr "mensagem"
+
+#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'."
+msgid "prune"
+msgstr "podar"
+
+#. "Pulling a branch means to fetch it and merge it."
+msgid "pull"
+msgstr "puxar"
+
+#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)"
+msgid "push"
+msgstr "publicar"
+
+#. ""
+msgid "redo"
+msgstr "refazer"
+
+#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks."
+msgid "remote"
+msgstr "remoto"
+
+#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)"
+msgid "repository"
+msgstr "repositório"
+
+#. ""
+msgid "reset"
+msgstr "repor"
+
+#. ""
+msgid "revert"
+msgstr "reverter"
+
+#. "A particular state of files and directories which was stored in the object database."
+msgid "revision"
+msgstr "revisão"
+
+#. ""
+msgid "sign off"
+msgstr "assinar por baixo"
+
+#. ""
+msgid "staging area"
+msgstr "área de estágio"
+
+#. ""
+msgid "status"
+msgstr "estado"
+
+#. "A ref pointing to a tag or commit object"
+msgid "tag [noun]"
+msgstr "tag"
+
+#. ""
+msgid "tag [verb]"
+msgstr "criar tag"
+
+#. "A regular git branch that is used to follow changes from another repository."
+msgid "tracking branch"
+msgstr "ramo de monitorização"
+
+#. ""
+msgid "undo"
+msgstr "desfazer"
+
+#. ""
+msgid "update"
+msgstr "atualizar"
+
+#. ""
+msgid "verify"
+msgstr "verificar"
+
+#. "The tree of actual checked out files."
+msgid "working copy, working tree"
+msgstr "cópia de trabalho, árvore de trabalho"
+
+#. "a commit that succeeds the current one in git's graph of commits (not necessarily directly)"
+msgid "ancestor"
+msgstr "antecessor"
+
+#. "prematurely stop and abandon an operation"
+msgid "abort"
+msgstr "abortar"
+
+#. "a repository with only .git directory, without working directory"
+msgid "bare repository"
+msgstr "repositório nu"
+
+#. "a parent version of the current file"
+msgid "base"
+msgstr "base"
+
+#. "get the authors responsible for each line in a file"
+msgid "blame"
+msgstr "culpar"
+
+#. "to select and apply a single commit without merging"
+msgid "cherry-pick"
+msgstr "efetuar cherry-pick (escolher-a-dedo?, selecionar?)"
+
+#. "a commit that directly succeeds the current one in git's graph of commits"
+msgid "child"
+msgstr "filho"
+
+#. "clean the state of the git repository, often after manually stopped operation"
+msgid "cleanup"
+msgstr "limpar"
+
+#. "a message that gets attached with any commit"
+msgid "commit message"
+msgstr "mensagem de commit"
+
+#. "a commit that precedes the current one in git's graph of commits (not necessarily directly)"
+msgid "descendant"
+msgstr "descendente"
+
+#. "checkout of a revision rather than a some head"
+msgid "detached checkout"
+msgstr "extração destacada"
+
+#. "any merge strategy that works on a file by file basis"
+msgid "file level merging"
+msgstr "integração ao nível de ficheiros"
+
+#. "the last revision in a branch"
+msgid "head"
+msgstr "cabeça"
+
+#. "script that gets executed automatically on some event"
+msgid "hook"
+msgstr "gancho"
+
+#. "the first checkout during a clone operation"
+msgid "initial checkout"
+msgstr "extração inicial"
+
+#. "a branch that resides in the local git repository"
+msgid "local branch"
+msgstr "ramo local"
+
+#. "a Git object that is not part of any pack"
+msgid "loose object"
+msgstr "objeto solto"
+
+#. "a branch called by convention 'master' that exists in a newly created git repository"
+msgid "master branch"
+msgstr "ramo mestre"
+
+#. "a remote called by convention 'origin' that the current git repository has been cloned from"
+msgid "origin"
+msgstr "origem"
+
+#. "a file containing many git objects packed together"
+msgid "pack [noun]"
+msgstr "pacote"
+
+#. "a Git object part of some pack"
+msgid "packed object"
+msgstr "objeto compactado"
+
+#. "a commit that directly precedes the current one in git's graph of commits"
+msgid "parent"
+msgstr "pai"
+
+#. "the log file containing all states of the HEAD reference (in other words past pristine states of the working copy)"
+msgid "reflog"
+msgstr "reflog"
+
+#. "decide which changes from alternative versions of a file should persist in Git"
+msgid "resolve (a conflict)"
+msgstr "resolver (um conflito)"
+
+#. "abandon changes and go to pristine version"
+msgid "revert changes"
+msgstr "reverter alterações"
+
+#. "expression that signifies a revision in git"
+msgid "revision expression"
+msgstr "expressão de revisão"
+
+#. "add some content of files and directories to the staging area in preparation for a commit"
+msgid "stage/unstage"
+msgstr "preparar/retirar"
+
+#. "temporarily save changes in a stack without committing"
+msgid "stash"
+msgstr "empilhar"
+
+#. "file whose content is tracked/not tracked by git"
+msgid "tracked/untracked"
+msgstr "controlado/não controlado"
diff --git a/git-gui/po/pt_pt.po b/git-gui/po/pt_pt.po
new file mode 100644 (file)
index 0000000..0ef3c79
--- /dev/null
@@ -0,0 +1,2716 @@
+# Portuguese translations for git-gui package.
+# Copyright (C) 2016 Shawn Pearce, et al.
+# This file is distributed under the same license as the git package.
+# Vasco Almeida <vascomalmeida@sapo.pt>, 2016.
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-05-06 09:36+0000\n"
+"PO-Revision-Date: 2016-05-06 13:09+0000\n"
+"Last-Translator: Vasco Almeida <vascomalmeida@sapo.pt>\n"
+"Language-Team: Portuguese\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+
+#: git-gui.sh:861
+#, tcl-format
+msgid "Invalid font specified in %s:"
+msgstr "Tipo de letra inválido especificado em %s:"
+
+#: git-gui.sh:915
+msgid "Main Font"
+msgstr "Tipo de letra principal"
+
+#: git-gui.sh:916
+msgid "Diff/Console Font"
+msgstr "Tipo de letra Diferenças/Consola"
+
+#: git-gui.sh:931 git-gui.sh:945 git-gui.sh:958 git-gui.sh:1048
+#: git-gui.sh:1067 git-gui.sh:3125
+msgid "git-gui: fatal error"
+msgstr "git-gui: erro fatal"
+
+#: git-gui.sh:932
+msgid "Cannot find git in PATH."
+msgstr "Não é possível encontrar o git em PATH."
+
+#: git-gui.sh:959
+msgid "Cannot parse Git version string:"
+msgstr "Não é possível analisar a versão do Git:"
+
+#: git-gui.sh:984
+#, tcl-format
+msgid ""
+"Git version cannot be determined.\n"
+"\n"
+"%s claims it is version '%s'.\n"
+"\n"
+"%s requires at least Git 1.5.0 or later.\n"
+"\n"
+"Assume '%s' is version 1.5.0?\n"
+msgstr ""
+"A versão do Git não pôde ser determinada.\n"
+"\n"
+"%s alega que está na versão '%s'.\n"
+"\n"
+"%s requer pelo menos Git 1.5.0 ou mais recente.\n"
+"\n"
+"Assumir que '%s' está na versão 1.5.0?\n"
+
+#: git-gui.sh:1281
+msgid "Git directory not found:"
+msgstr "Diretório Git não encontrado:"
+
+#: git-gui.sh:1315
+msgid "Cannot move to top of working directory:"
+msgstr "Não é possível mover para o topo do diretório de trabalho:"
+
+#: git-gui.sh:1323
+msgid "Cannot use bare repository:"
+msgstr "Não é possível usar repositório nu:"
+
+#: git-gui.sh:1331
+msgid "No working directory"
+msgstr "Nenhum diretório de trabalho"
+
+#: git-gui.sh:1503 lib/checkout_op.tcl:306
+msgid "Refreshing file status..."
+msgstr "A atualizar estado do ficheiro..."
+
+#: git-gui.sh:1563
+msgid "Scanning for modified files ..."
+msgstr "A procurar por ficheiros modificados..."
+
+#: git-gui.sh:1639
+msgid "Calling prepare-commit-msg hook..."
+msgstr ""
+"A invocar gancho preparar-mensagem-de-commit (prepare-commit-msg hook)..."
+
+#: git-gui.sh:1656
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr ""
+"Commit recusado pelo gancho preparar-mensagem-de-commit (prepare-commit-msg "
+"hook)."
+
+#: git-gui.sh:1814 lib/browser.tcl:252
+msgid "Ready."
+msgstr "Pronto."
+
+#: git-gui.sh:1978
+#, tcl-format
+msgid ""
+"Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files."
+msgstr ""
+"Limite de visualização (gui.maxfilesdisplayed = %s) atingido, não são "
+"mostrados todos os %s ficheiros."
+
+#: git-gui.sh:2101
+msgid "Unmodified"
+msgstr "Não modificado"
+
+#: git-gui.sh:2103
+msgid "Modified, not staged"
+msgstr "Modificado, não preparado"
+
+#: git-gui.sh:2104 git-gui.sh:2116
+msgid "Staged for commit"
+msgstr "Preparado para commit"
+
+#: git-gui.sh:2105 git-gui.sh:2117
+msgid "Portions staged for commit"
+msgstr "Porções preparadas para commit"
+
+#: git-gui.sh:2106 git-gui.sh:2118
+msgid "Staged for commit, missing"
+msgstr "Preparado para commit, em falta"
+
+#: git-gui.sh:2108
+msgid "File type changed, not staged"
+msgstr "Tipo de ficheiro modificado, não preparado"
+
+#: git-gui.sh:2109 git-gui.sh:2110
+msgid "File type changed, old type staged for commit"
+msgstr "Tipo de ficheiro modificado, tipo antigo preparado para commit"
+
+#: git-gui.sh:2111
+msgid "File type changed, staged"
+msgstr "Tipo de ficheiro modificado, preparado"
+
+#: git-gui.sh:2112
+msgid "File type change staged, modification not staged"
+msgstr "Tipo de ficheiro modificado, modificação não preparada"
+
+#: git-gui.sh:2113
+msgid "File type change staged, file missing"
+msgstr "Tipo de ficheiro modificado, ficheiro em falta"
+
+#: git-gui.sh:2115
+msgid "Untracked, not staged"
+msgstr "Não controlado, não preparado"
+
+#: git-gui.sh:2120
+msgid "Missing"
+msgstr "Em falta"
+
+#: git-gui.sh:2121
+msgid "Staged for removal"
+msgstr "Preparado para remoção"
+
+#: git-gui.sh:2122
+msgid "Staged for removal, still present"
+msgstr "Preparado para remoção, ainda presente"
+
+#: git-gui.sh:2124 git-gui.sh:2125 git-gui.sh:2126 git-gui.sh:2127
+#: git-gui.sh:2128 git-gui.sh:2129
+msgid "Requires merge resolution"
+msgstr "Requer resolução de integração"
+
+#: git-gui.sh:2164
+msgid "Starting gitk... please wait..."
+msgstr "A iniciar gitk... aguarde..."
+
+#: git-gui.sh:2176
+msgid "Couldn't find gitk in PATH"
+msgstr "Não foi possível encontrar gitk em PATH"
+
+#: git-gui.sh:2235
+msgid "Couldn't find git gui in PATH"
+msgstr "Não foi possível encontrar git gui em PATH"
+
+#: git-gui.sh:2654 lib/choose_repository.tcl:41
+msgid "Repository"
+msgstr "Repositório"
+
+#: git-gui.sh:2655
+msgid "Edit"
+msgstr "Editar"
+
+#: git-gui.sh:2657 lib/choose_rev.tcl:567
+msgid "Branch"
+msgstr "Ramo"
+
+#: git-gui.sh:2660 lib/choose_rev.tcl:554
+msgid "Commit@@noun"
+msgstr "Commit"
+
+#: git-gui.sh:2663 lib/merge.tcl:123 lib/merge.tcl:152 lib/merge.tcl:170
+msgid "Merge"
+msgstr "Integrar"
+
+#: git-gui.sh:2664 lib/choose_rev.tcl:563
+msgid "Remote"
+msgstr "Remoto"
+
+#: git-gui.sh:2667
+msgid "Tools"
+msgstr "Ferramentas"
+
+#: git-gui.sh:2676
+msgid "Explore Working Copy"
+msgstr "Explorar cópia de trabalho"
+
+#: git-gui.sh:2682
+msgid "Git Bash"
+msgstr "Git Bash"
+
+#: git-gui.sh:2692
+msgid "Browse Current Branch's Files"
+msgstr "Navegar pelos ficheiro do ramo atual"
+
+#: git-gui.sh:2696
+msgid "Browse Branch Files..."
+msgstr "Navegar pelos ficheiros do ramo..."
+
+#: git-gui.sh:2701
+msgid "Visualize Current Branch's History"
+msgstr "Visualizar histórico do ramo atual"
+
+#: git-gui.sh:2705
+msgid "Visualize All Branch History"
+msgstr "Visualizar histórico de todos os ramos"
+
+#: git-gui.sh:2712
+#, tcl-format
+msgid "Browse %s's Files"
+msgstr "Navegar pelos ficheiro de %s"
+
+#: git-gui.sh:2714
+#, tcl-format
+msgid "Visualize %s's History"
+msgstr "Visualizar histórico de %s"
+
+#: git-gui.sh:2719 lib/database.tcl:40 lib/database.tcl:66
+msgid "Database Statistics"
+msgstr "Estatísticas da base de dados"
+
+#: git-gui.sh:2722 lib/database.tcl:33
+msgid "Compress Database"
+msgstr "Comprimir base de dados"
+
+#: git-gui.sh:2725
+msgid "Verify Database"
+msgstr "Verificar base de dados"
+
+#: git-gui.sh:2732 git-gui.sh:2736 git-gui.sh:2740 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
+msgid "Create Desktop Icon"
+msgstr "Criar ícone no ambiente de trabalho"
+
+#: git-gui.sh:2748 lib/choose_repository.tcl:193 lib/choose_repository.tcl:201
+msgid "Quit"
+msgstr "Sair"
+
+#: git-gui.sh:2756
+msgid "Undo"
+msgstr "Desfazer"
+
+#: git-gui.sh:2759
+msgid "Redo"
+msgstr "Refazer"
+
+#: git-gui.sh:2763 git-gui.sh:3368
+msgid "Cut"
+msgstr "Cortar"
+
+#: git-gui.sh:2766 git-gui.sh:3371 git-gui.sh:3445 git-gui.sh:3530
+#: lib/console.tcl:69
+msgid "Copy"
+msgstr "Copiar"
+
+#: git-gui.sh:2769 git-gui.sh:3374
+msgid "Paste"
+msgstr "Colar"
+
+#: git-gui.sh:2772 git-gui.sh:3377 lib/remote_branch_delete.tcl:39
+#: lib/branch_delete.tcl:28
+msgid "Delete"
+msgstr "Eliminar"
+
+#: git-gui.sh:2776 git-gui.sh:3381 git-gui.sh:3534 lib/console.tcl:71
+msgid "Select All"
+msgstr "Selecionar tudo"
+
+#: git-gui.sh:2785
+msgid "Create..."
+msgstr "Criar..."
+
+#: git-gui.sh:2791
+msgid "Checkout..."
+msgstr "Extrair..."
+
+#: git-gui.sh:2797
+msgid "Rename..."
+msgstr "Mudar nome..."
+
+#: git-gui.sh:2802
+msgid "Delete..."
+msgstr "Eliminar..."
+
+#: git-gui.sh:2807
+msgid "Reset..."
+msgstr "Repor..."
+
+#: git-gui.sh:2817
+msgid "Done"
+msgstr "Concluído"
+
+#: git-gui.sh:2819
+msgid "Commit@@verb"
+msgstr "Submeter"
+
+#: git-gui.sh:2828 git-gui.sh:3309
+msgid "New Commit"
+msgstr "Novo commit"
+
+#: git-gui.sh:2836 git-gui.sh:3316
+msgid "Amend Last Commit"
+msgstr "Emendar último commit"
+
+#: git-gui.sh:2846 git-gui.sh:3270 lib/remote_branch_delete.tcl:101
+msgid "Rescan"
+msgstr "Reanalisar"
+
+#: git-gui.sh:2852
+msgid "Stage To Commit"
+msgstr "Preparar para commit"
+
+#: git-gui.sh:2858
+msgid "Stage Changed Files To Commit"
+msgstr "Preparar ficheiros modificados para commit"
+
+#: git-gui.sh:2864
+msgid "Unstage From Commit"
+msgstr "Retirar do commit"
+
+#: git-gui.sh:2870 lib/index.tcl:442
+msgid "Revert Changes"
+msgstr "Reverter alterações"
+
+#: git-gui.sh:2878 git-gui.sh:3581 git-gui.sh:3612
+msgid "Show Less Context"
+msgstr "Mostrar menos contexto"
+
+#: git-gui.sh:2882 git-gui.sh:3585 git-gui.sh:3616
+msgid "Show More Context"
+msgstr "Mostrar mais contexto"
+
+#: git-gui.sh:2889 git-gui.sh:3283 git-gui.sh:3392
+msgid "Sign Off"
+msgstr "Assinar por baixo"
+
+#: git-gui.sh:2905
+msgid "Local Merge..."
+msgstr "Integração local..."
+
+#: git-gui.sh:2910
+msgid "Abort Merge..."
+msgstr "Abortar integração..."
+
+#: git-gui.sh:2922 git-gui.sh:2950
+msgid "Add..."
+msgstr "Adicionar..."
+
+#: git-gui.sh:2926
+msgid "Push..."
+msgstr "Publicar..."
+
+#: git-gui.sh:2930
+msgid "Delete Branch..."
+msgstr "Eliminar ramo..."
+
+#: git-gui.sh:2940 git-gui.sh:3563
+msgid "Options..."
+msgstr "Opções..."
+
+#: git-gui.sh:2951
+msgid "Remove..."
+msgstr "Remover..."
+
+#: git-gui.sh:2960 lib/choose_repository.tcl:55
+msgid "Help"
+msgstr "Ajuda"
+
+#: git-gui.sh:2964 git-gui.sh:2968 lib/choose_repository.tcl:49
+#: lib/choose_repository.tcl:58 lib/about.tcl:14
+#, tcl-format
+msgid "About %s"
+msgstr "Sobre %s"
+
+#: git-gui.sh:2992
+msgid "Online Documentation"
+msgstr "Documentação online"
+
+#: git-gui.sh:2995 lib/choose_repository.tcl:52 lib/choose_repository.tcl:61
+msgid "Show SSH Key"
+msgstr "Mostrar chave SSH"
+
+#: git-gui.sh:3014 git-gui.sh:3146
+msgid "Usage"
+msgstr "Utilização"
+
+#: git-gui.sh:3095 lib/blame.tcl:573
+msgid "Error"
+msgstr "Erro"
+
+#: git-gui.sh:3126
+#, tcl-format
+msgid "fatal: cannot stat path %s: No such file or directory"
+msgstr ""
+"fatal: não é possível obter estado do caminho %s: Ficheiro ou diretório "
+"inexistente"
+
+#: git-gui.sh:3159
+msgid "Current Branch:"
+msgstr "Ramo atual:"
+
+#: git-gui.sh:3185
+msgid "Staged Changes (Will Commit)"
+msgstr "Alterações preparadas (para commit)"
+
+#: git-gui.sh:3205
+msgid "Unstaged Changes"
+msgstr "Alterações não preparadas"
+
+#: git-gui.sh:3276
+msgid "Stage Changed"
+msgstr "Preparar modificados"
+
+#: git-gui.sh:3295 lib/transport.tcl:137 lib/transport.tcl:229
+msgid "Push"
+msgstr "Publicar"
+
+#: git-gui.sh:3330
+msgid "Initial Commit Message:"
+msgstr "Mensagem de commit inicial:"
+
+#: git-gui.sh:3331
+msgid "Amended Commit Message:"
+msgstr "Mensagem de commit emendada:"
+
+#: git-gui.sh:3332
+msgid "Amended Initial Commit Message:"
+msgstr "Mensagem de commit inicial emendada:"
+
+#: git-gui.sh:3333
+msgid "Amended Merge Commit Message:"
+msgstr "Mensagem de commit de integração emendada:"
+
+#: git-gui.sh:3334
+msgid "Merge Commit Message:"
+msgstr "Mensagem de commit de integração:"
+
+#: git-gui.sh:3335
+msgid "Commit Message:"
+msgstr "Mensagem de commit:"
+
+#: git-gui.sh:3384 git-gui.sh:3538 lib/console.tcl:73
+msgid "Copy All"
+msgstr "Copiar tudo"
+
+#: git-gui.sh:3408 lib/blame.tcl:105
+msgid "File:"
+msgstr "Ficheiro:"
+
+#: git-gui.sh:3526
+msgid "Refresh"
+msgstr "Atualizar"
+
+#: git-gui.sh:3547
+msgid "Decrease Font Size"
+msgstr "Diminuir tamanho de letra"
+
+#: git-gui.sh:3551
+msgid "Increase Font Size"
+msgstr "Aumentar tamanho de letra"
+
+#: git-gui.sh:3559 lib/blame.tcl:294
+msgid "Encoding"
+msgstr "Codificação"
+
+#: git-gui.sh:3570
+msgid "Apply/Reverse Hunk"
+msgstr "Aplicar/Reverter excerto"
+
+#: git-gui.sh:3575
+msgid "Apply/Reverse Line"
+msgstr "Aplicar/Reverter linha"
+
+#: git-gui.sh:3594
+msgid "Run Merge Tool"
+msgstr "Executar ferramenta de integração"
+
+#: git-gui.sh:3599
+msgid "Use Remote Version"
+msgstr "Usar a versão remota"
+
+#: git-gui.sh:3603
+msgid "Use Local Version"
+msgstr "Usar a versão local"
+
+#: git-gui.sh:3607
+msgid "Revert To Base"
+msgstr "Reverter para a base"
+
+#: git-gui.sh:3625
+msgid "Visualize These Changes In The Submodule"
+msgstr "Visualizar estas alterações no submódulo"
+
+#: git-gui.sh:3629
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Visualizar histórico do ramo atual no submódulo"
+
+#: git-gui.sh:3633
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Visualizar histórico de todos os ramos no submódulo"
+
+#: git-gui.sh:3638
+msgid "Start git gui In The Submodule"
+msgstr "Iniciar git gui no submódulo"
+
+#: git-gui.sh:3673
+msgid "Unstage Hunk From Commit"
+msgstr "Retirar excerto do commit"
+
+#: git-gui.sh:3675
+msgid "Unstage Lines From Commit"
+msgstr "Retirar linhas do commit"
+
+#: git-gui.sh:3677
+msgid "Unstage Line From Commit"
+msgstr "Retirar linha do commit"
+
+#: git-gui.sh:3680
+msgid "Stage Hunk For Commit"
+msgstr "Preparar excerto para commit"
+
+#: git-gui.sh:3682
+msgid "Stage Lines For Commit"
+msgstr "Preparar linhas para commit"
+
+#: git-gui.sh:3684
+msgid "Stage Line For Commit"
+msgstr "Preparar linha para commit"
+
+#: git-gui.sh:3709
+msgid "Initializing..."
+msgstr "A inicializar..."
+
+#: git-gui.sh:3852
+#, tcl-format
+msgid ""
+"Possible environment issues exist.\n"
+"\n"
+"The following environment variables are probably\n"
+"going to be ignored by any Git subprocess run\n"
+"by %s:\n"
+"\n"
+msgstr ""
+"Existem possíveis erros de ambiente.\n"
+"\n"
+"As seguintes variáveis de ambiente serão provavelmente\n"
+"ignoradas pelos subprocessos do Git executados\n"
+"por %s:\n"
+"\n"
+
+#: git-gui.sh:3881
+msgid ""
+"\n"
+"This is due to a known issue with the\n"
+"Tcl binary distributed by Cygwin."
+msgstr ""
+"\n"
+"Devido a um problema conhecido com o\n"
+"binário Tcl distribuído pelo Cygwin."
+
+#: git-gui.sh:3886
+#, tcl-format
+msgid ""
+"\n"
+"\n"
+"A good replacement for %s\n"
+"is placing values for the user.name and\n"
+"user.email settings into your personal\n"
+"~/.gitconfig file.\n"
+msgstr ""
+"\n"
+"\n"
+"Um bom substituto para %s\n"
+"é colocar valores das definições user.name e\n"
+"user.email no ficheiro pessoal ~/.gitconfig.\n"
+
+#: lib/line.tcl:17
+msgid "Goto Line:"
+msgstr "Ir para a linha:"
+
+#: lib/line.tcl:23
+msgid "Go"
+msgstr "Ir"
+
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "A processar... aguarde..."
+
+#: lib/console.tcl:81 lib/checkout_op.tcl:146 lib/sshkey.tcl:55
+#: lib/database.tcl:30
+msgid "Close"
+msgstr "Fechar"
+
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Sucesso"
+
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Erro: falha ao executar comando"
+
+#: lib/checkout_op.tcl:85
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "A obter %s de %s"
+
+#: lib/checkout_op.tcl:133
+#, tcl-format
+msgid "fatal: Cannot resolve %s"
+msgstr "fatal: Não é possível resolver %s"
+
+#: lib/checkout_op.tcl:175
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "O ramo '%s' não existe."
+
+#: lib/checkout_op.tcl:194
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Falha ao configurar git-pull simplificado de '%s'."
+
+#: lib/checkout_op.tcl:202 lib/branch_rename.tcl:102
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "O ramo '%s' já existe."
+
+#: lib/checkout_op.tcl:229
+#, tcl-format
+msgid ""
+"Branch '%s' already exists.\n"
+"\n"
+"It cannot fast-forward to %s.\n"
+"A merge is required."
+msgstr ""
+"O ramo '%s' já existe.\n"
+"\n"
+"Não pode ser avançado rapidamente para %s.\n"
+"Integração necessária."
+
+#: lib/checkout_op.tcl:243
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "A estratégia de integração '%s' não é suportada."
+
+#: lib/checkout_op.tcl:262
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Falha ao atualizar '%s'."
+
+#: lib/checkout_op.tcl:274
+msgid "Staging area (index) is already locked."
+msgstr "A área de estágio (índice) já está bloqueada."
+
+#: lib/checkout_op.tcl:289
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before the current branch can be changed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado analisado não corresponde ao estado do repositório.\n"
+"\n"
+"Outro programa Git modificou este repositório deste a última análise. Deve-"
+"se reanalisar antes do ramo atual poder ser alterado.\n"
+"\n"
+"Irá-se reanalisar automaticamente agora.\n"
+
+#: lib/checkout_op.tcl:345
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "A atualizar o diretório de trabalho para '%s'..."
+
+#: lib/checkout_op.tcl:346
+msgid "files checked out"
+msgstr "ficheiros extraídos"
+
+#: lib/checkout_op.tcl:376
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
+msgstr ""
+"Extração de '%s' abortada (é necessário integrar ao nível de ficheiros)."
+
+#: lib/checkout_op.tcl:377
+msgid "File level merge required."
+msgstr "Integração ao nível de ficheiros necessária."
+
+#: lib/checkout_op.tcl:381
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Permanecer no ramo '%s'."
+
+#: lib/checkout_op.tcl:452
+msgid ""
+"You are no longer on a local branch.\n"
+"\n"
+"If you wanted to be on a branch, create one now starting from 'This Detached "
+"Checkout'."
+msgstr ""
+"Já não se encontra num ramo local.\n"
+"\n"
+"Se queria estar sobre um ramo, crie um a partir de 'Esta extração destacada'."
+
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "'%s' extraído."
+
+#: lib/checkout_op.tcl:535
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr "Ao repor '%s' para '%s' perderá os seguintes commits:"
+
+#: lib/checkout_op.tcl:557
+msgid "Recovering lost commits may not be easy."
+msgstr "Recuperar commits perdidos pode não ser fácil."
+
+#: lib/checkout_op.tcl:562
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Repor '%s'?"
+
+#: lib/checkout_op.tcl:567 lib/tools_dlg.tcl:336 lib/merge.tcl:166
+msgid "Visualize"
+msgstr "Visualizar"
+
+#: lib/checkout_op.tcl:571 lib/branch_create.tcl:85
+msgid "Reset"
+msgstr "Repor"
+
+#: lib/checkout_op.tcl:579 lib/transport.tcl:141 lib/remote_add.tcl:34
+#: lib/browser.tcl:292 lib/branch_checkout.tcl:30 lib/choose_font.tcl:45
+#: lib/option.tcl:127 lib/tools_dlg.tcl:41 lib/tools_dlg.tcl:202
+#: lib/tools_dlg.tcl:345 lib/branch_rename.tcl:32
+#: lib/remote_branch_delete.tcl:43 lib/branch_create.tcl:37
+#: lib/branch_delete.tcl:34 lib/merge.tcl:174
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: lib/checkout_op.tcl:635
+#, tcl-format
+msgid ""
+"Failed to set current branch.\n"
+"\n"
+"This working directory is only partially switched.  We successfully updated "
+"your files, but failed to update an internal Git file.\n"
+"\n"
+"This should not have occurred.  %s will now close and give up."
+msgstr ""
+"Falha ao definir ramo atual.\n"
+"\n"
+"Apenas se mudou o diretório de trabalho parcialmente. Os ficheiros foram "
+"atualizados com sucesso, mas não foi possível atualizar o ficheiro Git "
+"interno.\n"
+"\n"
+"Não devia ter ocorrido. %s irá terminar e desistir."
+
+#: lib/transport.tcl:6 lib/remote_add.tcl:132
+#, tcl-format
+msgid "fetch %s"
+msgstr "obter %s"
+
+#: lib/transport.tcl:7
+#, tcl-format
+msgid "Fetching new changes from %s"
+msgstr "Obter novas alterações de %s"
+
+#: lib/transport.tcl:18
+#, tcl-format
+msgid "remote prune %s"
+msgstr "poda remota de %s"
+
+#: lib/transport.tcl:19
+#, tcl-format
+msgid "Pruning tracking branches deleted from %s"
+msgstr "A podar ramos de monitorização eliminados de %s"
+
+#: lib/transport.tcl:25
+msgid "fetch all remotes"
+msgstr "obter de todos os remotos"
+
+#: lib/transport.tcl:26
+msgid "Fetching new changes from all remotes"
+msgstr "A obter novas alterações de todos os remotos"
+
+#: lib/transport.tcl:40
+msgid "remote prune all remotes"
+msgstr "poda remota de todos os remotos"
+
+#: lib/transport.tcl:41
+msgid "Pruning tracking branches deleted from all remotes"
+msgstr "A podar ramos de monitorização eliminados de todos os remotos"
+
+#: lib/transport.tcl:54 lib/transport.tcl:92 lib/transport.tcl:110
+#: lib/remote_add.tcl:162
+#, tcl-format
+msgid "push %s"
+msgstr "publicar %s"
+
+#: lib/transport.tcl:55
+#, tcl-format
+msgid "Pushing changes to %s"
+msgstr "A publicar alterações em %s"
+
+#: lib/transport.tcl:93
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "A espelhar em %s"
+
+#: lib/transport.tcl:111
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "A publicar %s %s em %s"
+
+#: lib/transport.tcl:132
+msgid "Push Branches"
+msgstr "Publicar ramos"
+
+#: lib/transport.tcl:147
+msgid "Source Branches"
+msgstr "Ramos de origem"
+
+#: lib/transport.tcl:162
+msgid "Destination Repository"
+msgstr "Repositório de destino"
+
+#: lib/transport.tcl:165 lib/remote_branch_delete.tcl:51
+msgid "Remote:"
+msgstr "Remoto:"
+
+#: lib/transport.tcl:187 lib/remote_branch_delete.tcl:72
+msgid "Arbitrary Location:"
+msgstr "Localização arbitrária:"
+
+#: lib/transport.tcl:205
+msgid "Transfer Options"
+msgstr "Opções de transferência"
+
+#: lib/transport.tcl:207
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr "Forçar substituição de ramos existente (pode descartar alterações)"
+
+#: lib/transport.tcl:211
+msgid "Use thin pack (for slow network connections)"
+msgstr "Usar pacote fino (para conexões de rede lentas)"
+
+#: lib/transport.tcl:215
+msgid "Include tags"
+msgstr "Incluir tags"
+
+#: lib/remote_add.tcl:20
+msgid "Add Remote"
+msgstr "Adicionar remoto"
+
+#: lib/remote_add.tcl:25
+msgid "Add New Remote"
+msgstr "Adicionar novo remoto"
+
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
+msgid "Add"
+msgstr "Adicionar"
+
+#: lib/remote_add.tcl:39
+msgid "Remote Details"
+msgstr "Detalhes do remoto"
+
+#: lib/remote_add.tcl:41 lib/tools_dlg.tcl:51 lib/branch_create.tcl:44
+msgid "Name:"
+msgstr "Nome:"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Localização:"
+
+#: lib/remote_add.tcl:60
+msgid "Further Action"
+msgstr "Ação adicional"
+
+#: lib/remote_add.tcl:63
+msgid "Fetch Immediately"
+msgstr "Obter imediatamente"
+
+#: lib/remote_add.tcl:69
+msgid "Initialize Remote Repository and Push"
+msgstr "Inicializar repositório remoto e publicar"
+
+#: lib/remote_add.tcl:75
+msgid "Do Nothing Else Now"
+msgstr "Não fazer mais nada agora"
+
+#: lib/remote_add.tcl:100
+msgid "Please supply a remote name."
+msgstr "Forneça um nome para o remoto."
+
+#: lib/remote_add.tcl:113
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "'%s' não pode ser aceite como nome de remoto."
+
+#: lib/remote_add.tcl:124
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Falha ao adicionar remoto '%s' localizado em '%s'."
+
+#: lib/remote_add.tcl:133
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "A obter de %s"
+
+#: lib/remote_add.tcl:156
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Não se sabe como inicializar o repositório localizado em '%s'."
+
+#: lib/remote_add.tcl:163
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "A configurar %s (em %s)"
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "A iniciar..."
+
+#: lib/browser.tcl:27
+msgid "File Browser"
+msgstr "Navegador de ficheiros"
+
+#: lib/browser.tcl:132 lib/browser.tcl:149
+#, tcl-format
+msgid "Loading %s..."
+msgstr "A carregar %s..."
+
+#: lib/browser.tcl:193
+msgid "[Up To Parent]"
+msgstr "[Subir]"
+
+#: lib/browser.tcl:275 lib/browser.tcl:282
+msgid "Browse Branch Files"
+msgstr "Navegar pelos ficheiros do ramo"
+
+#: lib/browser.tcl:288 lib/choose_repository.tcl:422
+#: lib/choose_repository.tcl:509 lib/choose_repository.tcl:518
+#: lib/choose_repository.tcl:1074
+msgid "Browse"
+msgstr "Navegar"
+
+#: lib/browser.tcl:297 lib/branch_checkout.tcl:35 lib/tools_dlg.tcl:321
+msgid "Revision"
+msgstr "Revisão"
+
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "Deve selecionar um ficheiro para executar %s."
+
+#: lib/tools.tcl:91
+#, tcl-format
+msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
+msgstr "Tem a certeza que pretende executar %1$s sobre o ficheiro \"%2$s\"?"
+
+#: lib/tools.tcl:95
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Tem a certeza que pretende executar %s?"
+
+#: lib/tools.tcl:116
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Ferramenta: %s"
+
+#: lib/tools.tcl:117
+#, tcl-format
+msgid "Running: %s"
+msgstr "A executar: %s"
+
+#: lib/tools.tcl:155
+#, tcl-format
+msgid "Tool completed successfully: %s"
+msgstr "A ferramenta concluí com sucesso: %s"
+
+#: lib/tools.tcl:157
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "A ferramenta falhou: %s"
+
+#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
+msgid "Checkout Branch"
+msgstr "Extrair ramo"
+
+#: lib/branch_checkout.tcl:26
+msgid "Checkout"
+msgstr "Extrair"
+
+#: lib/branch_checkout.tcl:39 lib/option.tcl:310 lib/branch_create.tcl:69
+msgid "Options"
+msgstr "Opções"
+
+#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Obter ramo de monitorização"
+
+#: lib/branch_checkout.tcl:47
+msgid "Detach From Local Branch"
+msgstr "Destacar do ramo local"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Corretor ortográfico não suportado"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Correção ortográfica indisponível"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Configuração inválida do corretor ortográfico"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "A reverter dicionário para %s."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "O corretor ortográfico falhou silenciosamente ao iniciar"
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Corretor ortográfico não reconhecido"
+
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Sem sugestões"
+
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "EOF (fim de ficheiro) inesperado do corretor ortográfico"
+
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "Corretor ortográfico falhou"
+
+#: lib/status_bar.tcl:87
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s ... %*i de %*i %s (%3i%%)"
+
+#: lib/diff.tcl:77
+#, tcl-format
+msgid ""
+"No differences detected.\n"
+"\n"
+"%s has no changes.\n"
+"\n"
+"The modification date of this file was updated by another application, but "
+"the content within the file was not changed.\n"
+"\n"
+"A rescan will be automatically started to find other files which may have "
+"the same state."
+msgstr ""
+"Nenhum diferença detetada.\n"
+"\n"
+"%s não tem alterações.\n"
+"\n"
+"A data de modificação deste ficheiro foi atualizada por outra aplicação, mas "
+"o conteúdo no interior do ficheiro não foi alterado.\n"
+"\n"
+"Irá-se reanalisar automaticamente para encontrar outros ficheiros que "
+"estejam no mesmo estado."
+
+#: lib/diff.tcl:117
+#, tcl-format
+msgid "Loading diff of %s..."
+msgstr "A carregar diferenças de %s..."
+
+#: lib/diff.tcl:140
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"LOCAL: eliminado\n"
+"REMOTO:\n"
+
+#: lib/diff.tcl:145
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"REMOTO: eliminado\n"
+"LOCAL:\n"
+
+#: lib/diff.tcl:152
+msgid "LOCAL:\n"
+msgstr "LOCAL:\n"
+
+#: lib/diff.tcl:155
+msgid "REMOTE:\n"
+msgstr "REMOTO:\n"
+
+#: lib/diff.tcl:217 lib/diff.tcl:355
+#, tcl-format
+msgid "Unable to display %s"
+msgstr "Não é possível mostrar %s"
+
+#: lib/diff.tcl:218
+msgid "Error loading file:"
+msgstr "Erro ao carregar ficheiro:"
+
+#: lib/diff.tcl:225
+msgid "Git Repository (subproject)"
+msgstr "Repositório Git (subprojeto)"
+
+#: lib/diff.tcl:237
+msgid "* Binary file (not showing content)."
+msgstr "* Ficheiro binário (conteúdo não exibido)."
+
+#: lib/diff.tcl:242
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"* O ficheiro não controlado tem %d bytes.\n"
+"* Exibido apenas os primeiros %d bytes.\n"
+
+#: lib/diff.tcl:248
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"* Ficheiro não controlado recortado aqui por %s.\n"
+"* Para ver o ficheiro inteiro, use um editor externo.\n"
+
+#: lib/diff.tcl:356 lib/blame.tcl:1128
+msgid "Error loading diff:"
+msgstr "Erro ao carregar diferenças:"
+
+#: lib/diff.tcl:578
+msgid "Failed to unstage selected hunk."
+msgstr "Falha ao retirar excerto selecionado do índice."
+
+#: lib/diff.tcl:585
+msgid "Failed to stage selected hunk."
+msgstr "Falha ao preparar excerto selecionado."
+
+#: lib/diff.tcl:664
+msgid "Failed to unstage selected line."
+msgstr "Falha ao retirar linha selecionada do índice."
+
+#: lib/diff.tcl:672
+msgid "Failed to stage selected line."
+msgstr "Falha ao preparar linha selecionada."
+
+#: lib/remote.tcl:200
+msgid "Push to"
+msgstr "Publicar em"
+
+#: lib/remote.tcl:218
+msgid "Remove Remote"
+msgstr "Remover remoto"
+
+#: lib/remote.tcl:223
+msgid "Prune from"
+msgstr "Podar de"
+
+#: lib/remote.tcl:228
+msgid "Fetch from"
+msgstr "Obter de"
+
+#: lib/choose_font.tcl:41
+msgid "Select"
+msgstr "Selecionar"
+
+#: lib/choose_font.tcl:55
+msgid "Font Family"
+msgstr "Família de tipo de letra"
+
+#: lib/choose_font.tcl:76
+msgid "Font Size"
+msgstr "Tamanho de letra"
+
+#: lib/choose_font.tcl:93
+msgid "Font Example"
+msgstr "Exemplo do tipo de letra"
+
+#: lib/choose_font.tcl:105
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Este texto é um exemplo.\n"
+"Se gostar deste texto, pode defini-lo como tipo de letra."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Codificação global '%s' inválida"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Codificação do repositório '%s' inválida"
+
+#: lib/option.tcl:119
+msgid "Restore Defaults"
+msgstr "Restaurar predefinições"
+
+#: lib/option.tcl:123
+msgid "Save"
+msgstr "Guardar"
+
+#: lib/option.tcl:133
+#, tcl-format
+msgid "%s Repository"
+msgstr "Repositório %s"
+
+#: lib/option.tcl:134
+msgid "Global (All Repositories)"
+msgstr "Global (todos os repositórios)"
+
+#: lib/option.tcl:140
+msgid "User Name"
+msgstr "Nome de utilizador"
+
+#: lib/option.tcl:141
+msgid "Email Address"
+msgstr "Endereço de e-mail"
+
+#: lib/option.tcl:143
+msgid "Summarize Merge Commits"
+msgstr "Resumir commits de integração"
+
+#: lib/option.tcl:144
+msgid "Merge Verbosity"
+msgstr "Verbosidade de integração"
+
+#: lib/option.tcl:145
+msgid "Show Diffstat After Merge"
+msgstr "Mostrar estatísticas de diferenças depois de integrar"
+
+#: lib/option.tcl:146
+msgid "Use Merge Tool"
+msgstr "Usar ferramenta de integração"
+
+#: lib/option.tcl:148
+msgid "Trust File Modification Timestamps"
+msgstr "Confiar na data de modificação dos ficheiros"
+
+#: lib/option.tcl:149
+msgid "Prune Tracking Branches During Fetch"
+msgstr "Podar ramos de monitorização ao obter"
+
+#: lib/option.tcl:150
+msgid "Match Tracking Branches"
+msgstr "Corresponder ramos de monitorização"
+
+#: lib/option.tcl:151
+msgid "Use Textconv For Diffs and Blames"
+msgstr "Usar textconv para mostrar diferenças e culpar"
+
+#: lib/option.tcl:152
+msgid "Blame Copy Only On Changed Files"
+msgstr "Detetar cópia apenas em ficheiros modificados"
+
+#: lib/option.tcl:153
+msgid "Maximum Length of Recent Repositories List"
+msgstr "Comprimento máximo da lista de repositórios recentes"
+
+#: lib/option.tcl:154
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Número mínimo de letras para detetar cópia"
+
+#: lib/option.tcl:155
+msgid "Blame History Context Radius (days)"
+msgstr "Raio de contexto histórico para culpar (dias)"
+
+#: lib/option.tcl:156
+msgid "Number of Diff Context Lines"
+msgstr "Número de linhas de contexto ao mostrar diferenças"
+
+#: lib/option.tcl:157
+msgid "Additional Diff Parameters"
+msgstr "Parâmetros de diff adicionais"
+
+#: lib/option.tcl:158
+msgid "Commit Message Text Width"
+msgstr "Largura do texto da mensagem de commit"
+
+#: lib/option.tcl:159
+msgid "New Branch Name Template"
+msgstr "Modelo para nome de novo ramo"
+
+#: lib/option.tcl:160
+msgid "Default File Contents Encoding"
+msgstr "Codificação predefinida dos conteúdos de ficheiros"
+
+#: lib/option.tcl:161
+msgid "Warn before committing to a detached head"
+msgstr "Avisar antes de submeter numa cabeça destacada"
+
+#: lib/option.tcl:162
+msgid "Staging of untracked files"
+msgstr "Preparar ficheiros não controlados"
+
+#: lib/option.tcl:163
+msgid "Show untracked files"
+msgstr "Mostrar ficheiros não controlados"
+
+#: lib/option.tcl:164
+msgid "Tab spacing"
+msgstr "Espaçamento da tabulação"
+
+#: lib/option.tcl:210
+msgid "Change"
+msgstr "Alterar"
+
+#: lib/option.tcl:254
+msgid "Spelling Dictionary:"
+msgstr "Dicionário ortográfico:"
+
+#: lib/option.tcl:284
+msgid "Change Font"
+msgstr "Alterar tipo de letra"
+
+#: lib/option.tcl:288
+#, tcl-format
+msgid "Choose %s"
+msgstr "Escolher %s"
+
+#: lib/option.tcl:294
+msgid "pt."
+msgstr "pt."
+
+#: lib/option.tcl:308
+msgid "Preferences"
+msgstr "Preferências"
+
+#: lib/option.tcl:345
+msgid "Failed to completely save options:"
+msgstr "Falha ao guardar todas as opções:"
+
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Forçar resolução para a versão base?"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Forçar resolução para este ramo?"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Forçar resolução para o outro ramo?"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Note que as diferenças mostram apenas alterações em conflito.\n"
+"\n"
+"%s será substituído.\n"
+"\n"
+"Esta operação só pode ser anulada reiniciando a integração."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"O ficheiro %s parece ter conflitos não resolvidos, prepará-lo mesmo assim?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "A adicionar resolução de %s"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Não é possível resolver conflitos de exclusão ou ligação usando uma "
+"ferramenta"
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "O ficheiro em conflito não existe"
+
+#: lib/mergetool.tcl:246
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Não é uma ferramenta GUI de integração: '%s'"
+
+#: lib/mergetool.tcl:275
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Ferramenta de integração '%s' não suportada"
+
+#: lib/mergetool.tcl:310
+msgid "Merge tool is already running, terminate it?"
+msgstr "A ferramenta de integração já está a executar, terminá-la?"
+
+#: lib/mergetool.tcl:330
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Erro ao obter versões:\n"
+"%s"
+
+#: lib/mergetool.tcl:350
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Não foi possível iniciar a ferramenta de integração:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:354
+msgid "Running merge tool..."
+msgstr "A executar a ferramenta de integração..."
+
+#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
+msgid "Merge tool failed."
+msgstr "A ferramenta de integração falhou."
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Adicionar ferramenta"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Adicionar novo comando de ferramenta"
+
+#: lib/tools_dlg.tcl:34
+msgid "Add globally"
+msgstr "Adicionar globalmente"
+
+#: lib/tools_dlg.tcl:46
+msgid "Tool Details"
+msgstr "Detalhes da ferramenta"
+
+#: lib/tools_dlg.tcl:49
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "Use separadores '/' para criar uma árvore de submenus:"
+
+#: lib/tools_dlg.tcl:60
+msgid "Command:"
+msgstr "Comando:"
+
+#: lib/tools_dlg.tcl:71
+msgid "Show a dialog before running"
+msgstr "Mostrar um diálogo antes de executar"
+
+#: lib/tools_dlg.tcl:77
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Pedir ao utilizador para selecionar uma revisão (define $REVISION)"
+
+#: lib/tools_dlg.tcl:82
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr "Pedir ao utilizador argumentos adicionais (define $ARGS)"
+
+#: lib/tools_dlg.tcl:89
+msgid "Don't show the command output window"
+msgstr "Não mostrar a janela com a saída do comando"
+
+#: lib/tools_dlg.tcl:94
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr "Executar só se for selecionada um diferença ($FILENAME não vazio)"
+
+#: lib/tools_dlg.tcl:118
+msgid "Please supply a name for the tool."
+msgstr "Forneça um nome para a ferramenta."
+
+#: lib/tools_dlg.tcl:126
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "A ferramenta '%s' já existe."
+
+#: lib/tools_dlg.tcl:148
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Não foi possível adicionar ferramenta:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:187
+msgid "Remove Tool"
+msgstr "Remover ferramenta"
+
+#: lib/tools_dlg.tcl:193
+msgid "Remove Tool Commands"
+msgstr "Remover comandos de ferramenta"
+
+#: lib/tools_dlg.tcl:198
+msgid "Remove"
+msgstr "Remover"
+
+#: lib/tools_dlg.tcl:231
+msgid "(Blue denotes repository-local tools)"
+msgstr "(Azul denota ferramentas locais do repositório)"
+
+#: lib/tools_dlg.tcl:292
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Executar comando: %s"
+
+#: lib/tools_dlg.tcl:306
+msgid "Arguments"
+msgstr "Argumentos"
+
+#: lib/tools_dlg.tcl:341
+msgid "OK"
+msgstr "OK"
+
+#: lib/search.tcl:48
+msgid "Find:"
+msgstr "Procurar:"
+
+#: lib/search.tcl:50
+msgid "Next"
+msgstr "Seguinte"
+
+#: lib/search.tcl:51
+msgid "Prev"
+msgstr "Anterior"
+
+#: lib/search.tcl:52
+msgid "RegExp"
+msgstr "ExpReg"
+
+#: lib/search.tcl:54
+msgid "Case"
+msgstr "Maiúsculas"
+
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
+msgid "Cannot write shortcut:"
+msgstr "Não é possível escrever atalho:"
+
+#: lib/shortcut.tcl:137
+msgid "Cannot write icon:"
+msgstr "Não é possível escrever ícone:"
+
+#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
+msgid "Rename Branch"
+msgstr "Mudar nome de ramo"
+
+#: lib/branch_rename.tcl:28
+msgid "Rename"
+msgstr "Mudar nome"
+
+#: lib/branch_rename.tcl:38
+msgid "Branch:"
+msgstr "Ramo:"
+
+#: lib/branch_rename.tcl:46
+msgid "New Name:"
+msgstr "Novo nome:"
+
+#: lib/branch_rename.tcl:81
+msgid "Please select a branch to rename."
+msgstr "Selecione um ramo para mudar de nome."
+
+#: lib/branch_rename.tcl:92 lib/branch_create.tcl:154
+msgid "Please supply a branch name."
+msgstr "Indique um nome para o ramo."
+
+#: lib/branch_rename.tcl:112 lib/branch_create.tcl:165
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "'%s' não pode ser aceite como nome de ramo."
+
+#: lib/branch_rename.tcl:123
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Falha ao mudar o nome de '%s'."
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Remover ramo remotamente"
+
+#: lib/remote_branch_delete.tcl:48
+msgid "From Repository"
+msgstr "Do repositório"
+
+#: lib/remote_branch_delete.tcl:88
+msgid "Branches"
+msgstr "Ramos"
+
+#: lib/remote_branch_delete.tcl:110
+msgid "Delete Only If"
+msgstr "Eliminar só se"
+
+#: lib/remote_branch_delete.tcl:112
+msgid "Merged Into:"
+msgstr "Integrar em:"
+
+#: lib/remote_branch_delete.tcl:120 lib/branch_delete.tcl:53
+msgid "Always (Do not perform merge checks)"
+msgstr "Sempre (não realizar verificação de integração)"
+
+#: lib/remote_branch_delete.tcl:153
+msgid "A branch is required for 'Merged Into'."
+msgstr "É necessário um ramo em 'Integrar em'."
+
+#: lib/remote_branch_delete.tcl:185
+#, tcl-format
+msgid ""
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
+msgstr ""
+"Os seguintes ramos não foram completamente integrados em %s:\n"
+"\n"
+" - %s"
+
+#: lib/remote_branch_delete.tcl:190
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits.  Try fetching from %s first."
+msgstr ""
+"Um ou mais testes de integração falharam porque não obteve os commits "
+"necessários. Tente primeiro obter de %s."
+
+#: lib/remote_branch_delete.tcl:208
+msgid "Please select one or more branches to delete."
+msgstr "Selecione um ou mais ramos para eliminar."
+
+#: lib/remote_branch_delete.tcl:218 lib/branch_delete.tcl:115
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Recuperar ramos eliminados é difícil.\n"
+"\n"
+"Eliminar os ramos selecionado?"
+
+#: lib/remote_branch_delete.tcl:227
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "A eliminar ramos de %s"
+
+#: lib/remote_branch_delete.tcl:300
+msgid "No repository selected."
+msgstr "Nenhum repositório selecionado."
+
+#: lib/remote_branch_delete.tcl:305
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "A analisar %s..."
+
+#: lib/choose_repository.tcl:33
+msgid "Git Gui"
+msgstr "Git Gui"
+
+#: lib/choose_repository.tcl:92 lib/choose_repository.tcl:412
+msgid "Create New Repository"
+msgstr "Criar novo repositório"
+
+#: lib/choose_repository.tcl:98
+msgid "New..."
+msgstr "Novo..."
+
+#: lib/choose_repository.tcl:105 lib/choose_repository.tcl:496
+msgid "Clone Existing Repository"
+msgstr "Clonar repositório existente"
+
+#: lib/choose_repository.tcl:116
+msgid "Clone..."
+msgstr "Clonar..."
+
+#: lib/choose_repository.tcl:123 lib/choose_repository.tcl:1064
+msgid "Open Existing Repository"
+msgstr "Abrir repositório existente"
+
+#: lib/choose_repository.tcl:129
+msgid "Open..."
+msgstr "Abrir..."
+
+#: lib/choose_repository.tcl:142
+msgid "Recent Repositories"
+msgstr "Repositórios recentes"
+
+#: lib/choose_repository.tcl:148
+msgid "Open Recent Repository:"
+msgstr "Abrir repositório recente:"
+
+#: lib/choose_repository.tcl:316 lib/choose_repository.tcl:323
+#: lib/choose_repository.tcl:330
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Falha ao criar o repositório %s:"
+
+#: lib/choose_repository.tcl:407 lib/branch_create.tcl:33
+msgid "Create"
+msgstr "Criar"
+
+#: lib/choose_repository.tcl:417
+msgid "Directory:"
+msgstr "Diretório:"
+
+#: lib/choose_repository.tcl:447 lib/choose_repository.tcl:573
+#: lib/choose_repository.tcl:1098
+msgid "Git Repository"
+msgstr "Repositório Git"
+
+#: lib/choose_repository.tcl:472
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "O diretório %s já existe."
+
+#: lib/choose_repository.tcl:476
+#, tcl-format
+msgid "File %s already exists."
+msgstr "O ficheiro %s já existe."
+
+#: lib/choose_repository.tcl:491
+msgid "Clone"
+msgstr "Clonar"
+
+#: lib/choose_repository.tcl:504
+msgid "Source Location:"
+msgstr "Localização de origem:"
+
+#: lib/choose_repository.tcl:513
+msgid "Target Directory:"
+msgstr "Diretório de destino:"
+
+#: lib/choose_repository.tcl:523
+msgid "Clone Type:"
+msgstr "Tipo de clone:"
+
+#: lib/choose_repository.tcl:528
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Padrão (rápido, semi-redundante, ligações fixas)"
+
+#: lib/choose_repository.tcl:533
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Cópia Total (lento, cópia de segurança redundante)"
+
+#: lib/choose_repository.tcl:538
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Partilhado (mais rápido, não recomendado, sem cópia)"
+
+#: lib/choose_repository.tcl:545
+msgid "Recursively clone submodules too"
+msgstr "Clonar recursivamente submódulos também"
+
+#: lib/choose_repository.tcl:579 lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:772 lib/choose_repository.tcl:842
+#: lib/choose_repository.tcl:1104 lib/choose_repository.tcl:1112
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Não é um repositório Git: %s"
+
+#: lib/choose_repository.tcl:615
+msgid "Standard only available for local repository."
+msgstr "Padrão só disponível em repositórios locais."
+
+#: lib/choose_repository.tcl:619
+msgid "Shared only available for local repository."
+msgstr "Partilhado só disponível em repositórios locais."
+
+#: lib/choose_repository.tcl:640
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "A localização %s já existe."
+
+#: lib/choose_repository.tcl:651
+msgid "Failed to configure origin"
+msgstr "Falha ao configurar origem"
+
+#: lib/choose_repository.tcl:663
+msgid "Counting objects"
+msgstr "A contar objetos"
+
+#: lib/choose_repository.tcl:664
+msgid "buckets"
+msgstr "baldes"
+
+#: lib/choose_repository.tcl:688
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Não é possível copiar objects/info/alternates: %s"
+
+#: lib/choose_repository.tcl:724
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Nada para clonar de %s."
+
+#: lib/choose_repository.tcl:726 lib/choose_repository.tcl:940
+#: lib/choose_repository.tcl:952
+msgid "The 'master' branch has not been initialized."
+msgstr "O ramo 'master' não foi inicializado."
+
+#: lib/choose_repository.tcl:739
+msgid "Hardlinks are unavailable.  Falling back to copying."
+msgstr "Ligações fixas indisponíveis. A recorrer a cópia."
+
+#: lib/choose_repository.tcl:751
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "A clonar de %s"
+
+#: lib/choose_repository.tcl:782
+msgid "Copying objects"
+msgstr "A copiar objetos"
+
+#: lib/choose_repository.tcl:783
+msgid "KiB"
+msgstr "KiB"
+
+#: lib/choose_repository.tcl:807
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Não é possível copiar objeto: %s"
+
+#: lib/choose_repository.tcl:817
+msgid "Linking objects"
+msgstr "A ligar objetos"
+
+#: lib/choose_repository.tcl:818
+msgid "objects"
+msgstr "objetos"
+
+#: lib/choose_repository.tcl:826
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Não é possível criar ligação fixa de objeto: %s"
+
+#: lib/choose_repository.tcl:881
+msgid "Cannot fetch branches and objects.  See console output for details."
+msgstr ""
+"Não é possível obter ramos e objetos. Ver saída na consola para detalhes."
+
+#: lib/choose_repository.tcl:892
+msgid "Cannot fetch tags.  See console output for details."
+msgstr "Não é possível obter tags. Ver saída na consola para detalhes."
+
+#: lib/choose_repository.tcl:916
+msgid "Cannot determine HEAD.  See console output for details."
+msgstr "Não é possível determinar HEAD. Ver saída na consola para detalhes."
+
+#: lib/choose_repository.tcl:925
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "Não foi possível limpar %s"
+
+#: lib/choose_repository.tcl:931
+msgid "Clone failed."
+msgstr "Falha ao clonar."
+
+#: lib/choose_repository.tcl:938
+msgid "No default branch obtained."
+msgstr "Não foi obtido nenhum ramo predefinido."
+
+#: lib/choose_repository.tcl:949
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Não é possível resolver %s como um commit."
+
+#: lib/choose_repository.tcl:961
+msgid "Creating working directory"
+msgstr "A criar diretório de trabalho"
+
+#: lib/choose_repository.tcl:962 lib/index.tcl:70 lib/index.tcl:136
+#: lib/index.tcl:207
+msgid "files"
+msgstr "ficheiros"
+
+#: lib/choose_repository.tcl:981
+msgid "Cannot clone submodules."
+msgstr "Não é possível clonar submódulos."
+
+#: lib/choose_repository.tcl:990
+msgid "Cloning submodules"
+msgstr "A clonar submódulos"
+
+#: lib/choose_repository.tcl:1015
+msgid "Initial file checkout failed."
+msgstr "Falha de extração inicial de ficheiro."
+
+#: lib/choose_repository.tcl:1059
+msgid "Open"
+msgstr "Abrir"
+
+#: lib/choose_repository.tcl:1069
+msgid "Repository:"
+msgstr "Repositório:"
+
+#: lib/choose_repository.tcl:1118
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Falha ao abrir o repositório %s:"
+
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui - uma interface gráfica do Git."
+
+#: lib/blame.tcl:73
+msgid "File Viewer"
+msgstr "Visualizador de ficheiros"
+
+#: lib/blame.tcl:79
+msgid "Commit:"
+msgstr "Commit:"
+
+#: lib/blame.tcl:280
+msgid "Copy Commit"
+msgstr "Copiar commit"
+
+#: lib/blame.tcl:284
+msgid "Find Text..."
+msgstr "Procurar texto..."
+
+#: lib/blame.tcl:288
+msgid "Goto Line..."
+msgstr "Ir para a linha..."
+
+#: lib/blame.tcl:297
+msgid "Do Full Copy Detection"
+msgstr "Efetuar deteção de cópia integral"
+
+#: lib/blame.tcl:301
+msgid "Show History Context"
+msgstr "Mostrar contexto histórico"
+
+#: lib/blame.tcl:304
+msgid "Blame Parent Commit"
+msgstr "Culpar commit pai"
+
+#: lib/blame.tcl:466
+#, tcl-format
+msgid "Reading %s..."
+msgstr "A ler %s..."
+
+#: lib/blame.tcl:594
+msgid "Loading copy/move tracking annotations..."
+msgstr "A carregar anotações de cópia/movimento..."
+
+#: lib/blame.tcl:614
+msgid "lines annotated"
+msgstr "linhas anotadas"
+
+#: lib/blame.tcl:806
+msgid "Loading original location annotations..."
+msgstr "A carregar anotações da localização original..."
+
+#: lib/blame.tcl:809
+msgid "Annotation complete."
+msgstr "Anotação concluída."
+
+#: lib/blame.tcl:839
+msgid "Busy"
+msgstr "A processar"
+
+#: lib/blame.tcl:840
+msgid "Annotation process is already running."
+msgstr "O processo de anotação já está em execução."
+
+#: lib/blame.tcl:879
+msgid "Running thorough copy detection..."
+msgstr "A executar deteção de cópia integral..."
+
+#: lib/blame.tcl:947
+msgid "Loading annotation..."
+msgstr "A carregar anotação..."
+
+#: lib/blame.tcl:1000
+msgid "Author:"
+msgstr "Autor:"
+
+#: lib/blame.tcl:1004
+msgid "Committer:"
+msgstr "Committer:"
+
+#: lib/blame.tcl:1009
+msgid "Original File:"
+msgstr "Ficheiro original:"
+
+#: lib/blame.tcl:1057
+msgid "Cannot find HEAD commit:"
+msgstr "Não é possível encontrar commit HEAD:"
+
+#: lib/blame.tcl:1112
+msgid "Cannot find parent commit:"
+msgstr "Não é possível encontrar commit pai:"
+
+#: lib/blame.tcl:1127
+msgid "Unable to display parent"
+msgstr "Não é possível mostrar pai"
+
+#: lib/blame.tcl:1269
+msgid "Originally By:"
+msgstr "Originalmente por:"
+
+#: lib/blame.tcl:1275
+msgid "In File:"
+msgstr "No ficheiro:"
+
+#: lib/blame.tcl:1280
+msgid "Copied Or Moved Here By:"
+msgstr "Copiado ou Movido para aqui por:"
+
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Nenhum chave encontrada."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Chave pública encontrada em: %s"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Gerar chave"
+
+#: lib/sshkey.tcl:58
+msgid "Copy To Clipboard"
+msgstr "Copiar para a área de transferência"
+
+#: lib/sshkey.tcl:72
+msgid "Your OpenSSH Public Key"
+msgstr "A sua chave OpenSSH pública"
+
+#: lib/sshkey.tcl:80
+msgid "Generating..."
+msgstr "A gerar..."
+
+#: lib/sshkey.tcl:86
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Não foi possível iniciar ssh-keygen:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:113
+msgid "Generation failed."
+msgstr "Falha ao gerar."
+
+#: lib/sshkey.tcl:120
+msgid "Generation succeeded, but no keys found."
+msgstr "Gerada com sucesso, mas não foi encontrada nenhum chave."
+
+#: lib/sshkey.tcl:123
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "A sua chave encontra-se em: %s"
+
+#: lib/branch_create.tcl:23
+msgid "Create Branch"
+msgstr "Criar ramo"
+
+#: lib/branch_create.tcl:28
+msgid "Create New Branch"
+msgstr "Cria novo ramo"
+
+#: lib/branch_create.tcl:42
+msgid "Branch Name"
+msgstr "Nome do ramo"
+
+#: lib/branch_create.tcl:57
+msgid "Match Tracking Branch Name"
+msgstr "Corresponder ao nome do ramo de monitorização"
+
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Revisão inicial"
+
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Atualizar ramo existente:"
+
+#: lib/branch_create.tcl:75
+msgid "No"
+msgstr "Não"
+
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Apenas avanço rápido (fast-forward)"
+
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Extrair depois de criar"
+
+#: lib/branch_create.tcl:132
+msgid "Please select a tracking branch."
+msgstr "Selecione um ramo de monitorização."
+
+#: lib/branch_create.tcl:141
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "O ramo de monitorização %s não é um ramo no repositório remoto."
+
+#: lib/commit.tcl:9
+msgid ""
+"There is nothing to amend.\n"
+"\n"
+"You are about to create the initial commit.  There is no commit before this "
+"to amend.\n"
+msgstr ""
+"Não há nada para emendar.\n"
+"\n"
+"Está prestes a criar o commit inicial. Não há nenhum commit antes deste para "
+"emendar.\n"
+
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed.  You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"Não é possível emendar ao mesmo tempo que se integra.\n"
+"\n"
+"Há uma integração em curso que não foi concluída. Não pode emendar o commit "
+"anterior a não ser que primeiro aborte a atividade da integração atual.\n"
+
+#: lib/commit.tcl:48
+msgid "Error loading commit data for amend:"
+msgstr "Erro ao carregar dados do commit para emendar:"
+
+#: lib/commit.tcl:75
+msgid "Unable to obtain your identity:"
+msgstr "Não é possível obter a sua identidade:"
+
+#: lib/commit.tcl:80
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "GIT_COMMITTER_IDENT inválido:"
+
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "aviso: Tcl não suporta a codificação '%s'."
+
+#: lib/commit.tcl:149
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before another commit can be created.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado analisado não corresponde ao estado do repositório.\n"
+"\n"
+"Outro programa Git modificou este repositório deste a última análise. Deve-"
+"se reanalisar antes que se possa criar outro commit.\n"
+"\n"
+"Irá-se reanalisar automaticamente agora.\n"
+
+#: lib/commit.tcl:173
+#, tcl-format
+msgid ""
+"Unmerged files cannot be committed.\n"
+"\n"
+"File %s has merge conflicts.  You must resolve them and stage the file "
+"before committing.\n"
+msgstr ""
+"Não pode fazer commit de ficheiros não integrados.\n"
+"\n"
+"O ficheiro %s tem conflitos de integração. Deve resolvê-los e preparar o "
+"ficheiro antes de submeter.\n"
+
+#: lib/commit.tcl:181
+#, tcl-format
+msgid ""
+"Unknown file state %s detected.\n"
+"\n"
+"File %s cannot be committed by this program.\n"
+msgstr ""
+"Detetado estado de ficheiro %s desconhecido.\n"
+"\n"
+"Este programa não pode submeter o ficheiro %s.\n"
+
+#: lib/commit.tcl:189
+msgid ""
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
+msgstr ""
+"Nenhum alteração para submeter.\n"
+"\n"
+"Deve preparar pelo menos 1 ficheiro antes de submeter.\n"
+
+#: lib/commit.tcl:204
+msgid ""
+"Please supply a commit message.\n"
+"\n"
+"A good commit message has the following format:\n"
+"\n"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
+msgstr ""
+"Forneça uma mensagem de commit.\n"
+"\n"
+"Um boa mensagem de commit tem o seguinte formato:\n"
+"\n"
+"- Primeira linha: descreve numa frase o que fez.\n"
+"- Segunda linha: em branco.\n"
+"- Linhas restantes: descreve porque esta alteração é vantajosa.\n"
+
+#: lib/commit.tcl:235
+msgid "Calling pre-commit hook..."
+msgstr "A invocar gancho de pré-commit (pre-commit hook)..."
+
+#: lib/commit.tcl:250
+msgid "Commit declined by pre-commit hook."
+msgstr "Commit recusado pela retina de pré-commit (pre-commit hook)."
+
+#: lib/commit.tcl:269
+msgid ""
+"You are about to commit on a detached head. This is a potentially dangerous "
+"thing to do because if you switch to another branch you will lose your "
+"changes and it can be difficult to retrieve them later from the reflog. You "
+"should probably cancel this commit and create a new branch to continue.\n"
+" \n"
+" Do you really want to proceed with your Commit?"
+msgstr ""
+"Está prestes a submeter numa cabeça destacada. Fazê-lo é potencialmente "
+"perigoso, porque, se mudar para outro ramo, perderá as suas alterações e "
+"pode ser difícil recuperá-las do reflog posteriormente. Provavelmente deve "
+"cancelar este commit e criar um novo ramo para continuar.\n"
+"\n"
+"Pretende mesmo continuar com o commit?"
+
+#: lib/commit.tcl:290
+msgid "Calling commit-msg hook..."
+msgstr "A invocar gancho de mensagem-de-commit (commit-msg hook)..."
+
+#: lib/commit.tcl:305
+msgid "Commit declined by commit-msg hook."
+msgstr "Commit recusado pelo gancho de mensagem-de-commit (commit-msg hook)."
+
+#: lib/commit.tcl:318
+msgid "Committing changes..."
+msgstr "A submeter alterações..."
+
+#: lib/commit.tcl:334
+msgid "write-tree failed:"
+msgstr "write-tree falhou:"
+
+#: lib/commit.tcl:335 lib/commit.tcl:379 lib/commit.tcl:400
+msgid "Commit failed."
+msgstr "Falha ao submeter."
+
+#: lib/commit.tcl:352
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "O commit %s parece estar corrompido"
+
+#: lib/commit.tcl:357
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
+msgstr ""
+"Não há alterações para submeter.\n"
+"\n"
+"Nenhum ficheiro foi modificado por este commit e não era um commit de "
+"integração.\n"
+"\n"
+"Irá-se reanalisar agora automaticamente.\n"
+
+#: lib/commit.tcl:364
+msgid "No changes to commit."
+msgstr "Não há alterações para submeter."
+
+#: lib/commit.tcl:378
+msgid "commit-tree failed:"
+msgstr "commit-tree falhou:"
+
+#: lib/commit.tcl:399
+msgid "update-ref failed:"
+msgstr "update-ref falhou:"
+
+#: lib/commit.tcl:492
+#, tcl-format
+msgid "Created commit %s: %s"
+msgstr "Commit %s criado: %s"
+
+#: lib/branch_delete.tcl:16
+msgid "Delete Branch"
+msgstr "Eliminar ramo"
+
+#: lib/branch_delete.tcl:21
+msgid "Delete Local Branch"
+msgstr "Eliminar ramo local"
+
+#: lib/branch_delete.tcl:39
+msgid "Local Branches"
+msgstr "Ramos locais"
+
+#: lib/branch_delete.tcl:51
+msgid "Delete Only If Merged Into"
+msgstr "Eliminar só se foi integrado"
+
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Os seguintes ramos não foram completamente integrados em %s:"
+
+#: lib/branch_delete.tcl:141
+#, tcl-format
+msgid ""
+"Failed to delete branches:\n"
+"%s"
+msgstr ""
+"Falha ao eliminar ramos:\n"
+"%s"
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Não é possível desbloquear o índice."
+
+#: lib/index.tcl:17
+msgid "Index Error"
+msgstr "Erro de Índice"
+
+#: lib/index.tcl:19
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"Falha ao atualizar o índice do Git. Irá-se reanalisar automaticamente para "
+"ressincronizar o git-gui."
+
+#: lib/index.tcl:30
+msgid "Continue"
+msgstr "Continuar"
+
+#: lib/index.tcl:33
+msgid "Unlock Index"
+msgstr "Desbloquear índice"
+
+#: lib/index.tcl:294
+msgid "Unstaging selected files from commit"
+msgstr "A retirar ficheiros selecionados do commit"
+
+#: lib/index.tcl:298
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "A retirar %s do commit"
+
+#: lib/index.tcl:337
+msgid "Ready to commit."
+msgstr "Pronto para submeter."
+
+#: lib/index.tcl:346
+msgid "Adding selected files"
+msgstr "A adicionar ficheiros selecionados"
+
+#: lib/index.tcl:350
+#, tcl-format
+msgid "Adding %s"
+msgstr "A adicionar %s"
+
+#: lib/index.tcl:380
+#, tcl-format
+msgid "Stage %d untracked files?"
+msgstr "Preparar %d ficheiros não controlados?"
+
+#: lib/index.tcl:388
+msgid "Adding all changed files"
+msgstr "A adicionar todos os ficheiros controlados"
+
+#: lib/index.tcl:428
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Reverter alterações no ficheiro %s?"
+
+#: lib/index.tcl:430
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Reverter alterações nestes %i ficheiros?"
+
+#: lib/index.tcl:438
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Qualquer alteração não preparada será permanentemente perdida ao reverter."
+
+#: lib/index.tcl:441
+msgid "Do Nothing"
+msgstr "Não fazer nada"
+
+#: lib/index.tcl:459
+msgid "Reverting selected files"
+msgstr "A reverter ficheiros selecionados"
+
+#: lib/index.tcl:463
+#, tcl-format
+msgid "Reverting %s"
+msgstr "A reverter %s"
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Predefinição"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Sistema (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Outro"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Data do Git inválida: %s"
+
+#: lib/choose_rev.tcl:52
+msgid "This Detached Checkout"
+msgstr "Esta extração destacada"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Expressão de revisão:"
+
+#: lib/choose_rev.tcl:72
+msgid "Local Branch"
+msgstr "Ramo local"
+
+#: lib/choose_rev.tcl:77
+msgid "Tracking Branch"
+msgstr "Ramo de monitorização"
+
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
+msgid "Tag"
+msgstr "Tag"
+
+#: lib/choose_rev.tcl:321
+#, tcl-format
+msgid "Invalid revision: %s"
+msgstr "Revisão inválida: %s"
+
+#: lib/choose_rev.tcl:342
+msgid "No revision selected."
+msgstr "Nenhum revisão selecionada."
+
+#: lib/choose_rev.tcl:350
+msgid "Revision expression is empty."
+msgstr "A expressão de revisão está vazia."
+
+#: lib/choose_rev.tcl:537
+msgid "Updated"
+msgstr "Atualizado"
+
+#: lib/choose_rev.tcl:565
+msgid "URL"
+msgstr "URL"
+
+#: lib/database.tcl:42
+msgid "Number of loose objects"
+msgstr "Número de objetos soltos"
+
+#: lib/database.tcl:43
+msgid "Disk space used by loose objects"
+msgstr "Espaço em disco usados por objetos soltos"
+
+#: lib/database.tcl:44
+msgid "Number of packed objects"
+msgstr "Número de objetos compactados"
+
+#: lib/database.tcl:45
+msgid "Number of packs"
+msgstr "Números de pacotes"
+
+#: lib/database.tcl:46
+msgid "Disk space used by packed objects"
+msgstr "Espaço em disco usado por objetos compactados"
+
+#: lib/database.tcl:47
+msgid "Packed objects waiting for pruning"
+msgstr "Objetos compactados à espera de poda"
+
+#: lib/database.tcl:48
+msgid "Garbage files"
+msgstr "Ficheiros de lixo"
+
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "A comprimir a base de dados de objetos"
+
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr "A verificar a base de dados de objetos com fsck-objects"
+
+#: lib/database.tcl:107
+#, tcl-format
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"Este repositório tem aproximadamente %i objetos soltos.\n"
+"\n"
+"Para manter o desempenho ótimo é veemente recomendado que comprima a base de "
+"dados.\n"
+"\n"
+"Comprimir a base de dados agora?"
+
+#: lib/error.tcl:20 lib/error.tcl:116
+msgid "error"
+msgstr "erro"
+
+#: lib/error.tcl:36
+msgid "warning"
+msgstr "aviso"
+
+#: lib/error.tcl:96
+msgid "You must correct the above errors before committing."
+msgstr "Deve corrigir os erros acima antes de submeter."
+
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"Não possível integrar ao mesmo tempo que se emenda.\n"
+"\n"
+"Deve acabar de emendar este commit antes de iniciar qualquer tipo de "
+"integração.\n"
+
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan.  A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"O último estado analisado não corresponde ao estado do repositório.\n"
+"\n"
+"Outro programa Git modificou este repositório deste a última análise. Deve-"
+"se reanalisar antes de se poder integrar.\n"
+"\n"
+"Irá-se reanalisar agora automaticamente.\n"
+
+#: lib/merge.tcl:45
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge.  Only then can you begin another merge.\n"
+msgstr ""
+"Integração com conflitos em curso.\n"
+"\n"
+"O ficheiro %s tem conflitos de integração.\n"
+"\n"
+"Deve resolvê-los, preparar o ficheiro e submeter para concluir a integração "
+"atual. Só então pode iniciar outra integração.\n"
+
+#: lib/merge.tcl:55
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge.  Doing so "
+"will help you abort a failed merge, should the need arise.\n"
+msgstr ""
+"Tem alterações presentes.\n"
+"\n"
+"O ficheiro %s foi modificado.\n"
+"\n"
+"Deve concluir o commit atual antes de iniciar uma integração. Assim, ajuda-o "
+"a abortar uma integração falhada, caso necessário.\n"
+
+#: lib/merge.tcl:108
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s de %s"
+
+#: lib/merge.tcl:122
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "A integrar %s e %s..."
+
+#: lib/merge.tcl:133
+msgid "Merge completed successfully."
+msgstr "Integração concluída com sucesso."
+
+#: lib/merge.tcl:135
+msgid "Merge failed.  Conflict resolution is required."
+msgstr "Integração falhada. É necessário resolver conflitos."
+
+#: lib/merge.tcl:160
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Integrar em %s"
+
+#: lib/merge.tcl:179
+msgid "Revision To Merge"
+msgstr "Revisão a integrar"
+
+#: lib/merge.tcl:214
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Não é possível abortar enquanto se emenda.\n"
+"\n"
+"Deve acabar de emendar este commit.\n"
+
+#: lib/merge.tcl:224
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Abortar integração?\n"
+"\n"
+"Ao abortar a integração atual perderá *TODAS* as alteração que não foram "
+"submetidas.\n"
+"\n"
+"Continuar a abortar a integração atual?"
+
+#: lib/merge.tcl:230
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Repor alterações?\n"
+"\n"
+"Ao repor as alterações perderá *TODAS* as alterações não submetidas.\n"
+"\n"
+"Continuar a repor as alterações atuais?"
+
+#: lib/merge.tcl:241
+msgid "Aborting"
+msgstr "A abortar"
+
+#: lib/merge.tcl:241
+msgid "files reset"
+msgstr "ficheiros repostos"
+
+#: lib/merge.tcl:269
+msgid "Abort failed."
+msgstr "Falha ao abortar."
+
+#: lib/merge.tcl:271
+msgid "Abort completed.  Ready."
+msgstr "Aborto concluído. Pronto."
+
+#~ msgid "Displaying only %s of %s files."
+#~ msgstr "A mostrar apenas %s de %s ficheiros."
+
+#~ msgid "Case-Sensitive"
+#~ msgstr "Distinguir Maiúsculas"
index 5e474e4a8584fc8ccfc7a638f9fb7a3e98f2c1e3..ca994c5c545cb0394fc5d355ecb6ea2fe5f1f193 100644 (file)
@@ -1041,7 +1041,7 @@ The possible behaviours are: ignore, warn, error.")"
                # placed before the commit of the next action
                checkout_onto
 
-               warn "$(gettext "You can fix this with 'git rebase --edit-todo'.")"
+               warn "$(gettext "You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.")"
                die "$(gettext "Or you can abort the rebase with 'git rebase --abort'.")"
        fi
 }
index 826af183d489d06468f03ee8002e05da0d7ffd34..90d63f293e8359f63a3c2350850fcad17de2b455 100755 (executable)
@@ -100,7 +100,7 @@ create_stash () {
                                u_tree=$(git write-tree) &&
                                printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
                                rm -f "$TMPindex"
-               ) ) || die "Cannot save the untracked files"
+               ) ) || die "$(gettext "Cannot save the untracked files")"
 
                untracked_commit_option="-p $u_commit";
        else
@@ -248,7 +248,7 @@ save_stash () {
 
        if test -n "$patch_mode" && test -n "$untracked"
        then
-           die "Can't use --patch and --include-untracked or --all at the same time"
+               die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
        fi
 
        stash_msg="$*"
@@ -494,7 +494,7 @@ apply_stash () {
                GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
                GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
                rm -f "$TMPindex" ||
-               die 'Could not restore untracked files from stash'
+               die "$(gettext "Could not restore untracked files from stash")"
        fi
 
        eval "
index a1cc71b521b76c346509207f4ef9a24d48ead229..a024a135d6663c8a8d5ceb926e3f42df1f577411 100755 (executable)
@@ -44,6 +44,7 @@ update=
 prefix=
 custom_name=
 depth=
+progress=
 
 die_if_unmatched ()
 {
@@ -498,6 +499,9 @@ cmd_update()
                -q|--quiet)
                        GIT_QUIET=1
                        ;;
+               --progress)
+                       progress="--progress"
+                       ;;
                -i|--init)
                        init=1
                        ;;
@@ -573,6 +577,7 @@ cmd_update()
 
        {
        git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
+               ${progress:+"$progress"} \
                ${wt_prefix:+--prefix "$wt_prefix"} \
                ${prefix:+--recursive-prefix "$prefix"} \
                ${update:+--update "$update"} \
index 33d701d8525fd9334e4a899a807c8f8f0164dcc5..44094f41d580bd4259bdb2bdb680b6dff4fd5340 100755 (executable)
@@ -3913,7 +3913,7 @@ sub blob_contenttype {
 # guess file syntax for syntax highlighting; return undef if no highlighting
 # the name of syntax can (in the future) depend on syntax highlighter used
 sub guess_file_syntax {
-       my ($highlight, $mimetype, $file_name) = @_;
+       my ($highlight, $file_name) = @_;
        return undef unless ($highlight && defined $file_name);
        my $basename = basename($file_name, '.in');
        return $highlight_basename{$basename}
@@ -3931,15 +3931,16 @@ sub guess_file_syntax {
 # or return original FD if no highlighting
 sub run_highlighter {
        my ($fd, $highlight, $syntax) = @_;
-       return $fd unless ($highlight && defined $syntax);
+       return $fd unless ($highlight);
 
        close $fd;
+       my $syntax_arg = (defined $syntax) ? "--syntax $syntax" : "--force";
        open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
                  quote_command($^X, '-CO', '-MEncode=decode,FB_DEFAULT', '-pse',
                    '$_ = decode($fe, $_, FB_DEFAULT) if !utf8::decode($_);',
                    '--', "-fe=$fallback_encoding")." | ".
                  quote_command($highlight_bin).
-                 " --replace-tabs=8 --fragment --syntax $syntax |"
+                 " --replace-tabs=8 --fragment $syntax_arg |"
                or die_error(500, "Couldn't open file or run syntax highlighter");
        return $fd;
 }
@@ -7062,9 +7063,8 @@ sub git_blob {
        $have_blame &&= ($mimetype =~ m!^text/!);
 
        my $highlight = gitweb_check_feature('highlight');
-       my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
-       $fd = run_highlighter($fd, $highlight, $syntax)
-               if $syntax;
+       my $syntax = guess_file_syntax($highlight, $file_name);
+       $fd = run_highlighter($fd, $highlight, $syntax);
 
        git_header_html(undef, $expires);
        my $formats_nav = '';
@@ -7117,7 +7117,7 @@ sub git_blob {
                        $line = untabify($line);
                        printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
                               $nr, esc_attr(href(-replay => 1)), $nr, $nr,
-                              $syntax ? sanitize($line) : esc_html($line, -nbsp=>1);
+                              $highlight ? sanitize($line) : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
diff --git a/grep.c b/grep.c
index d7d00b87cb2a28332ccad4ea4ff676e3f2bc2611..1194d35b5d06d7960d4464884952e755914e2519 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -898,17 +898,6 @@ static int fixmatch(struct grep_pat *p, char *line, char *eol,
        }
 }
 
-static int regmatch(const regex_t *preg, char *line, char *eol,
-                   regmatch_t *match, int eflags)
-{
-#ifdef REG_STARTEND
-       match->rm_so = 0;
-       match->rm_eo = eol - line;
-       eflags |= REG_STARTEND;
-#endif
-       return regexec(preg, line, 1, match, eflags);
-}
-
 static int patmatch(struct grep_pat *p, char *line, char *eol,
                    regmatch_t *match, int eflags)
 {
@@ -919,7 +908,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
        else if (p->pcre_regexp)
                hit = !pcrematch(p, line, eol, match, eflags);
        else
-               hit = !regmatch(&p->regexp, line, eol, match, eflags);
+               hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
+                                  eflags);
 
        return hit;
 }
diff --git a/ident.c b/ident.c
index e20a772dde4230b0871ffe85a5204919402aaf94..ac4ae02b485637002869b3fc7a78033d7a5b6aec 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -101,7 +101,7 @@ static int canonical_name(const char *host, struct strbuf *out)
        memset (&hints, '\0', sizeof (hints));
        hints.ai_flags = AI_CANONNAME;
        if (!getaddrinfo(host, NULL, &hints, &ai)) {
-               if (ai && strchr(ai->ai_canonname, '.')) {
+               if (ai && ai->ai_canonname && strchr(ai->ai_canonname, '.')) {
                        strbuf_addstr(out, ai->ai_canonname);
                        status = 0;
                }
@@ -331,17 +331,17 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
 }
 
 static const char *env_hint =
-"\n"
-"*** Please tell me who you are.\n"
-"\n"
-"Run\n"
-"\n"
-"  git config --global user.email \"you@example.com\"\n"
-"  git config --global user.name \"Your Name\"\n"
-"\n"
-"to set your account\'s default identity.\n"
-"Omit --global to set the identity only in this repository.\n"
-"\n";
+N_("\n"
+   "*** Please tell me who you are.\n"
+   "\n"
+   "Run\n"
+   "\n"
+   "  git config --global user.email \"you@example.com\"\n"
+   "  git config --global user.name \"Your Name\"\n"
+   "\n"
+   "to set your account\'s default identity.\n"
+   "Omit --global to set the identity only in this repository.\n"
+   "\n");
 
 const char *fmt_ident(const char *name, const char *email,
                      const char *date_str, int flag)
@@ -356,13 +356,13 @@ const char *fmt_ident(const char *name, const char *email,
                if (!name) {
                        if (strict && ident_use_config_only
                            && !(ident_config_given & IDENT_NAME_GIVEN)) {
-                               fputs(env_hint, stderr);
+                               fputs(_(env_hint), stderr);
                                die("no name was given and auto-detection is disabled");
                        }
                        name = ident_default_name();
                        using_default = 1;
                        if (strict && default_name_is_bogus) {
-                               fputs(env_hint, stderr);
+                               fputs(_(env_hint), stderr);
                                die("unable to auto-detect name (got '%s')", name);
                        }
                }
@@ -370,7 +370,7 @@ const char *fmt_ident(const char *name, const char *email,
                        struct passwd *pw;
                        if (strict) {
                                if (using_default)
-                                       fputs(env_hint, stderr);
+                                       fputs(_(env_hint), stderr);
                                die("empty ident name (for <%s>) not allowed", email);
                        }
                        pw = xgetpwuid_self(NULL);
@@ -381,12 +381,12 @@ const char *fmt_ident(const char *name, const char *email,
        if (!email) {
                if (strict && ident_use_config_only
                    && !(ident_config_given & IDENT_MAIL_GIVEN)) {
-                       fputs(env_hint, stderr);
+                       fputs(_(env_hint), stderr);
                        die("no email was given and auto-detection is disabled");
                }
                email = ident_default_email();
                if (strict && default_email_is_bogus) {
-                       fputs(env_hint, stderr);
+                       fputs(_(env_hint), stderr);
                        die("unable to auto-detect email address (got '%s')", email);
                }
        }
index e19abe3cb93e24aed8848cadec8ddf8a8767f303..2fb3877ee44e9cdc83c43fa23971f02a94b5a49f 100644 (file)
@@ -54,6 +54,86 @@ static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
        get_sane_name(&mi->name, &mi->name, &mi->email);
 }
 
+static const char *unquote_comment(struct strbuf *outbuf, const char *in)
+{
+       int c;
+       int take_next_litterally = 0;
+
+       strbuf_addch(outbuf, '(');
+
+       while ((c = *in++) != 0) {
+               if (take_next_litterally == 1) {
+                       take_next_litterally = 0;
+               } else {
+                       switch (c) {
+                       case '\\':
+                               take_next_litterally = 1;
+                               continue;
+                       case '(':
+                               in = unquote_comment(outbuf, in);
+                               continue;
+                       case ')':
+                               strbuf_addch(outbuf, ')');
+                               return in;
+                       }
+               }
+
+               strbuf_addch(outbuf, c);
+       }
+
+       return in;
+}
+
+static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
+{
+       int c;
+       int take_next_litterally = 0;
+
+       while ((c = *in++) != 0) {
+               if (take_next_litterally == 1) {
+                       take_next_litterally = 0;
+               } else {
+                       switch (c) {
+                       case '\\':
+                               take_next_litterally = 1;
+                               continue;
+                       case '"':
+                               return in;
+                       }
+               }
+
+               strbuf_addch(outbuf, c);
+       }
+
+       return in;
+}
+
+static void unquote_quoted_pair(struct strbuf *line)
+{
+       struct strbuf outbuf;
+       const char *in = line->buf;
+       int c;
+
+       strbuf_init(&outbuf, line->len);
+
+       while ((c = *in++) != 0) {
+               switch (c) {
+               case '"':
+                       in = unquote_quoted_string(&outbuf, in);
+                       continue;
+               case '(':
+                       in = unquote_comment(&outbuf, in);
+                       continue;
+               }
+
+               strbuf_addch(&outbuf, c);
+       }
+
+       strbuf_swap(&outbuf, line);
+       strbuf_release(&outbuf);
+
+}
+
 static void handle_from(struct mailinfo *mi, const struct strbuf *from)
 {
        char *at;
@@ -63,6 +143,8 @@ static void handle_from(struct mailinfo *mi, const struct strbuf *from)
        strbuf_init(&f, from->len);
        strbuf_addbuf(&f, from);
 
+       unquote_quoted_pair(&f);
+
        at = strchr(f.buf, '@');
        if (!at) {
                parse_bogus_from(mi, from);
@@ -495,26 +577,26 @@ static int check_header(struct mailinfo *mi,
                goto check_header_out;
        }
 
-       /* for inbody stuff */
-       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-               ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-               goto check_header_out;
-       }
-       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-               for (i = 0; header[i]; i++) {
-                       if (!strcmp("Subject", header[i])) {
-                               handle_header(&hdr_data[i], line);
-                               ret = 1;
-                               goto check_header_out;
-                       }
-               }
-       }
-
 check_header_out:
        strbuf_release(&sb);
        return ret;
 }
 
+/*
+ * Returns 1 if the given line or any line beginning with the given line is an
+ * in-body header (that is, check_header will succeed when passed
+ * mi->s_hdr_data).
+ */
+static int is_inbody_header(const struct mailinfo *mi,
+                           const struct strbuf *line)
+{
+       int i;
+       for (i = 0; header[i]; i++)
+               if (!mi->s_hdr_data[i] && cmp_header(line, header[i]))
+                       return 1;
+       return 0;
+}
+
 static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
 {
        struct strbuf *ret;
@@ -572,37 +654,35 @@ static inline int patchbreak(const struct strbuf *line)
        return 0;
 }
 
-static int is_scissors_line(const struct strbuf *line)
+static int is_scissors_line(const char *line)
 {
-       size_t i, len = line->len;
+       const char *c;
        int scissors = 0, gap = 0;
-       int first_nonblank = -1;
-       int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
-       const char *buf = line->buf;
+       const char *first_nonblank = NULL, *last_nonblank = NULL;
+       int visible, perforation = 0, in_perforation = 0;
 
-       for (i = 0; i < len; i++) {
-               if (isspace(buf[i])) {
+       for (c = line; *c; c++) {
+               if (isspace(*c)) {
                        if (in_perforation) {
                                perforation++;
                                gap++;
                        }
                        continue;
                }
-               last_nonblank = i;
-               if (first_nonblank < 0)
-                       first_nonblank = i;
-               if (buf[i] == '-') {
+               last_nonblank = c;
+               if (first_nonblank == NULL)
+                       first_nonblank = c;
+               if (*c == '-') {
                        in_perforation = 1;
                        perforation++;
                        continue;
                }
-               if (i + 1 < len &&
-                   (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
-                    !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
+               if ((!memcmp(c, ">8", 2) || !memcmp(c, "8<", 2) ||
+                    !memcmp(c, ">%", 2) || !memcmp(c, "%<", 2))) {
                        in_perforation = 1;
                        perforation += 2;
                        scissors += 2;
-                       i++;
+                       c++;
                        continue;
                }
                in_perforation = 0;
@@ -617,12 +697,60 @@ static int is_scissors_line(const struct strbuf *line)
         * than half of the perforation.
         */
 
-       visible = last_nonblank - first_nonblank + 1;
+       if (first_nonblank && last_nonblank)
+               visible = last_nonblank - first_nonblank + 1;
+       else
+               visible = 0;
        return (scissors && 8 <= visible &&
                visible < perforation * 3 &&
                gap * 2 < perforation);
 }
 
+static void flush_inbody_header_accum(struct mailinfo *mi)
+{
+       if (!mi->inbody_header_accum.len)
+               return;
+       assert(check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0));
+       strbuf_reset(&mi->inbody_header_accum);
+}
+
+static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)
+{
+       if (mi->inbody_header_accum.len &&
+           (line->buf[0] == ' ' || line->buf[0] == '\t')) {
+               if (mi->use_scissors && is_scissors_line(line->buf)) {
+                       /*
+                        * This is a scissors line; do not consider this line
+                        * as a header continuation line.
+                        */
+                       flush_inbody_header_accum(mi);
+                       return 0;
+               }
+               strbuf_strip_suffix(&mi->inbody_header_accum, "\n");
+               strbuf_addbuf(&mi->inbody_header_accum, line);
+               return 1;
+       }
+
+       flush_inbody_header_accum(mi);
+
+       if (starts_with(line->buf, ">From") && isspace(line->buf[5]))
+               return is_format_patch_separator(line->buf + 1, line->len - 1);
+       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+               int i;
+               for (i = 0; header[i]; i++)
+                       if (!strcmp("Subject", header[i])) {
+                               handle_header(&mi->s_hdr_data[i], line);
+                               return 1;
+                       }
+               return 0;
+       }
+       if (is_inbody_header(mi, line)) {
+               strbuf_addbuf(&mi->inbody_header_accum, line);
+               return 1;
+       }
+       return 0;
+}
+
 static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
 {
        assert(!mi->filter_stage);
@@ -633,7 +761,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
        }
 
        if (mi->use_inbody_headers && mi->header_stage) {
-               mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
+               mi->header_stage = check_inbody_header(mi, line);
                if (mi->header_stage)
                        return 0;
        } else
@@ -646,7 +774,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
        if (convert_to_utf8(mi, line, mi->charset.buf))
                return 0; /* mi->input_error already set */
 
-       if (mi->use_scissors && is_scissors_line(line)) {
+       if (mi->use_scissors && is_scissors_line(line->buf)) {
                int i;
 
                strbuf_setlen(&mi->log_message, 0);
@@ -886,6 +1014,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
                        break;
        } while (!strbuf_getwholeline(line, mi->input, '\n'));
 
+       flush_inbody_header_accum(mi);
+
 handle_body_out:
        strbuf_release(&prev);
 }
@@ -1001,6 +1131,7 @@ void setup_mailinfo(struct mailinfo *mi)
        strbuf_init(&mi->email, 0);
        strbuf_init(&mi->charset, 0);
        strbuf_init(&mi->log_message, 0);
+       strbuf_init(&mi->inbody_header_accum, 0);
        mi->header_stage = 1;
        mi->use_inbody_headers = 1;
        mi->content_top = mi->content;
@@ -1014,6 +1145,7 @@ void clear_mailinfo(struct mailinfo *mi)
        strbuf_release(&mi->name);
        strbuf_release(&mi->email);
        strbuf_release(&mi->charset);
+       strbuf_release(&mi->inbody_header_accum);
        free(mi->message_id);
 
        for (i = 0; mi->p_hdr_data[i]; i++)
index 93776a7e05a68c49951e9e8e062c164c364d5d45..04a25351d6d39c4d0bb00943bb23ee99e8d41bcf 100644 (file)
@@ -27,6 +27,7 @@ struct mailinfo {
        int patch_lines;
        int filter_stage; /* still reading log or are we copying patch? */
        int header_stage; /* still checking in-body headers? */
+       struct strbuf inbody_header_accum;
        struct strbuf **p_hdr_data;
        struct strbuf **s_hdr_data;
 
index 3750d2534f5f2e9842375516eb75143a8e52d7fe..5200d5ccf8e10b9d5726f1c814f43dfebf32531d 100644 (file)
@@ -206,7 +206,7 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                        find_unique_abbrev(commit->object.oid.hash,
                                DEFAULT_ABBREV));
                if (parse_commit(commit) != 0)
-                       strbuf_addf(&o->obuf, _("(bad commit)\n"));
+                       strbuf_addstr(&o->obuf, _("(bad commit)\n"));
                else {
                        const char *title;
                        const char *msg = get_commit_buffer(commit, NULL);
index b3536284c4954be3f24f9c68e4d52c42ec29eed1..5998605acc64471193dc9d945610db8491f34bd8 100644 (file)
@@ -270,15 +270,15 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
                if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
                    !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
                        if (advice_resolve_conflict)
-                               die("You have not concluded your previous "
+                               die(_("You have not concluded your previous "
                                    "notes merge (%s exists).\nPlease, use "
                                    "'git notes merge --commit' or 'git notes "
                                    "merge --abort' to commit/abort the "
                                    "previous merge before you start a new "
-                                   "notes merge.", git_path("NOTES_MERGE_*"));
+                                   "notes merge."), git_path("NOTES_MERGE_*"));
                        else
-                               die("You have not concluded your notes merge "
-                                   "(%s exists).", git_path("NOTES_MERGE_*"));
+                               die(_("You have not concluded your notes merge "
+                                   "(%s exists)."), git_path("NOTES_MERGE_*"));
                }
 
                if (safe_create_leading_directories_const(git_path(
index d123846ea2be7c34360049864aeb3a88f505dfd1..c5c7763323156232748314723e39deb92b93fa4e 100644 (file)
@@ -57,11 +57,8 @@ static int verify_packfile(struct packed_git *p,
        int err = 0;
        struct idx_entry *entries;
 
-       /* Note that the pack header checks are actually performed by
-        * use_pack when it first opens the pack file.  If anything
-        * goes wrong during those checks then the call will die out
-        * immediately.
-        */
+       if (!is_pack_valid(p))
+               return error("packfile %s cannot be accessed", p->pack_name);
 
        git_SHA1_Init(&ctx);
        do {
index 96d51c3467b9ef864f62962aa3567247338d2c3a..6bc7c940335cdf2d31cb38c2db6f8f6c985d3c3b 100644 (file)
@@ -107,7 +107,7 @@ static void sort_revindex(struct revindex_entry *entries, unsigned n, off_t max)
         * we have to move it back from the temporary storage.
         */
        if (from != entries)
-               memcpy(entries, tmp, n * sizeof(*entries));
+               COPY_ARRAY(entries, tmp, n);
        free(tmp);
        free(pos);
 
index 9667bc75a08e8b64290f5f44c92b0bac35d1d0fa..b5d920914e2a3ba3a46017f89f9952d08b8e5b58 100644 (file)
@@ -158,6 +158,18 @@ int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+/**
+ * Report that the option is unknown, so that other code can handle
+ * it. This can be used as a callback together with
+ * OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
+ * "-h" output even if it's not being handled directly by
+ * parse_options().
+ */
+int parse_opt_unknown_cb(const struct option *opt, const char *arg, int unset)
+{
+       return -2;
+}
+
 /**
  * Recreates the command-line option in the strbuf.
  */
index 78f8384c56b02cbabcc586b3ee082cb1cb12e764..dcd8a0926cc9c18fd49d800bc83064259eced279 100644 (file)
@@ -228,6 +228,7 @@ extern int parse_opt_commits(const struct option *, const char *, int);
 extern int parse_opt_tertiary(const struct option *, const char *, int);
 extern int parse_opt_string_list(const struct option *, const char *, int);
 extern int parse_opt_noop_cb(const struct option *, const char *, int);
+extern int parse_opt_unknown_cb(const struct option *, const char *, int);
 extern int parse_opt_passthru(const struct option *, const char *, int);
 extern int parse_opt_passthru_argv(const struct option *, const char *, int);
 
index 24e0dd52322baf4a507765ae0948c98f0ec30602..49a53607bb004e8ccc34e6a18eadc8dfc2e7879d 100644 (file)
@@ -485,8 +485,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
        *dst = *src;
        ALLOC_ARRAY(dst->items, dst->nr);
-       memcpy(dst->items, src->items,
-              sizeof(struct pathspec_item) * dst->nr);
+       COPY_ARRAY(dst->items, src->items, dst->nr);
 }
 
 void clear_pathspec(struct pathspec *pathspec)
index 9788bd8f3f0ece03a6b87778a2d43bfaf3c14a9d..493edb0a446ec0019e39e8f4f2ca8a4e108c40f5 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1072,6 +1072,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = want_color(c->pretty_ctx->color);
+                       if (c->auto_color)
+                               strbuf_addstr(sb, GIT_COLOR_RESET);
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
                        int ret = parse_color(sb, placeholder, c);
index 31eddec5f751b2c8d8abda0698869bca133b606a..38d67faf708d7b88f208f10dad23f893dd16f587 100644 (file)
@@ -627,7 +627,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
        hashcpy(ce->oid.hash, sha1);
 }
 
-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
 {
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
@@ -656,11 +656,10 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        else
                ce->ce_flags |= CE_INTENT_TO_ADD;
 
-       if (S_ISREG(st_mode) && force_mode)
-               ce->ce_mode = create_ce_mode(force_mode);
-       else if (trust_executable_bit && has_symlinks)
+
+       if (trust_executable_bit && has_symlinks) {
                ce->ce_mode = create_ce_mode(st_mode);
-       else {
+       else {
                /* If there is an existing entry, pick the mode bits and type
                 * from it, otherwise assume unexecutable regular file.
                 */
@@ -719,13 +718,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        return 0;
 }
 
-int add_file_to_index(struct index_state *istate, const char *path,
-       int flags, int force_mode)
+int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
        struct stat st;
        if (lstat(path, &st))
                die_errno("unable to stat '%s'", path);
-       return add_to_index(istate, path, &st, flags, force_mode);
+       return add_to_index(istate, path, &st, flags);
 }
 
 struct cache_entry *make_cache_entry(unsigned int mode,
@@ -756,6 +754,35 @@ struct cache_entry *make_cache_entry(unsigned int mode,
        return ret;
 }
 
+/*
+ * Chmod an index entry with either +x or -x.
+ *
+ * Returns -1 if the chmod for the particular cache entry failed (if it's
+ * not a regular file), -2 if an invalid flip argument is passed in, 0
+ * otherwise.
+ */
+int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
+                     char flip)
+{
+       if (!S_ISREG(ce->ce_mode))
+               return -1;
+       switch (flip) {
+       case '+':
+               ce->ce_mode |= 0111;
+               break;
+       case '-':
+               ce->ce_mode &= ~0111;
+               break;
+       default:
+               return -2;
+       }
+       cache_tree_invalidate_path(istate, ce->name);
+       ce->ce_flags |= CE_UPDATE_IN_BASE;
+       istate->cache_changed |= CE_ENTRY_CHANGED;
+
+       return 0;
+}
+
 int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
 {
        int len = ce_namelen(a);
index d29850a81cdb09b41da5dc3e7284106672d85a4c..ad6c5424edab2ae15ac17bfcf12ac4ee93b5aa3f 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -2073,7 +2073,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                        _("Your branch is based on '%s', but the upstream is gone.\n"),
                        base);
                if (advice_status_hints)
-                       strbuf_addf(sb,
+                       strbuf_addstr(sb,
                                _("  (use \"git branch --unset-upstream\" to fixup)\n"));
        } else if (!ours && !theirs) {
                strbuf_addf(sb,
@@ -2086,7 +2086,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                           ours),
                        base, ours);
                if (advice_status_hints)
-                       strbuf_addf(sb,
+                       strbuf_addstr(sb,
                                _("  (use \"git push\" to publish your local commits)\n"));
        } else if (!ours) {
                strbuf_addf(sb,
@@ -2097,7 +2097,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                           theirs),
                        base, theirs);
                if (advice_status_hints)
-                       strbuf_addf(sb,
+                       strbuf_addstr(sb,
                                _("  (use \"git pull\" to update your local branch)\n"));
        } else {
                strbuf_addf(sb,
@@ -2110,7 +2110,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)
                           ours + theirs),
                        base, ours, theirs);
                if (advice_status_hints)
-                       strbuf_addf(sb,
+                       strbuf_addstr(sb,
                                _("  (use \"git pull\" to merge the remote branch into yours)\n"));
        }
        free(base);
index b9c1fa3f1a0c24fa411d590987b60362cae6f943..94daf31ec6b73f99c4e07ed3b6f9235c2f1baec8 100644 (file)
@@ -1646,7 +1646,9 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        return used;
 }
 
-int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
+static int unpack_sha1_short_header(git_zstream *stream,
+                                   unsigned char *map, unsigned long mapsize,
+                                   void *buffer, unsigned long bufsiz)
 {
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
@@ -1659,13 +1661,31 @@ int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long ma
        return git_inflate(stream, 0);
 }
 
+int unpack_sha1_header(git_zstream *stream,
+                      unsigned char *map, unsigned long mapsize,
+                      void *buffer, unsigned long bufsiz)
+{
+       int status = unpack_sha1_short_header(stream, map, mapsize,
+                                             buffer, bufsiz);
+
+       if (status < Z_OK)
+               return status;
+
+       /* Make sure we have the terminating NUL */
+       if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
+               return -1;
+       return 0;
+}
+
 static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
                                        unsigned long mapsize, void *buffer,
                                        unsigned long bufsiz, struct strbuf *header)
 {
        int status;
 
-       status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
+       status = unpack_sha1_short_header(stream, map, mapsize, buffer, bufsiz);
+       if (status < Z_OK)
+               return -1;
 
        /*
         * Check if entire header is unpacked in the first iteration.
@@ -1756,6 +1776,8 @@ static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
         */
        for (;;) {
                char c = *hdr++;
+               if (!c)
+                       return -1;
                if (c == ' ')
                        break;
                type_len++;
index 3c75d4b9ce717fa92520881ef754485d068a3a60..35da553655bdf7b70519ecf529544796ca8185b5 100644 (file)
@@ -83,8 +83,7 @@ void move_cache_to_base_index(struct index_state *istate)
        si->base->timestamp = istate->timestamp;
        ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc);
        si->base->cache_nr = istate->cache_nr;
-       memcpy(si->base->cache, istate->cache,
-              sizeof(*istate->cache) * istate->cache_nr);
+       COPY_ARRAY(si->base->cache, istate->cache, istate->cache_nr);
        mark_base_index_entries(si->base);
        for (i = 0; i < si->base->cache_nr; i++)
                si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE;
@@ -141,8 +140,7 @@ void merge_base_index(struct index_state *istate)
        istate->cache       = NULL;
        istate->cache_alloc = 0;
        ALLOC_GROW(istate->cache, istate->cache_nr, istate->cache_alloc);
-       memcpy(istate->cache, si->base->cache,
-              sizeof(*istate->cache) * istate->cache_nr);
+       COPY_ARRAY(istate->cache, si->base->cache, istate->cache_nr);
 
        si->nr_deletions = 0;
        si->nr_replacements = 0;
index 3c48f049d3aa3150573f31a888cc741134d7669e..3f017a1c058fb94bc3220be09c384e53e565fb40 100644 (file)
@@ -337,17 +337,17 @@ static open_method_decl(loose)
        st->u.loose.mapped = map_sha1_file(sha1, &st->u.loose.mapsize);
        if (!st->u.loose.mapped)
                return -1;
-       if (unpack_sha1_header(&st->z,
-                              st->u.loose.mapped,
-                              st->u.loose.mapsize,
-                              st->u.loose.hdr,
-                              sizeof(st->u.loose.hdr)) < 0) {
+       if ((unpack_sha1_header(&st->z,
+                               st->u.loose.mapped,
+                               st->u.loose.mapsize,
+                               st->u.loose.hdr,
+                               sizeof(st->u.loose.hdr)) < 0) ||
+           (parse_sha1_header(st->u.loose.hdr, &st->size) < 0)) {
                git_inflate_end(&st->z);
                munmap(st->u.loose.mapped, st->u.loose.mapsize);
                return -1;
        }
 
-       parse_sha1_header(st->u.loose.hdr, &st->size);
        st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
        st->u.loose.hdr_avail = st->z.total_out;
        st->z_state = z_used;
index 8ffbbea4d65dbd8328563da53ef2959dd39ce796..b8fc588b1922760ade8502fda3dc465f6ebf2887 100755 (executable)
@@ -393,4 +393,21 @@ test_expect_success 'remote init from does not use config from cwd' '
        test_cmp expect actual
 '
 
+test_expect_success 're-init from a linked worktree' '
+       git init main-worktree &&
+       (
+               cd main-worktree &&
+               test_commit first &&
+               git worktree add ../linked-worktree &&
+               mv .git/info/exclude expected-exclude &&
+               cp .git/config expected-config &&
+               find .git/worktrees -print | sort >expected &&
+               git -C ../linked-worktree init &&
+               test_cmp expected-exclude .git/info/exclude &&
+               test_cmp expected-config .git/config &&
+               find .git/worktrees -print | sort >actual &&
+               test_cmp expected actual
+       )
+'
+
 test_done
index e76e84afbba58f94c600586388f8793b6b6eae84..2e47fe01cfaf565777c9359e7c2675ef051c33e3 100755 (executable)
@@ -41,6 +41,15 @@ test_expect_success 'check ambiguity' '
        test_must_fail git checkout world all
 '
 
+test_expect_success 'check ambiguity in subdir' '
+       mkdir sub &&
+       # not ambiguous because sub/world does not exist
+       git -C sub checkout world ../all &&
+       echo hello >sub/world &&
+       # ambiguous because sub/world does exist
+       test_must_fail git -C sub checkout world ../all
+'
+
 test_expect_success 'disambiguate checking out from a tree-ish' '
        echo bye > world &&
        git checkout world -- world &&
index 468a000e4bbb113d004ee8df8ef0db9b8260fdb5..3e5ac81bd29bf6aded0d0fa643dca448a281d481 100755 (executable)
@@ -174,6 +174,18 @@ test_expect_success 'checkout of branch with a file having the same name fails'
        test_branch master
 '
 
+test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
+       git checkout -B master &&
+       test_might_fail git branch -D spam &&
+
+       >spam &&
+       mkdir sub &&
+       mv spam sub/spam &&
+       test_must_fail git -C sub checkout spam &&
+       test_must_fail git rev-parse --verify refs/heads/spam &&
+       test_branch master
+'
+
 test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
        git checkout -B master &&
        test_might_fail git branch -D spam &&
index dfe02f48183ad77a93d72c0f5e952380b63a9229..32ac6e09bdc81acfb8de5cf887302794d20c8ece 100755 (executable)
@@ -80,4 +80,17 @@ test_expect_success '.lock files cleaned up' '
        )
 '
 
+test_expect_success '--chmod=+x and chmod=-x in the same argument list' '
+       >A &&
+       >B &&
+       git add A B &&
+       git update-index --chmod=+x A --chmod=-x B &&
+       cat >expect <<-\EOF &&
+       100755 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       A
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       B
+       EOF
+       git ls-files --stage A B >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 696743632729f6f7a244606adb4e1b0de9beeb7f..baef2d692431169d83e0bc3aed11d9030ea4a24a 100755 (executable)
@@ -225,7 +225,7 @@ test_expect_success 'cannot do merge w/conflicts when previous merge is unfinish
        test -d .git/NOTES_MERGE_WORKTREE &&
        test_must_fail git notes merge z >output 2>&1 &&
        # Output should indicate what is wrong
-       grep -q "\\.git/NOTES_MERGE_\\* exists" output
+       test_i18ngrep -q "\\.git/NOTES_MERGE_\\* exists" output
 '
 
 # Setup non-conflicting merge between x and new notes ref w
index 597e94e294d9dc949695867be295af333bf8f0fe..e38e2963880d42dc335870292f7a2aa4d583497c 100755 (executable)
@@ -1195,7 +1195,7 @@ To avoid this message, use "drop" to explicitly remove a commit.
 Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
 The possible behaviours are: ignore, warn, error.
 
-You can fix this with 'git rebase --edit-todo'.
+You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
 Or you can abort the rebase with 'git rebase --abort'.
 EOF
 
@@ -1219,7 +1219,7 @@ cat >expect <<EOF
 Warning: the command isn't recognized in the following line:
  - badcmd $(git rev-list --oneline -1 master~1)
 
-You can fix this with 'git rebase --edit-todo'.
+You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
 Or you can abort the rebase with 'git rebase --abort'.
 EOF
 
@@ -1254,7 +1254,7 @@ cat >expect <<EOF
 Warning: the SHA-1 is missing or isn't a commit in the following line:
  - edit XXXXXXX False commit
 
-You can fix this with 'git rebase --edit-todo'.
+You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
 Or you can abort the rebase with 'git rebase --abort'.
 EOF
 
index 2978cb9d640d1e826e8fb9b96e99bc69ee86f09d..924a26612681817156e54f4c1bc4a89e7cb5e8de 100755 (executable)
@@ -349,4 +349,52 @@ test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
        test_mode_in_index 100755 foo2
 '
 
+test_expect_success 'git add --chmod=[+-]x changes index with already added file' '
+       echo foo >foo3 &&
+       git add foo3 &&
+       git add --chmod=+x foo3 &&
+       test_mode_in_index 100755 foo3 &&
+       echo foo >xfoo3 &&
+       chmod 755 xfoo3 &&
+       git add xfoo3 &&
+       git add --chmod=-x xfoo3 &&
+       test_mode_in_index 100644 xfoo3
+'
+
+test_expect_success POSIXPERM 'git add --chmod=[+-]x does not change the working tree' '
+       echo foo >foo4 &&
+       git add foo4 &&
+       git add --chmod=+x foo4 &&
+       ! test -x foo4
+'
+
+test_expect_success 'no file status change if no pathspec is given' '
+       >foo5 &&
+       >foo6 &&
+       git add foo5 foo6 &&
+       git add --chmod=+x &&
+       test_mode_in_index 100644 foo5 &&
+       test_mode_in_index 100644 foo6
+'
+
+test_expect_success 'no file status change if no pathspec is given in subdir' '
+       mkdir -p sub &&
+       (
+               cd sub &&
+               >sub-foo1 &&
+               >sub-foo2 &&
+               git add . &&
+               git add --chmod=+x &&
+               test_mode_in_index 100644 sub-foo1 &&
+               test_mode_in_index 100644 sub-foo2
+       )
+'
+
+test_expect_success 'all statuses changed in folder if . is given' '
+       git add --chmod=+x . &&
+       test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
+       git add --chmod=-x . &&
+       test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
+'
+
 test_done
index 4bf1dbe9c9f3ffeb1151be07bf75dcd872167916..3b94283e35535ea4c594e3e746bd01a095eba4e7 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'UTF-8 invalid characters refused' '
        printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       grep "did not conform" "$HOME"/stderr
+       test_i18ngrep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 overlong sequences rejected' '
@@ -55,7 +55,7 @@ test_expect_success 'UTF-8 overlong sequences rejected' '
        printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       grep "did not conform" "$HOME"/stderr
+       test_i18ngrep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -64,7 +64,7 @@ test_expect_success 'UTF-8 non-characters refused' '
        printf "Commit message\n\nNon-character:\364\217\277\276\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       grep "did not conform" "$HOME"/stderr
+       test_i18ngrep "did not conform" "$HOME"/stderr
 '
 
 test_expect_success 'UTF-8 non-characters refused' '
@@ -73,7 +73,7 @@ test_expect_success 'UTF-8 non-characters refused' '
        printf "Commit message\n\nNon-character:\357\267\220\n" \
                >"$HOME/invalid" &&
        git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
-       grep "did not conform" "$HOME"/stderr
+       test_i18ngrep "did not conform" "$HOME"/stderr
 '
 
 for H in ISO8859-1 eucJP ISO-2022-JP
index 509084e1a70b4cd5ee7f56421bb9b8995b0a5d94..f663d567c8ac89e59236df79a632cbd2609910f5 100755 (executable)
@@ -295,7 +295,7 @@ test_expect_success 'am --no-utf8 (U/L)' '
 
        # commit-tree will warn that the commit message does not contain valid UTF-8
        # as mailinfo did not convert it
-       grep "did not conform" err &&
+       test_i18ngrep "did not conform" err &&
 
        check_encoding 2
 '
index 8d90a6e500362afe75fb724e07848e904753e7fb..ba4902df2b605f89ec2abcac50abefc2f23fc9bf 100755 (executable)
@@ -1086,6 +1086,15 @@ test_expect_success 'empty subject prefix does not have extra space' '
        test_cmp expect actual
 '
 
+test_expect_success '--rfc' '
+       cat >expect <<-\EOF &&
+       Subject: [RFC PATCH 1/1] header with . in it
+       EOF
+       git format-patch -n -1 --stdout --rfc >patch &&
+       grep ^Subject: patch >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '--from=ident notices bogus ident' '
        test_must_fail git format-patch -1 --stdout --from=foo >patch
 '
diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh
new file mode 100755 (executable)
index 0000000..5564506
--- /dev/null
@@ -0,0 +1,216 @@
+#!/bin/sh
+
+test_description='Test diff indent heuristic.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+# Compare two diff outputs. Ignore "index" lines, because we don't
+# care about SHA-1s or file modes.
+compare_diff () {
+       sed -e "/^index /d" <"$1" >.tmp-1
+       sed -e "/^index /d" <"$2" >.tmp-2
+       test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+# Compare blame output using the expectation for a diff as reference.
+# Only look for the lines coming from non-boundary commits.
+compare_blame () {
+       sed -n -e "1,4d" -e "s/^\+//p" <"$1" >.tmp-1
+       sed -ne "s/^[^^][^)]*) *//p" <"$2" >.tmp-2
+       test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+test_expect_success 'prepare' '
+       cat <<-\EOF >spaces.txt &&
+       1
+       2
+       a
+
+       b
+       3
+       4
+       EOF
+
+       cat <<-\EOF >functions.c &&
+       1
+       2
+       /* function */
+       foo() {
+           foo
+       }
+
+       3
+       4
+       EOF
+
+       git add spaces.txt functions.c &&
+       test_tick &&
+       git commit -m initial &&
+       git branch old &&
+
+       cat <<-\EOF >spaces.txt &&
+       1
+       2
+       a
+
+       b
+       a
+
+       b
+       3
+       4
+       EOF
+
+       cat <<-\EOF >functions.c &&
+       1
+       2
+       /* function */
+       bar() {
+           foo
+       }
+
+       /* function */
+       foo() {
+           foo
+       }
+
+       3
+       4
+       EOF
+
+       git add spaces.txt functions.c &&
+       test_tick &&
+       git commit -m initial &&
+       git branch new &&
+
+       tr "_" " " <<-\EOF >spaces-expect &&
+       diff --git a/spaces.txt b/spaces.txt
+       --- a/spaces.txt
+       +++ b/spaces.txt
+       @@ -3,5 +3,8 @@
+        a
+       _
+        b
+       +a
+       +
+       +b
+        3
+        4
+       EOF
+
+       tr "_" " " <<-\EOF >spaces-compacted-expect &&
+       diff --git a/spaces.txt b/spaces.txt
+       --- a/spaces.txt
+       +++ b/spaces.txt
+       @@ -2,6 +2,9 @@
+        2
+        a
+       _
+       +b
+       +a
+       +
+        b
+        3
+        4
+       EOF
+
+       tr "_" " " <<-\EOF >functions-expect &&
+       diff --git a/functions.c b/functions.c
+       --- a/functions.c
+       +++ b/functions.c
+       @@ -1,6 +1,11 @@
+        1
+        2
+        /* function */
+       +bar() {
+       +    foo
+       +}
+       +
+       +/* function */
+        foo() {
+            foo
+        }
+       EOF
+
+       tr "_" " " <<-\EOF >functions-compacted-expect
+       diff --git a/functions.c b/functions.c
+       --- a/functions.c
+       +++ b/functions.c
+       @@ -1,5 +1,10 @@
+        1
+        2
+       +/* function */
+       +bar() {
+       +    foo
+       +}
+       +
+        /* function */
+        foo() {
+            foo
+       EOF
+'
+
+test_expect_success 'diff: ugly spaces' '
+       git diff old new -- spaces.txt >out &&
+       compare_diff spaces-expect out
+'
+
+test_expect_success 'diff: nice spaces with --indent-heuristic' '
+       git diff --indent-heuristic old new -- spaces.txt >out-compacted &&
+       compare_diff spaces-compacted-expect out-compacted
+'
+
+test_expect_success 'diff: nice spaces with diff.indentHeuristic' '
+       git -c diff.indentHeuristic=true diff old new -- spaces.txt >out-compacted2 &&
+       compare_diff spaces-compacted-expect out-compacted2
+'
+
+test_expect_success 'diff: --no-indent-heuristic overrides config' '
+       git -c diff.indentHeuristic=true diff --no-indent-heuristic old new -- spaces.txt >out2 &&
+       compare_diff spaces-expect out2
+'
+
+test_expect_success 'diff: --indent-heuristic with --patience' '
+       git diff --indent-heuristic --patience old new -- spaces.txt >out-compacted3 &&
+       compare_diff spaces-compacted-expect out-compacted3
+'
+
+test_expect_success 'diff: --indent-heuristic with --histogram' '
+       git diff --indent-heuristic --histogram old new -- spaces.txt >out-compacted4 &&
+       compare_diff spaces-compacted-expect out-compacted4
+'
+
+test_expect_success 'diff: ugly functions' '
+       git diff old new -- functions.c >out &&
+       compare_diff functions-expect out
+'
+
+test_expect_success 'diff: nice functions with --indent-heuristic' '
+       git diff --indent-heuristic old new -- functions.c >out-compacted &&
+       compare_diff functions-compacted-expect out-compacted
+'
+
+test_expect_success 'blame: ugly spaces' '
+       git blame old..new -- spaces.txt >out-blame &&
+       compare_blame spaces-expect out-blame
+'
+
+test_expect_success 'blame: nice spaces with --indent-heuristic' '
+       git blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted &&
+       compare_blame spaces-compacted-expect out-blame-compacted
+'
+
+test_expect_success 'blame: nice spaces with diff.indentHeuristic' '
+       git -c diff.indentHeuristic=true blame old..new -- spaces.txt >out-blame-compacted2 &&
+       compare_blame spaces-compacted-expect out-blame-compacted2
+'
+
+test_expect_success 'blame: --no-indent-heuristic overrides config' '
+       git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame2 &&
+       git blame old..new -- spaces.txt >out-blame &&
+       compare_blame spaces-expect out-blame2
+'
+
+test_done
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
new file mode 100755 (executable)
index 0000000..f0bf50b
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Johannes Schindelin
+#
+
+test_description='Pickaxe options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit initial &&
+       printf "%04096d" 0 >4096-zeroes.txt &&
+       git add 4096-zeroes.txt &&
+       test_tick &&
+       git commit -m "A 4k file"
+'
+test_expect_success '-G matches' '
+       git diff --name-only -G "^0{4096}$" HEAD^ >out &&
+       test 4096-zeroes.txt = "$(cat out)"
+'
+
+test_done
index 9ce9424d1569a1d788d4ad1ecd59ed49ee712101..89a5bacac5ad069ec10b16bfdf3dbde7217cf4de 100755 (executable)
@@ -977,4 +977,27 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
        test_cmp msg out
 '
 
+test_expect_success 'am works with multi-line in-body headers' '
+       FORTY="String that has a length of more than forty characters" &&
+       LONG="$FORTY $FORTY" &&
+       rm -fr .git/rebase-apply &&
+       git checkout -f first &&
+       echo one >> file &&
+       git commit -am "$LONG" --author="$LONG <long@example.com>" &&
+       git format-patch --stdout -1 >patch &&
+       # bump from, date, and subject down to in-body header
+       perl -lpe "
+               if (/^From:/) {
+                       print \"From: x <x\@example.com>\";
+                       print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\";
+                       print \"Subject: x\n\";
+               }
+       " patch >msg &&
+       git checkout HEAD^ &&
+       git am msg &&
+       # Ensure that the author and full message are present
+       git cat-file commit HEAD | grep "^author.*long@example.com" &&
+       git cat-file commit HEAD | grep "^$LONG"
+'
+
 test_done
index 1a5a546230b9d971594af754fa5f11e5decd3cef..e6b995161e0fd15493bdaf4e5a5df2a3c2990798 100755 (executable)
@@ -7,37 +7,39 @@ test_description='git mailinfo and git mailsplit test'
 
 . ./test-lib.sh
 
+DATA="$TEST_DIRECTORY/t5100"
+
 test_expect_success 'split sample box' \
-       'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
+       'git mailsplit -o. "$DATA/sample.mbox" >last &&
        last=$(cat last) &&
        echo total is $last &&
-       test $(cat last) = 17'
+       test $(cat last) = 18'
 
 check_mailinfo () {
        mail=$1 opt=$2
        mo="$mail$opt"
-       git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
-       test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
-       test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
-       test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+       git mailinfo -u $opt "msg$mo" "patch$mo" <"$mail" >"info$mo" &&
+       test_cmp "$DATA/msg$mo" "msg$mo" &&
+       test_cmp "$DATA/patch$mo" "patch$mo" &&
+       test_cmp "$DATA/info$mo" "info$mo"
 }
 
 
 for mail in 00*
 do
        test_expect_success "mailinfo $mail" '
-               check_mailinfo $mail "" &&
-               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+               check_mailinfo "$mail" "" &&
+               if test -f "$DATA/msg$mail--scissors"
                then
-                       check_mailinfo $mail --scissors
+                       check_mailinfo "$mail" --scissors
                fi &&
-               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
+               if test -f "$DATA/msg$mail--no-inbody-headers"
                then
-                       check_mailinfo $mail --no-inbody-headers
+                       check_mailinfo "$mail" --no-inbody-headers
                fi &&
-               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--message-id
+               if test -f "$DATA/msg$mail--message-id"
                then
-                       check_mailinfo $mail --message-id
+                       check_mailinfo "$mail" --message-id
                fi
        '
 done
@@ -45,7 +47,7 @@ done
 
 test_expect_success 'split box with rfc2047 samples' \
        'mkdir rfc2047 &&
-       git mailsplit -orfc2047 "$TEST_DIRECTORY"/t5100/rfc2047-samples.mbox \
+       git mailsplit -orfc2047 "$DATA/rfc2047-samples.mbox" \
          >rfc2047/last &&
        last=$(cat rfc2047/last) &&
        echo total is $last &&
@@ -54,20 +56,20 @@ test_expect_success 'split box with rfc2047 samples' \
 for mail in rfc2047/00*
 do
        test_expect_success "mailinfo $mail" '
-               git mailinfo -u $mail-msg $mail-patch <$mail >$mail-info &&
+               git mailinfo -u "$mail-msg" "$mail-patch" <"$mail" >"$mail-info" &&
                echo msg &&
-               test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-msg &&
+               test_cmp "$DATA/empty" "$mail-msg" &&
                echo patch &&
-               test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-patch &&
+               test_cmp "$DATA/empty" "$mail-patch" &&
                echo info &&
-               test_cmp "$TEST_DIRECTORY"/t5100/rfc2047-info-$(basename $mail) $mail-info
+               test_cmp "$DATA/rfc2047-info-$(basename $mail)" "$mail-info"
        '
 done
 
 test_expect_success 'respect NULs' '
 
-       git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
-       test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
+       git mailsplit -d3 -o. "$DATA/nul-plain" &&
+       test_cmp "$DATA/nul-plain" 001 &&
        (cat 001 | git mailinfo msg patch) &&
        test_line_count = 4 patch
 
@@ -75,52 +77,52 @@ test_expect_success 'respect NULs' '
 
 test_expect_success 'Preserve NULs out of MIME encoded message' '
 
-       git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in &&
-       test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 &&
+       git mailsplit -d5 -o. "$DATA/nul-b64.in" &&
+       test_cmp "$DATA/nul-b64.in" 00001 &&
        git mailinfo msg patch <00001 &&
-       test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch
+       test_cmp "$DATA/nul-b64.expect" patch
 
 '
 
 test_expect_success 'mailinfo on from header without name works' '
 
        mkdir info-from &&
-       git mailsplit -oinfo-from "$TEST_DIRECTORY"/t5100/info-from.in &&
-       test_cmp "$TEST_DIRECTORY"/t5100/info-from.in info-from/0001 &&
+       git mailsplit -oinfo-from "$DATA/info-from.in" &&
+       test_cmp "$DATA/info-from.in" info-from/0001 &&
        git mailinfo info-from/msg info-from/patch \
          <info-from/0001 >info-from/out &&
-       test_cmp "$TEST_DIRECTORY"/t5100/info-from.expect info-from/out
+       test_cmp "$DATA/info-from.expect" info-from/out
 
 '
 
 test_expect_success 'mailinfo finds headers after embedded From line' '
        mkdir embed-from &&
-       git mailsplit -oembed-from "$TEST_DIRECTORY"/t5100/embed-from.in &&
-       test_cmp "$TEST_DIRECTORY"/t5100/embed-from.in embed-from/0001 &&
+       git mailsplit -oembed-from "$DATA/embed-from.in" &&
+       test_cmp "$DATA/embed-from.in" embed-from/0001 &&
        git mailinfo embed-from/msg embed-from/patch \
          <embed-from/0001 >embed-from/out &&
-       test_cmp "$TEST_DIRECTORY"/t5100/embed-from.expect embed-from/out
+       test_cmp "$DATA/embed-from.expect" embed-from/out
 '
 
 test_expect_success 'mailinfo on message with quoted >From' '
        mkdir quoted-from &&
-       git mailsplit -oquoted-from "$TEST_DIRECTORY"/t5100/quoted-from.in &&
-       test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.in quoted-from/0001 &&
+       git mailsplit -oquoted-from "$DATA/quoted-from.in" &&
+       test_cmp "$DATA/quoted-from.in" quoted-from/0001 &&
        git mailinfo quoted-from/msg quoted-from/patch \
          <quoted-from/0001 >quoted-from/out &&
-       test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.expect quoted-from/msg
+       test_cmp "$DATA/quoted-from.expect" quoted-from/msg
 '
 
 test_expect_success 'mailinfo unescapes with --mboxrd' '
        mkdir mboxrd &&
        git mailsplit -omboxrd --mboxrd \
-               "$TEST_DIRECTORY"/t5100/sample.mboxrd >last &&
+               "$DATA/sample.mboxrd" >last &&
        test x"$(cat last)" = x2 &&
        for i in 0001 0002
        do
                git mailinfo mboxrd/msg mboxrd/patch \
                  <mboxrd/$i >mboxrd/out &&
-               test_cmp "$TEST_DIRECTORY"/t5100/${i}mboxrd mboxrd/msg
+               test_cmp "$DATA/${i}mboxrd" mboxrd/msg
        done &&
        sp=" " &&
        echo "From " >expect &&
@@ -142,4 +144,18 @@ test_expect_success 'mailinfo unescapes with --mboxrd' '
        test_cmp expect mboxrd/msg
 '
 
+test_expect_success 'mailinfo handles rfc2822 quoted-string' '
+       mkdir quoted-string &&
+       git mailinfo /dev/null /dev/null <"$DATA/quoted-string.in" \
+               >quoted-string/info &&
+       test_cmp "$DATA/quoted-string.expect" quoted-string/info
+'
+
+test_expect_success 'mailinfo handles rfc2822 comment' '
+       mkdir comment &&
+       git mailinfo /dev/null /dev/null <"$DATA/comment.in" \
+               >comment/info &&
+       test_cmp "$DATA/comment.expect" comment/info
+'
+
 test_done
diff --git a/t/t5100/comment.expect b/t/t5100/comment.expect
new file mode 100644 (file)
index 0000000..7228177
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor (this is (really) a comment (honestly))
+Email: somebody@example.com
+Subject: testing comments
+Date: Sun, 25 May 2008 00:38:18 -0700
+
diff --git a/t/t5100/comment.in b/t/t5100/comment.in
new file mode 100644 (file)
index 0000000..c53a192
--- /dev/null
@@ -0,0 +1,9 @@
+From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] testing comments
+
+
+
+---
+patch
diff --git a/t/t5100/info0018 b/t/t5100/info0018
new file mode 100644 (file)
index 0000000..d53e749
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Another Thor
+Email: a.thor@example.com
+Subject: This one contains a tab and a space
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0018--no-inbody-headers b/t/t5100/info0018--no-inbody-headers
new file mode 100644 (file)
index 0000000..30b17bd
--- /dev/null
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check multiline inbody headers
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
index 4abb3d5c6c4884806cdaabcc0b01acb9c1263af2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,2 +0,0 @@
-  - a list
-  - of stuff
diff --git a/t/t5100/msg0018 b/t/t5100/msg0018
new file mode 100644 (file)
index 0000000..56de83d
--- /dev/null
@@ -0,0 +1,2 @@
+a commit message
+
diff --git a/t/t5100/msg0018--no-inbody-headers b/t/t5100/msg0018--no-inbody-headers
new file mode 100644 (file)
index 0000000..b1e05d3
--- /dev/null
@@ -0,0 +1,8 @@
+From: Another Thor
+ <a.thor@example.com>
+Subject: This one contains
+       a tab
+ and a space
+
+a commit message
+
diff --git a/t/t5100/patch0018 b/t/t5100/patch0018
new file mode 100644 (file)
index 0000000..789df6d
--- /dev/null
@@ -0,0 +1,6 @@
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
diff --git a/t/t5100/patch0018--no-inbody-headers b/t/t5100/patch0018--no-inbody-headers
new file mode 100644 (file)
index 0000000..789df6d
--- /dev/null
@@ -0,0 +1,6 @@
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
diff --git a/t/t5100/quoted-string.expect b/t/t5100/quoted-string.expect
new file mode 100644 (file)
index 0000000..cab1bce
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Author "The Author" Name
+Email: somebody@example.com
+Subject: testing quoted-pair
+Date: Sun, 25 May 2008 00:38:18 -0700
+
diff --git a/t/t5100/quoted-string.in b/t/t5100/quoted-string.in
new file mode 100644 (file)
index 0000000..e2e627a
--- /dev/null
@@ -0,0 +1,9 @@
+From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+From: "Author \"The Author\" Name" <somebody@example.com>
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] testing quoted-pair
+
+
+
+---
+patch
index 8b2ae064c36b969ca20324154cea66bf26739de6..6d4d0e44742ed3a162b1f9c813b0ad41b6f7dc7a 100644 (file)
@@ -699,3 +699,22 @@ index e69de29..d95f3ad 100644
 +++ b/foo
 @@ -0,0 +1 @@
 +New content
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check multiline inbody headers
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+From: Another Thor
+ <a.thor@example.com>
+Subject: This one contains
+       a tab
+ and a space
+
+a commit message
+
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
index befdfeefc603e42ad1ce69599fd1b1142917a8d7..55fc83fc0624fa9bf1a024c4bc4aede25999fae0 100755 (executable)
@@ -99,7 +99,7 @@ test_expect_success 'confuses pattern as remote when no remote specified' '
        # We could just as easily have used "master"; the "*" emphasizes its
        # role as a pattern.
        test_must_fail git ls-remote refs*master >actual 2>&1 &&
-       test_cmp exp actual
+       test_i18ncmp exp actual
 '
 
 test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
index a1dcdb81d789cfa2d0e8fcc178311a807a608771..f6020cd2aa9b726f11ef0b93ecb2ce8227a1017c 100755 (executable)
@@ -225,7 +225,7 @@ test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
 
 test_expect_success '%C(auto) respects --color' '
        git log --color --format="%C(auto)%H" -1 >actual &&
-       printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
+       printf "\\033[m\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
        test_cmp expect actual
 '
 
index e94b2f147a72b216a1c97d278bb9c822aed7392a..6d06ed96cbc37ee0d19173a4c5379e021c1e5414 100755 (executable)
@@ -709,6 +709,14 @@ test_expect_success HIGHLIGHT \
         git commit -m "Add test.sh" &&
         gitweb_run "p=.git;a=blob;f=test.sh"'
 
+test_expect_success HIGHLIGHT \
+       'syntax highlighting (highlighter language autodetection)' \
+       'git config gitweb.highlight yes &&
+        echo "#!/usr/bin/perl" > test &&
+        git add test &&
+        git commit -m "Add test" &&
+        gitweb_run "p=.git;a=blob;f=test"'
+
 # ----------------------------------------------------------------------
 # forks of projects
 
index 3db3f02577a432ddec187873397e63e4c9f5af3e..ea6bdd20e0491554e88f4b84bd3b6354867021bc 100644 (file)
@@ -1094,12 +1094,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        int i, ret;
        static struct cache_entry *dfc;
        struct exclude_list el;
-       struct checkout state;
+       struct checkout state = CHECKOUT_INIT;
 
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
-       memset(&state, 0, sizeof(state));
-       state.base_dir = "";
        state.force = 1;
        state.quiet = 1;
        state.refresh_cache = 1;
index 9a14658e7e0509ef815d62ddec9518819c2767b3..9628c1d5d75ca3ff4d3bc6ccc66152f371d9483e 100644 (file)
@@ -367,11 +367,11 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
                if (d->new_submodule_commits || d->dirty_submodule) {
                        strbuf_addstr(&extra, " (");
                        if (d->new_submodule_commits)
-                               strbuf_addf(&extra, _("new commits, "));
+                               strbuf_addstr(&extra, _("new commits, "));
                        if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-                               strbuf_addf(&extra, _("modified content, "));
+                               strbuf_addstr(&extra, _("modified content, "));
                        if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-                               strbuf_addf(&extra, _("untracked content, "));
+                               strbuf_addstr(&extra, _("untracked content, "));
                        strbuf_setlen(&extra, extra.len - 2);
                        strbuf_addch(&extra, ')');
                }
index 3bfc69cade6e543f69164ed6d989473de40b7b3c..060038c2d6b92512e6625d0f5d94ed5439cd249c 100644 (file)
@@ -214,11 +214,10 @@ struct ff_regs {
 static long ff_regexp(const char *line, long len,
                char *buffer, long buffer_size, void *priv)
 {
-       char *line_buffer;
        struct ff_regs *regs = priv;
        regmatch_t pmatch[2];
        int i;
-       int result = -1;
+       int result;
 
        /* Exclude terminating newline (and cr) from matching */
        if (len > 0 && line[len-1] == '\n') {
@@ -228,18 +227,16 @@ static long ff_regexp(const char *line, long len,
                        len--;
        }
 
-       line_buffer = xstrndup(line, len); /* make NUL terminated */
-
        for (i = 0; i < regs->nr; i++) {
                struct ff_reg *reg = regs->array + i;
-               if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
+               if (!regexec_buf(&reg->re, line, len, 2, pmatch, 0)) {
                        if (reg->negate)
-                               goto fail;
+                               return -1;
                        break;
                }
        }
        if (regs->nr <= i)
-               goto fail;
+               return -1;
        i = pmatch[1].rm_so >= 0 ? 1 : 0;
        line += pmatch[i].rm_so;
        result = pmatch[i].rm_eo - pmatch[i].rm_so;
@@ -248,8 +245,6 @@ static long ff_regexp(const char *line, long len,
        while (result > 0 && (isspace(line[result - 1])))
                result--;
        memcpy(buffer, line, result);
- fail:
-       free(line_buffer);
        return result;
 }
 
index 7423f77fc8b2682c121e8d4adb855337e107b29e..8db16d4ae6def5657bc51fa2b9b354a7c0267201 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 #define XDF_IGNORE_BLANK_LINES (1 << 7)
 
 #define XDF_COMPACTION_HEURISTIC (1 << 8)
+#define XDF_INDENT_HEURISTIC (1 << 9)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
index b3c684887502c3c473a614114e4acdfbc57ea094..760fbb6db7583f6d0ec5eed9b9c63ea6a0e7abe7 100644 (file)
@@ -400,138 +400,577 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
-static int is_blank_line(xrecord_t **recs, long ix, long flags)
+static int is_blank_line(xrecord_t *rec, long flags)
 {
-       return xdl_blankline(recs[ix]->ptr, recs[ix]->size, flags);
+       return xdl_blankline(rec->ptr, rec->size, flags);
 }
 
-static int recs_match(xrecord_t **recs, long ixs, long ix, long flags)
+static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags)
 {
-       return (recs[ixs]->ha == recs[ix]->ha &&
-               xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size,
-                            recs[ix]->ptr, recs[ix]->size,
+       return (rec1->ha == rec2->ha &&
+               xdl_recmatch(rec1->ptr, rec1->size,
+                            rec2->ptr, rec2->size,
                             flags));
 }
 
-int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
-       long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
-       char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
-       unsigned int blank_lines;
-       xrecord_t **recs = xdf->recs;
+/*
+ * If a line is indented more than this, get_indent() just returns this value.
+ * This avoids having to do absurd amounts of work for data that are not
+ * human-readable text, and also ensures that the output of get_indent fits within
+ * an int.
+ */
+#define MAX_INDENT 200
 
+/*
+ * Return the amount of indentation of the specified line, treating TAB as 8
+ * columns. Return -1 if line is empty or contains only whitespace. Clamp the
+ * output value at MAX_INDENT.
+ */
+static int get_indent(xrecord_t *rec)
+{
+       long i;
+       int ret = 0;
+
+       for (i = 0; i < rec->size; i++) {
+               char c = rec->ptr[i];
+
+               if (!XDL_ISSPACE(c))
+                       return ret;
+               else if (c == ' ')
+                       ret += 1;
+               else if (c == '\t')
+                       ret += 8 - ret % 8;
+               /* ignore other whitespace characters */
+
+               if (ret >= MAX_INDENT)
+                       return MAX_INDENT;
+       }
+
+       /* The line contains only whitespace. */
+       return -1;
+}
+
+/*
+ * If more than this number of consecutive blank rows are found, just return this
+ * value. This avoids requiring O(N^2) work for pathological cases, and also
+ * ensures that the output of score_split fits in an int.
+ */
+#define MAX_BLANKS 20
+
+/* Characteristics measured about a hypothetical split position. */
+struct split_measurement {
        /*
-        * This is the same of what GNU diff does. Move back and forward
-        * change groups for a consistent and pretty diff output. This also
-        * helps in finding joinable change groups and reduce the diff size.
+        * Is the split at the end of the file (aside from any blank lines)?
         */
-       for (ix = ixo = 0;;) {
-               /*
-                * Find the first changed line in the to-be-compacted file.
-                * We need to keep track of both indexes, so if we find a
-                * changed lines group on the other file, while scanning the
-                * to-be-compacted file, we need to skip it properly. Note
-                * that loops that are testing for changed lines on rchg* do
-                * not need index bounding since the array is prepared with
-                * a zero at position -1 and N.
-                */
-               for (; ix < nrec && !rchg[ix]; ix++)
-                       while (rchgo[ixo++]);
-               if (ix == nrec)
+       int end_of_file;
+
+       /*
+        * How much is the line immediately following the split indented (or -1 if
+        * the line is blank):
+        */
+       int indent;
+
+       /*
+        * How many consecutive lines above the split are blank?
+        */
+       int pre_blank;
+
+       /*
+        * How much is the nearest non-blank line above the split indented (or -1
+        * if there is no such line)?
+        */
+       int pre_indent;
+
+       /*
+        * How many lines after the line following the split are blank?
+        */
+       int post_blank;
+
+       /*
+        * How much is the nearest non-blank line after the line following the
+        * split indented (or -1 if there is no such line)?
+        */
+       int post_indent;
+};
+
+struct split_score {
+       /* The effective indent of this split (smaller is preferred). */
+       int effective_indent;
+
+       /* Penalty for this split (smaller is preferred). */
+       int penalty;
+};
+
+/*
+ * Fill m with information about a hypothetical split of xdf above line split.
+ */
+static void measure_split(const xdfile_t *xdf, long split,
+                         struct split_measurement *m)
+{
+       long i;
+
+       if (split >= xdf->nrec) {
+               m->end_of_file = 1;
+               m->indent = -1;
+       } else {
+               m->end_of_file = 0;
+               m->indent = get_indent(xdf->recs[split]);
+       }
+
+       m->pre_blank = 0;
+       m->pre_indent = -1;
+       for (i = split - 1; i >= 0; i--) {
+               m->pre_indent = get_indent(xdf->recs[i]);
+               if (m->pre_indent != -1)
+                       break;
+               m->pre_blank += 1;
+               if (m->pre_blank == MAX_BLANKS) {
+                       m->pre_indent = 0;
+                       break;
+               }
+       }
+
+       m->post_blank = 0;
+       m->post_indent = -1;
+       for (i = split + 1; i < xdf->nrec; i++) {
+               m->post_indent = get_indent(xdf->recs[i]);
+               if (m->post_indent != -1)
                        break;
+               m->post_blank += 1;
+               if (m->post_blank == MAX_BLANKS) {
+                       m->post_indent = 0;
+                       break;
+               }
+       }
+}
+
+/*
+ * The empirically-determined weight factors used by score_split() below.
+ * Larger values means that the position is a less favorable place to split.
+ *
+ * Note that scores are only ever compared against each other, so multiplying
+ * all of these weight/penalty values by the same factor wouldn't change the
+ * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*.
+ * In practice, these numbers are chosen to be large enough that they can be
+ * adjusted relative to each other with sufficient precision despite using
+ * integer math.
+ */
+
+/* Penalty if there are no non-blank lines before the split */
+#define START_OF_FILE_PENALTY 1
+
+/* Penalty if there are no non-blank lines after the split */
+#define END_OF_FILE_PENALTY 21
 
+/* Multiplier for the number of blank lines around the split */
+#define TOTAL_BLANK_WEIGHT (-30)
+
+/* Multiplier for the number of blank lines after the split */
+#define POST_BLANK_WEIGHT 6
+
+/*
+ * Penalties applied if the line is indented more than its predecessor
+ */
+#define RELATIVE_INDENT_PENALTY (-4)
+#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10
+
+/*
+ * Penalties applied if the line is indented less than both its predecessor and
+ * its successor
+ */
+#define RELATIVE_OUTDENT_PENALTY 24
+#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * Penalties applied if the line is indented less than its predecessor but not
+ * less than its successor
+ */
+#define RELATIVE_DEDENT_PENALTY 23
+#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * We only consider whether the sum of the effective indents for splits are
+ * less than (-1), equal to (0), or greater than (+1) each other. The resulting
+ * value is multiplied by the following weight and combined with the penalty to
+ * determine the better of two scores.
+ */
+#define INDENT_WEIGHT 60
+
+/*
+ * Compute a badness score for the hypothetical split whose measurements are
+ * stored in m. The weight factors were determined empirically using the tools and
+ * corpus described in
+ *
+ *     https://github.com/mhagger/diff-slider-tools
+ *
+ * Also see that project if you want to improve the weights based on, for example,
+ * a larger or more diverse corpus.
+ */
+static void score_add_split(const struct split_measurement *m, struct split_score *s)
+{
+       /*
+        * A place to accumulate penalty factors (positive makes this index more
+        * favored):
+        */
+       int post_blank, total_blank, indent, any_blanks;
+
+       if (m->pre_indent == -1 && m->pre_blank == 0)
+               s->penalty += START_OF_FILE_PENALTY;
+
+       if (m->end_of_file)
+               s->penalty += END_OF_FILE_PENALTY;
+
+       /*
+        * Set post_blank to the number of blank lines following the split,
+        * including the line immediately after the split:
+        */
+       post_blank = (m->indent == -1) ? 1 + m->post_blank : 0;
+       total_blank = m->pre_blank + post_blank;
+
+       /* Penalties based on nearby blank lines: */
+       s->penalty += TOTAL_BLANK_WEIGHT * total_blank;
+       s->penalty += POST_BLANK_WEIGHT * post_blank;
+
+       if (m->indent != -1)
+               indent = m->indent;
+       else
+               indent = m->post_indent;
+
+       any_blanks = (total_blank != 0);
+
+       /* Note that the effective indent is -1 at the end of the file: */
+       s->effective_indent += indent;
+
+       if (indent == -1) {
+               /* No additional adjustments needed. */
+       } else if (m->pre_indent == -1) {
+               /* No additional adjustments needed. */
+       } else if (indent > m->pre_indent) {
+               /*
+                * The line is indented more than its predecessor.
+                */
+               s->penalty += any_blanks ?
+                       RELATIVE_INDENT_WITH_BLANK_PENALTY :
+                       RELATIVE_INDENT_PENALTY;
+       } else if (indent == m->pre_indent) {
+               /*
+                * The line has the same indentation level as its predecessor.
+                * No additional adjustments needed.
+                */
+       } else {
                /*
-                * Record the start of a changed-group in the to-be-compacted file
-                * and find the end of it, on both to-be-compacted and other file
-                * indexes (ix and ixo).
+                * The line is indented less than its predecessor. It could be
+                * the block terminator of the previous block, but it could
+                * also be the start of a new block (e.g., an "else" block, or
+                * maybe the previous block didn't have a block terminator).
+                * Try to distinguish those cases based on what comes next:
                 */
-               ixs = ix;
-               for (ix++; rchg[ix]; ix++);
-               for (; rchgo[ixo]; ixo++);
+               if (m->post_indent != -1 && m->post_indent > indent) {
+                       /*
+                        * The following line is indented more. So it is likely
+                        * that this line is the start of a block.
+                        */
+                       s->penalty += any_blanks ?
+                               RELATIVE_OUTDENT_WITH_BLANK_PENALTY :
+                               RELATIVE_OUTDENT_PENALTY;
+               } else {
+                       /*
+                        * That was probably the end of a block.
+                        */
+                       s->penalty += any_blanks ?
+                               RELATIVE_DEDENT_WITH_BLANK_PENALTY :
+                               RELATIVE_DEDENT_PENALTY;
+               }
+       }
+}
+
+static int score_cmp(struct split_score *s1, struct split_score *s2)
+{
+       /* -1 if s1.effective_indent < s2->effective_indent, etc. */
+       int cmp_indents = ((s1->effective_indent > s2->effective_indent) -
+                          (s1->effective_indent < s2->effective_indent));
+
+       return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty);
+}
+
+/*
+ * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group
+ * of lines that was inserted or deleted from the corresponding version of the
+ * file). We consider there to be such a group at the beginning of the file, at
+ * the end of the file, and between any two unchanged lines, though most such
+ * groups will usually be empty.
+ *
+ * If the first line in a group is equal to the line following the group, then
+ * the group can be slid down. Similarly, if the last line in a group is equal
+ * to the line preceding the group, then the group can be slid up. See
+ * group_slide_down() and group_slide_up().
+ *
+ * Note that loops that are testing for changed lines in xdf->rchg do not need
+ * index bounding since the array is prepared with a zero at position -1 and N.
+ */
+struct xdlgroup {
+       /*
+        * The index of the first changed line in the group, or the index of
+        * the unchanged line above which the (empty) group is located.
+        */
+       long start;
+
+       /*
+        * The index of the first unchanged line after the group. For an empty
+        * group, end is equal to start.
+        */
+       long end;
+};
+
+/*
+ * Initialize g to point at the first group in xdf.
+ */
+static void group_init(xdfile_t *xdf, struct xdlgroup *g)
+{
+       g->start = g->end = 0;
+       while (xdf->rchg[g->end])
+               g->end++;
+}
+
+/*
+ * Move g to describe the next (possibly empty) group in xdf and return 0. If g
+ * is already at the end of the file, do nothing and return -1.
+ */
+static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)
+{
+       if (g->end == xdf->nrec)
+               return -1;
 
+       g->start = g->end + 1;
+       for (g->end = g->start; xdf->rchg[g->end]; g->end++)
+               ;
+
+       return 0;
+}
+
+/*
+ * Move g to describe the previous (possibly empty) group in xdf and return 0.
+ * If g is already at the beginning of the file, do nothing and return -1.
+ */
+static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)
+{
+       if (g->start == 0)
+               return -1;
+
+       g->end = g->start - 1;
+       for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)
+               ;
+
+       return 0;
+}
+
+/*
+ * If g can be slid toward the end of the file, do so, and if it bumps into a
+ * following group, expand this group to include it. Return 0 on success or -1
+ * if g cannot be slid down.
+ */
+static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags)
+{
+       if (g->end < xdf->nrec &&
+           recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) {
+               xdf->rchg[g->start++] = 0;
+               xdf->rchg[g->end++] = 1;
+
+               while (xdf->rchg[g->end])
+                       g->end++;
+
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+/*
+ * If g can be slid toward the beginning of the file, do so, and if it bumps
+ * into a previous group, expand this group to include it. Return 0 on success
+ * or -1 if g cannot be slid up.
+ */
+static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags)
+{
+       if (g->start > 0 &&
+           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) {
+               xdf->rchg[--g->start] = 1;
+               xdf->rchg[--g->end] = 0;
+
+               while (xdf->rchg[g->start - 1])
+                       g->start--;
+
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+static void xdl_bug(const char *msg)
+{
+       fprintf(stderr, "BUG: %s\n", msg);
+       exit(1);
+}
+
+/*
+ * Move back and forward change groups for a consistent and pretty diff output.
+ * This also helps in finding joinable change groups and reducing the diff
+ * size.
+ */
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+       struct xdlgroup g, go;
+       long earliest_end, end_matching_other;
+       long groupsize;
+       unsigned int blank_lines;
+
+       group_init(xdf, &g);
+       group_init(xdfo, &go);
+
+       while (1) {
+               /* If the group is empty in the to-be-compacted file, skip it: */
+               if (g.end == g.start)
+                       goto next;
+
+               /*
+                * Now shift the change up and then down as far as possible in
+                * each direction. If it bumps into any other changes, merge them.
+                */
                do {
-                       grpsiz = ix - ixs;
-                       blank_lines = 0;
+                       groupsize = g.end - g.start;
 
                        /*
-                        * If the line before the current change group, is equal to
-                        * the last line of the current change group, shift backward
-                        * the group.
+                        * Keep track of the last "end" index that causes this
+                        * group to align with a group of changed lines in the
+                        * other file. -1 indicates that we haven't found such
+                        * a match yet:
                         */
-                       while (ixs > 0 && recs_match(recs, ixs - 1, ix - 1, flags)) {
-                               rchg[--ixs] = 1;
-                               rchg[--ix] = 0;
-
-                               /*
-                                * This change might have joined two change groups,
-                                * so we try to take this scenario in account by moving
-                                * the start index accordingly (and so the other-file
-                                * end-of-group index).
-                                */
-                               for (; rchg[ixs - 1]; ixs--);
-                               while (rchgo[--ixo]);
-                       }
+                       end_matching_other = -1;
 
                        /*
-                        * Record the end-of-group position in case we are matched
-                        * with a group of changes in the other file (that is, the
-                        * change record before the end-of-group index in the other
-                        * file is set).
+                        * Boolean value that records whether there are any blank
+                        * lines that could be made to be the last line of this
+                        * group.
                         */
-                       ixref = rchgo[ixo - 1] ? ix: nrec;
+                       blank_lines = 0;
+
+                       /* Shift the group backward as much as possible: */
+                       while (!group_slide_up(xdf, &g, flags))
+                               if (group_previous(xdfo, &go))
+                                       xdl_bug("group sync broken sliding up");
 
                        /*
-                        * If the first line of the current change group, is equal to
-                        * the line next of the current change group, shift forward
-                        * the group.
+                        * This is this highest that this group can be shifted.
+                        * Record its end index:
                         */
-                       while (ix < nrec && recs_match(recs, ixs, ix, flags)) {
-                               blank_lines += is_blank_line(recs, ix, flags);
-
-                               rchg[ixs++] = 0;
-                               rchg[ix++] = 1;
-
-                               /*
-                                * This change might have joined two change groups,
-                                * so we try to take this scenario in account by moving
-                                * the start index accordingly (and so the other-file
-                                * end-of-group index). Keep tracking the reference
-                                * index in case we are shifting together with a
-                                * corresponding group of changes in the other file.
-                                */
-                               for (; rchg[ix]; ix++);
-                               while (rchgo[++ixo])
-                                       ixref = ix;
-                       }
-               } while (grpsiz != ix - ixs);
+                       earliest_end = g.end;
 
-               /*
-                * Try to move back the possibly merged group of changes, to match
-                * the recorded position in the other file.
-                */
-               while (ixref < ix) {
-                       rchg[--ixs] = 1;
-                       rchg[--ix] = 0;
-                       while (rchgo[--ixo]);
-               }
+                       if (go.end > go.start)
+                               end_matching_other = g.end;
+
+                       /* Now shift the group forward as far as possible: */
+                       while (1) {
+                               if (!blank_lines)
+                                       blank_lines = is_blank_line(
+                                                       xdf->recs[g.end - 1],
+                                                       flags);
+
+                               if (group_slide_down(xdf, &g, flags))
+                                       break;
+                               if (group_next(xdfo, &go))
+                                       xdl_bug("group sync broken sliding down");
+
+                               if (go.end > go.start)
+                                       end_matching_other = g.end;
+                       }
+               } while (groupsize != g.end - g.start);
 
                /*
-                * If a group can be moved back and forth, see if there is a
-                * blank line in the moving space. If there is a blank line,
-                * make sure the last blank line is the end of the group.
+                * If the group can be shifted, then we can possibly use this
+                * freedom to produce a more intuitive diff.
                 *
-                * As we already shifted the group forward as far as possible
-                * in the earlier loop, we need to shift it back only if at all.
+                * The group is currently shifted as far down as possible, so the
+                * heuristics below only have to handle upwards shifts.
                 */
-               if ((flags & XDF_COMPACTION_HEURISTIC) && blank_lines) {
-                       while (ixs > 0 &&
-                              !is_blank_line(recs, ix - 1, flags) &&
-                              recs_match(recs, ixs - 1, ix - 1, flags)) {
-                               rchg[--ixs] = 1;
-                               rchg[--ix] = 0;
+
+               if (g.end == earliest_end) {
+                       /* no shifting was possible */
+               } else if (end_matching_other != -1) {
+                       /*
+                        * Move the possibly merged group of changes back to line
+                        * up with the last group of changes from the other file
+                        * that it can align with.
+                        */
+                       while (go.end == go.start) {
+                               if (group_slide_up(xdf, &g, flags))
+                                       xdl_bug("match disappeared");
+                               if (group_previous(xdfo, &go))
+                                       xdl_bug("group sync broken sliding to match");
+                       }
+               } else if ((flags & XDF_COMPACTION_HEURISTIC) && blank_lines) {
+                       /*
+                        * Compaction heuristic: if it is possible to shift the
+                        * group to make its bottom line a blank line, do so.
+                        *
+                        * As we already shifted the group forward as far as
+                        * possible in the earlier loop, we only need to handle
+                        * backward shifts, not forward ones.
+                        */
+                       while (!is_blank_line(xdf->recs[g.end - 1], flags)) {
+                               if (group_slide_up(xdf, &g, flags))
+                                       xdl_bug("blank line disappeared");
+                               if (group_previous(xdfo, &go))
+                                       xdl_bug("group sync broken sliding to blank line");
+                       }
+               } else if (flags & XDF_INDENT_HEURISTIC) {
+                       /*
+                        * Indent heuristic: a group of pure add/delete lines
+                        * implies two splits, one between the end of the "before"
+                        * context and the start of the group, and another between
+                        * the end of the group and the beginning of the "after"
+                        * context. Some splits are aesthetically better and some
+                        * are worse. We compute a badness "score" for each split,
+                        * and add the scores for the two splits to define a
+                        * "score" for each position that the group can be shifted
+                        * to. Then we pick the shift with the lowest score.
+                        */
+                       long shift, best_shift = -1;
+                       struct split_score best_score;
+
+                       for (shift = earliest_end; shift <= g.end; shift++) {
+                               struct split_measurement m;
+                               struct split_score score = {0, 0};
+
+                               measure_split(xdf, shift, &m);
+                               score_add_split(&m, &score);
+                               measure_split(xdf, shift - groupsize, &m);
+                               score_add_split(&m, &score);
+                               if (best_shift == -1 ||
+                                   score_cmp(&score, &best_score) <= 0) {
+                                       best_score.effective_indent = score.effective_indent;
+                                       best_score.penalty = score.penalty;
+                                       best_shift = shift;
+                               }
+                       }
+
+                       while (g.end > best_shift) {
+                               if (group_slide_up(xdf, &g, flags))
+                                       xdl_bug("best shift unreached");
+                               if (group_previous(xdfo, &go))
+                                       xdl_bug("group sync broken sliding to blank line");
                        }
                }
+
+       next:
+               /* Move past the just-processed group: */
+               if (group_next(xdf, &g))
+                       break;
+               if (group_next(xdfo, &go))
+                       xdl_bug("group sync broken moving to next group");
        }
 
+       if (!group_next(xdfo, &go))
+               xdl_bug("group sync broken at end of file");
+
        return 0;
 }