Merge branch 'fc/fast-export'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:42:31 +0000 (11:42 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:42:31 +0000 (11:42 -0700)
Code simpification.

* fc/fast-export:
fast-export: refactor get_tags_and_duplicates()
fast-export: make extra_refs global

254 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/1.8.5.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/blame-options.txt
Documentation/config.txt
Documentation/git-blame.txt
Documentation/git-cat-file.txt
Documentation/git-check-attr.txt
Documentation/git-config.txt
Documentation/git-fast-import.txt
Documentation/git-fetch-pack.txt
Documentation/git-format-patch.txt
Documentation/git-gc.txt
Documentation/git-log.txt
Documentation/git-merge-file.txt
Documentation/git-merge.txt
Documentation/git-mv.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-rev-parse.txt
Documentation/git-rm.txt
Documentation/git-whatchanged.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/gitremote-helpers.txt
Documentation/glossary-content.txt
Documentation/line-range-format.txt
Documentation/technical/api-setup.txt
Documentation/technical/http-protocol.txt [new file with mode: 0644]
Documentation/technical/pack-heuristics.txt
GIT-VERSION-GEN
Makefile
RelNotes
archive.c
archive.h
builtin/add.c
builtin/apply.c
builtin/bisect--helper.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/check-attr.c
builtin/check-ignore.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/index-pack.c
builtin/log.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/merge-base.c
builtin/merge-file.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/push.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/rerere.c
builtin/reset.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/tag.c
builtin/tar-tree.c
builtin/update-index.c
builtin/update-ref.c
bulk-checkin.c
cache.h
combine-diff.c
commit.c
commit.h
compat/apple-common-crypto.h [new file with mode: 0644]
compat/clipped-write.c [deleted file]
compat/mingw.c
compat/precompose_utf8.c
config.c
config.mak.uname
connect.c
connect.h [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/contacts/git-contacts
contrib/examples/git-log.sh [new file with mode: 0755]
contrib/examples/git-whatchanged.sh [new file with mode: 0755]
contrib/hooks/post-receive-email
contrib/mw-to-git/Makefile
contrib/mw-to-git/git-remote-mediawiki.perl
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
dir.c
dir.h
editor.c
fast-import.c
fetch-pack.c
fetch-pack.h
git-compat-util.h
git-filter-branch.sh
git-p4.py
git-pull.sh
git-rebase--interactive.sh
git-remote-testgit.sh
git-sh-setup.sh
git.c
gitweb/gitweb.perl
gitweb/static/gitweb.css
http-push.c
http.c
imap-send.c
line-log.c
line-log.h
line-range.c
line-range.h
list-objects.c
log-tree.c
mailmap.c
merge-recursive.c
notes-merge.c
notes-utils.h
parse-options.c
parse-options.h
path.c
pathspec.c
pathspec.h
perl/Git.pm
perl/Git/SVN/Fetcher.pm
po/TEAMS
po/de.po
po/fr.po
preload-index.c
quote.c
quote.h
reachable.c
read-cache.c
refs.c
remote-curl.c
remote.c
remote.h
rerere.c
rerere.h
resolve-undo.c
resolve-undo.h
revision.c
revision.h
send-pack.c
setup.c
sha1_file.c
submodule.c
submodule.h
t/.gitattributes
t/annotate-tests.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/t0008-ignores.sh
t/t0021-conversion.sh
t/t0050-filesystem.sh
t/t0070-fundamental.sh
t/t0110-urlmatch-normalization.sh [new file with mode: 0755]
t/t0110/README [new file with mode: 0644]
t/t0110/url-1 [new file with mode: 0644]
t/t0110/url-10 [new file with mode: 0644]
t/t0110/url-11 [new file with mode: 0644]
t/t0110/url-2 [new file with mode: 0644]
t/t0110/url-3 [new file with mode: 0644]
t/t0110/url-4 [new file with mode: 0644]
t/t0110/url-5 [new file with mode: 0644]
t/t0110/url-6 [new file with mode: 0644]
t/t0110/url-7 [new file with mode: 0644]
t/t0110/url-8 [new file with mode: 0644]
t/t0110/url-9 [new file with mode: 0644]
t/t1006-cat-file.sh
t/t1300-repo-config.sh
t/t1411-reflog-show.sh
t/t3010-ls-files-killed-modified.sh
t/t3404-rebase-interactive.sh
t/t3409-rebase-preserve-merges.sh
t/t3501-revert-cherry-pick.sh
t/t3506-cherry-pick-ff.sh
t/t3509-cherry-pick-merge-df.sh
t/t3600-rm.sh
t/t3701-add-interactive.sh
t/t3910-mac-os-precompose.sh
t/t4055-diff-context.sh
t/t4203-mailmap.sh
t/t4211-line-log.sh
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5533-push-cas.sh [new file with mode: 0755]
t/t5541-http-push.sh
t/t5551-http-fetch.sh
t/t5801-remote-helpers.sh
t/t5802-connect-helper.sh [new file with mode: 0755]
t/t6012-rev-list-simplify.sh
t/t6022-merge-rename.sh
t/t6130-pathspec-noglob.sh
t/t6131-pathspec-icase.sh [new file with mode: 0755]
t/t7001-mv.sh
t/t7009-filter-branch-null-sha1.sh [new file with mode: 0755]
t/t7106-reset-unborn-branch.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7501-commit.sh
t/t7610-mergetool.sh
t/t9300-fast-import.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
templates/hooks--pre-push.sample
test-urlmatch-normalization.c [new file with mode: 0644]
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree.c
tree.h
unpack-trees.c
upload-pack.c
urlmatch.c [new file with mode: 0644]
urlmatch.h [new file with mode: 0644]
walker.c
wrapper.c
wt-status.c
wt-status.h
index 6b1fd1bfb060c438d7b70684612cef08fc1a5859..66199edd4af6ddcd1398bff13fe9065ba4309a22 100644 (file)
 /test-string-list
 /test-subprocess
 /test-svn-fe
+/test-urlmatch-normalization
 /test-wildmatch
 /common-cmds.h
 *.tar.gz
index 559d5f9ebf6fefe57b403327ed3e06942d0b822b..e5ca3b75d3778308ee1b7b20e3a86475244f7b96 100644 (file)
@@ -242,6 +242,14 @@ Writing Documentation:
  processed into HTML and manpages (e.g. git.html and git.1 in the
  same directory).
 
+ The documentation liberally mixes US and UK English (en_US/UK)
+ norms for spelling and grammar, which is somewhat unfortunate.
+ In an ideal world, it would have been better if it consistently
+ used only one and not the other, and we would have picked en_US
+ (if you wish to correct the English of some of the existing
+ documentation, please see the documentation-related advice in the
+ Documentation/SubmittingPatches file).
+
  Every user-visible change should be reflected in the documentation.
  The same general rule as for code applies -- imitate the existing
  conventions.  A few commented examples follow to provide reference
diff --git a/Documentation/RelNotes/1.8.5.txt b/Documentation/RelNotes/1.8.5.txt
new file mode 100644 (file)
index 0000000..d6fb0c0
--- /dev/null
@@ -0,0 +1,225 @@
+Git v1.8.5 Release Notes
+========================
+
+Backward compatibility notes (for Git 2.0)
+------------------------------------------
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there).  In Git 2.0, the default will change to the "simple"
+semantics that pushes:
+
+ - only the current branch to the branch with the same name, and only
+   when the current branch is set to integrate with that remote
+   branch, if you are pushing to the same remote as you fetch from; or
+
+ - only the current branch to the branch with the same name, if you
+   are pushing to a remote that is not where you usually fetch from.
+
+Use the user preference configuration variable "push.default" to
+change this.  If you are an old-timer who is used to the "matching"
+semantics, you can set the variable to "matching" to keep the
+traditional behaviour.  If you want to live in the future early, you
+can set it to "simple" today without waiting for Git 2.0.
+
+When "git add -u" (and "git add -A") is run inside a subdirectory and
+does not specify which paths to add on the command line, it
+will operate on the entire tree in Git 2.0 for consistency
+with "git commit -a" and other commands.  There will be no
+mechanism to make plain "git add -u" behave like "git add -u .".
+Current users of "git add -u" (without a pathspec) should start
+training their fingers to explicitly say "git add -u ."
+before Git 2.0 comes.  A warning is issued when these commands are
+run without a pathspec and when you have local changes outside the
+current directory, because the behaviour in Git 2.0 will be different
+from today's version in such a situation.
+
+In Git 2.0, "git add <path>" will behave as "git add -A <path>", so
+that "git add dir/" will notice paths you removed from the directory
+and record the removal.  Versions before Git 2.0, including this
+release, will keep ignoring removals, but the users who rely on this
+behaviour are encouraged to start using "git add --ignore-removal <path>"
+now before 2.0 is released.
+
+
+Updates since v1.8.4
+--------------------
+
+Foreign interfaces, subsystems and ports.
+
+ * remote-hg remote helper misbehaved when interacting with a local Hg
+   repository relative to the home directory, e.g. "clone hg::~/there".
+
+ * imap-send ported to OS X uses Apple's security framework instead of
+   OpenSSL one.
+
+ * Subversion 1.8.0 that was recently released breaks older subversion
+   clients coming over http/https in various ways.
+
+ * "git fast-import" treats an empty path given to "ls" as the root of
+   the tree.
+
+
+UI, Workflows & Features
+
+ * "git config" did not provide a way to set or access numbers larger
+   than a native "int" on the platform; it now provides 64-bit signed
+   integers on all platforms.
+
+ * "git pull --rebase" always chose to do the bog-standard flattening
+   rebase.  You can tell it to run "rebase --preserve-merges" by
+   setting "pull.rebase" configuration to "preserve".
+
+ * "git push --no-thin" actually disables the "thin pack transfer"
+   optimization.
+
+ * Magic pathspecs like ":(icase)makefile" that matches both
+   Makefile and makefile can be used in more places.
+
+ * The "http.*" variables can now be specified per URL that the
+   configuration applies.  For example,
+
+   [http]
+       sslVerify = true
+   [http "https://weak.example.com/"]
+       sslVerify = false
+
+   would flip http.sslVerify off only when talking to that specified
+   site.
+
+ * "git mv A B" when moving a submodule A has been taught to
+   relocate its working tree and to adjust the paths in the
+   .gitmodules file.
+
+ * "git blame" can now take more than one -L option to discover the
+   origin of multiple blocks of the lines.
+
+ * The http transport clients can optionally ask to save cookies
+   with http.savecookies configuration variable.
+
+ * "git push" learned a more fine grained control over a blunt
+   "--force" when requesting a non-fast-forward update with the
+   "--force-with-lease=<refname>:<expected object name>" option.
+
+ * "git diff --diff-filter=<classes of changes>" can now take
+   lowercase letters (e.g. "--diff-filter=d") to mean "show
+   everything but these classes".  "git diff-files -q" is now a
+   deprecated synonym for "git diff-files --diff-filter=d".
+
+ * "git fetch" (hence "git pull" as well) learned to check
+   "fetch.prune" and "remote.*.prune" configuration variables and
+   to behave as if the "--prune" command line option was given.
+
+ * "git check-ignore -z" applied the NUL termination to both its input
+   (with --stdin) and its output, but "git check-attr -z" ignored the
+   option on the output side. Make both honor -z on the input and
+   output side the same way.
+
+ * "git whatchanged" may still be used by old timers, but mention of
+   it in documents meant for new users will only waste readers' time
+   wonderig what the difference is between it and "git log".  Make it
+   less prominent in the general part of the documentation and explain
+   that it is merely a "git log" with different default behaviour in
+   its own document.
+
+
+Performance, Internal Implementation, etc.
+
+ * Many commands use --dashed-option as a operation mode selector
+   (e.g. "git tag --delete") that the user can use at most one
+   (e.g. "git tag --delete --verify" is a nonsense) and you cannot
+   negate (e.g. "git tag --no-delete" is a nonsense).  parse-options
+   API learned a new OPT_CMDMODE macro to make it easier to implement
+   such a set of options.
+
+ * OPT_BOOLEAN() in parse-options API was misdesigned to be "counting
+   up" but many subcommands expect it to behave as "on/off". Update
+   them to use OPT_BOOL() which is a proper boolean.
+
+ * "git gc" exits early without doing a double-work when it detects
+   that another instance of itself is already running.
+
+ * Under memory pressure and/or file descriptor pressure, we used to
+   close pack windows that are not used and also closed filehandle to
+   an open but unused packfiles. These are now controlled separately
+   to better cope with the load.
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v1.8.4
+------------------
+
+Unless otherwise noted, all the fixes since v1.8.4 in the maintenance
+track are contained in this release (see release notes to them for
+details).
+
+ * "git ls-files -k" needs to crawl only the part of the working tree
+   that may overlap the paths in the index to find killed files, but
+   shared code with the logic to find all the untracked files, which
+   made it unnecessarily inefficient.
+   (merge 680be04 jc/ls-files-killed-optim later to maint).
+
+ * The commit object names in the insn sheet that was prepared at the
+   beginning of "rebase -i" session can become ambiguous as the
+   rebasing progresses and the repository gains more commits. Make
+   sure the internal record is kept with full 40-hex object names.
+   (merge 75c6976 es/rebase-i-no-abbrev later to maint).
+
+ * "git rebase --preserve-merges" internally used the merge machinery
+   and as a side effect, left merge summary message in the log, but
+   when rebasing, there should not be a need for merge summary.
+   (merge a9f739c rt/rebase-p-no-merge-summary later to maint).
+
+ * A call to xread() was used without a loop around to cope with short
+   read in the codepath to stream new contents to a pack.
+   (merge e92527c js/xread-in-full later to maint).
+
+ * "git rebase -i" forgot that the comment character can be
+   configurable while reading its insn sheet.
+   (merge 7bca7af es/rebase-i-respect-core-commentchar later to maint).
+
+ * The mailmap support code read past the allocated buffer when the
+   mailmap file ended with an incomplete line.
+   (merge f972a16 jk/mailmap-incomplete-line later to maint).
+
+ * We used to send a large request to read(2)/write(2) as a single
+   system call, which was bad from the latency point of view when
+   the operation needs to be killed, and also triggered an error on
+   broken 64-bit systems that refuse to take more than 2GB read or
+   write in one go.
+   (merge a487916 sp/clip-read-write-to-8mb later to maint).
+
+ * "git fetch" that auto-followed tags incorrectly reused the
+   connection with Git-aware transport helper (like the sample "ext::"
+   helper shipped with Git).
+   (merge 0f73f8b jc/transport-do-not-use-connect-twice-in-fetch later to maint).
+
+ * "git log --full-diff -- <pathspec>" showed a huge diff for paths
+   outside the given <pathspec> for each commit, instead of showing
+   the change relative to the parent of the commit.  "git reflog -p"
+   had a similar problem.
+   (merge 838f9a1 tr/log-full-diff-keep-true-parents later to maint).
+
+ * Setting submodule.*.path configuration variable to true (without
+   giving "= value") caused Git to segfault.
+   (merge 4b05440 jl/some-submodule-config-are-not-boolean later to maint).
+
+ * "git rebase -i" (there could be others, as the root cause is pretty
+   generic) fed a random, data dependeant string to 'echo' and
+   expects it to come out literally, corrupting its error message.
+   (merge 89b0230 mm/no-shell-escape-in-die-message later to maint).
+
+ * Some people still use rather old versions of bash, which cannot
+   grok some constructs like 'printf -v varname' the prompt and
+   completion code started to use recently.
+   (merge a44aa69 bc/completion-for-bash-3.0 later to maint).
+
+ * Code to read configuration from a blob object did not compile on
+   platforms with fgetc() etc. implemented as macros.
+   (merge 49d6cfa hv/config-from-blob later to maint-1.8.3).
+
+ * The recent "short-cut clone connectivity check" topic broke a
+   shallow repository when a fetch operation tries to auto-follow tags.
+   (merge 6da8bdc nd/fetch-pack-shallow-fix later to maint-1.8.3).
index d0a4733e456777d97555ffd475799b468dc8b9de..705557689d7fa46f88f936bb8c42471ffbfb052b 100644 (file)
@@ -65,7 +65,20 @@ feature does not trigger when it shouldn't.  Also make sure that the
 test suite passes after your commit.  Do not forget to update the
 documentation to describe the updated behaviour.
 
-Oh, another thing.  I am picky about whitespaces.  Make sure your
+Speaking of the documentation, it is currently a liberal mixture of US
+and UK English norms for spelling and grammar, which is somewhat
+unfortunate.  A huge patch that touches the files all over the place
+only to correct the inconsistency is not welcome, though.  Potential
+clashes with other changes that can result from such a patch are not
+worth it.  We prefer to gradually reconcile the inconsistencies in
+favor of US English, with small and easily digestible patches, as a
+side effect of doing some other real work in the vicinity (e.g.
+rewriting a paragraph for clarity, while turning en_UK spelling to
+en_US).  Obvious typographical fixes are much more welcomed ("teh ->
+"the"), preferably submitted as independent patches separate from
+other documentation changes.
+
+Oh, another thing.  We are picky about whitespaces.  Make sure your
 changes do not trigger errors with the sample pre-commit hook shipped
 in templates/hooks--pre-commit.  To help ensure this does not happen,
 run git diff --check on your changes before you commit.
index 4e55b1564e8ad6526c53f797141cfccd83648fc7..0cebc4f6927211ffbc013de9368f03f480dba65d 100644 (file)
 
 -L <start>,<end>::
 -L :<regex>::
-       Annotate only the given line range.  <start> and <end> are optional.
-       ``-L <start>'' or ``-L <start>,'' spans from <start> to end of file.
-       ``-L ,<end>'' spans from start of file to <end>.
+       Annotate only the given line range. May be specified multiple times.
+       Overlapping ranges are allowed.
++
+<start> and <end> are optional. ``-L <start>'' or ``-L <start>,'' spans from
+<start> to end of file. ``-L ,<end>'' spans from start of file to <end>.
 +
-<start> and <end> can take one of these forms:
-
 include::line-range-format.txt[]
 
 -l::
index ec57a15ac5da284f35ced4441e2315fec9b05e2d..f7334904dc58c85da024827c8a0d037fe515e1c1 100644 (file)
@@ -553,22 +553,20 @@ sequence.editor::
        When not configured the default commit message editor is used instead.
 
 core.pager::
-       The command that Git will use to paginate output.  Can
-       be overridden with the `GIT_PAGER` environment
-       variable.  Note that Git sets the `LESS` environment
-       variable to `FRSX` if it is unset when it runs the
-       pager.  One can change these settings by setting the
-       `LESS` variable to some other value.  Alternately,
-       these settings can be overridden on a project or
-       global basis by setting the `core.pager` option.
-       Setting `core.pager` has no effect on the `LESS`
-       environment variable behaviour above, so if you want
-       to override Git's default settings this way, you need
-       to be explicit.  For example, to disable the S option
-       in a backward compatible manner, set `core.pager`
-       to `less -+S`.  This will be passed to the shell by
-       Git, which will translate the final command to
-       `LESS=FRSX less -+S`.
+       Text viewer for use by Git commands (e.g., 'less').  The value
+       is meant to be interpreted by the shell.  The order of preference
+       is the `$GIT_PAGER` environment variable, then `core.pager`
+       configuration, then `$PAGER`, and then the default chosen at
+       compile time (usually 'less').
++
+When the `LESS` environment variable is unset, Git sets it to `FRSX`
+(if `LESS` environment variable is set, Git does not change it at
+all).  If you want to selectively override Git's default setting
+for `LESS`, you can set `core.pager` to e.g. `less -+S`.  This will
+be passed to the shell by Git, which will translate the final
+command to `LESS=FRSX less -+S`. The environment tells the command
+to set the `S` option to chop long lines but the command line
+resets it to the default to fold long lines.
 
 core.whitespace::
        A comma separated list of common whitespace problems to
@@ -765,6 +763,10 @@ branch.<name>.rebase::
        instead of merging the default branch from the default remote when
        "git pull" is run. See "pull.rebase" for doing this in a non
        branch-specific manner.
++
+       When preserve, also pass `--preserve-merges` along to 'git rebase'
+       so that locally committed merge commits will not be flattened
+       by running 'git pull'.
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
@@ -787,8 +789,8 @@ browser.<tool>.path::
        working repository in gitweb (see linkgit:git-instaweb[1]).
 
 clean.requireForce::
-       A boolean to make git-clean do nothing unless given -f
-       or -n.   Defaults to true.
+       A boolean to make git-clean do nothing unless given -f,
+       -i or -n.   Defaults to true.
 
 color.branch::
        A boolean to enable/disable color in the output of
@@ -1061,6 +1063,10 @@ fetch.unpackLimit::
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
 
+fetch.prune::
+       If true, fetch will automatically behave as if the `--prune`
+       option was given on the command line.  See also `remote.<name>.prune`.
+
 format.attach::
        Enable multipart/mixed attachments as the default for
        'format-patch'.  The value can also be a double quoted string
@@ -1445,7 +1451,11 @@ http.cookiefile::
        of the file to read cookies from should be plain HTTP headers or
        the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
        NOTE that the file specified with http.cookiefile is only used as
-       input. No cookies will be stored in the file.
+       input unless http.saveCookies is set.
+
+http.savecookies::
+       If set, store cookies received during requests to the file specified by
+       http.cookiefile. Has no effect if http.cookiefile is unset.
 
 http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
@@ -1525,6 +1535,51 @@ http.useragent::
        of common USER_AGENT strings (but not including those like git/1.7.1).
        Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
 
+http.<url>.*::
+       Any of the http.* options above can be applied selectively to some urls.
+       For a config key to match a URL, each element of the config key is
+       compared to that of the URL, in the following order:
++
+--
+. Scheme (e.g., `https` in `https://example.com/`). This field
+  must match exactly between the config key and the URL.
+
+. Host/domain name (e.g., `example.com` in `https://example.com/`).
+  This field must match exactly between the config key and the URL.
+
+. Port number (e.g., `8080` in `http://example.com:8080/`).
+  This field must match exactly between the config key and the URL.
+  Omitted port numbers are automatically converted to the correct
+  default for the scheme before matching.
+
+. Path (e.g., `repo.git` in `https://example.com/repo.git`). The
+  path field of the config key must match the path field of the URL
+  either exactly or as a prefix of slash-delimited path elements.  This means
+  a config key with path `foo/` matches URL path `foo/bar`.  A prefix can only
+  match on a slash (`/`) boundary.  Longer matches take precedence (so a config
+  key with path `foo/bar` is a better match to URL path `foo/bar` than a config
+  key with just path `foo/`).
+
+. User name (e.g., `user` in `https://user@example.com/repo.git`). If
+  the config key has a user name it must match the user name in the
+  URL exactly. If the config key does not have a user name, that
+  config key will match a URL with any user name (including none),
+  but at a lower precedence than a config key with a user name.
+--
++
+The list above is ordered by decreasing precedence; a URL that matches
+a config key's path is preferred to one that matches its user name. For example,
+if the URL is `https://user@example.com/foo/bar` a config key match of
+`https://example.com/foo` will be preferred over a config key match of
+`https://user@example.com`.
++
+All URLs are normalized before attempting any matching (the password part,
+if embedded in the URL, is always ignored for matching purposes) so that
+equivalent urls that are simply spelled differently will match properly.
+Environment variable settings always override any matches.  The urls that are
+matched against are those given directly to Git commands.  This means any URLs
+visited as a result of a redirection do not participate in matching.
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
        does not care per se, but this information is necessary e.g. when
@@ -1825,6 +1880,10 @@ pull.rebase::
        of merging the default branch from the default remote when "git
        pull" is run. See "branch.<name>.rebase" for setting this on a
        per-branch basis.
++
+       When preserve, also pass `--preserve-merges` along to 'git rebase'
+       so that locally committed merge commits will not be flattened
+       by running 'git pull'.
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
@@ -2024,6 +2083,12 @@ remote.<name>.vcs::
        Setting this to a value <vcs> will cause Git to interact with
        the remote with the git-remote-<vcs> helper.
 
+remote.<name>.prune::
+       When set to true, fetching from this remote by default will also
+       remove any remote-tracking branches which no longer exist on the
+       remote (as if the `--prune` option was give on the command line).
+       Overrides `fetch.prune` settings, if any.
+
 remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
index 6cea7f1ce1ba3a917ddd0cc9a12195d6f768bf76..f2c85cc6334e50f716c23be6cac4e502fe4d228f 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
-           [-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
+           [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
            [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
@@ -18,7 +18,8 @@ DESCRIPTION
 Annotates each line in the given file with information from the revision which
 last modified the line. Optionally, start annotating from the given revision.
 
-The command can also limit the range of lines annotated.
+When specified one or more times, `-L` restricts annotation to the requested
+lines.
 
 The origin of lines is automatically followed across whole-file
 renames (currently there is no option to turn the rename-following
@@ -130,7 +131,10 @@ SPECIFYING RANGES
 
 Unlike 'git blame' and 'git annotate' in older versions of git, the extent
 of the annotation can be limited to both line ranges and revision
-ranges.  When you are interested in finding the origin for
+ranges. The `-L` option, which limits annotation to a range of lines, may be
+specified multiple times.
+
+When you are interested in finding the origin for
 lines 40-60 for file `foo`, you can use the `-L` option like so
 (they mean the same thing -- both ask for 21 lines starting at
 line 40):
index 10fbc6a373758437f23ef80245b3bd9e164add1f..21cffe2bcd0d5907efda9d525228e3f78a5cff65 100644 (file)
@@ -86,10 +86,9 @@ BATCH OUTPUT
 ------------
 
 If `--batch` or `--batch-check` is given, `cat-file` will read objects
-from stdin, one per line, and print information about them.
-
-Each line is considered as a whole object name, and is parsed as if
-given to linkgit:git-rev-parse[1].
+from stdin, one per line, and print information about them. By default,
+the whole line is considered as an object, as if it were fed to
+linkgit:git-rev-parse[1].
 
 You can specify the information shown for each object by using a custom
 `<format>`. The `<format>` is copied literally to stdout for each
@@ -110,6 +109,13 @@ newline. The available atoms are:
        The size, in bytes, that the object takes up on disk. See the
        note about on-disk sizes in the `CAVEATS` section below.
 
+`rest`::
+       If this atom is used in the output string, input lines are split
+       at the first whitespace boundary. All characters before that
+       whitespace are considered to be the object name; characters
+       after that first run of whitespace (i.e., the "rest" of the
+       line) are output in place of the `%(rest)` atom.
+
 If no format is specified, the default format is `%(objectname)
 %(objecttype) %(objectsize)`.
 
index a7be80d48bf6fd6928186116f00f3b48cf564041..00e2aa2df259d449602f59cbec33599346ebd8a0 100644 (file)
@@ -31,8 +31,9 @@ OPTIONS
        Read file names from stdin instead of from the command-line.
 
 -z::
-       Only meaningful with `--stdin`; paths are separated with a
-       NUL character instead of a linefeed character.
+       The output format is modified to be machine-parseable.
+       If `--stdin` is also given, input paths are separated
+       with a NUL character instead of a linefeed character.
 
 \--::
        Interpret all preceding arguments as attributes and all following
@@ -48,6 +49,10 @@ OUTPUT
 The output is of the form:
 <path> COLON SP <attribute> COLON SP <info> LF
 
+unless `-z` is in effect, in which case NUL is used as delimiter:
+<path> NUL <attribute> NUL <info> NUL
+
+
 <path> is the path of a file being queried, <attribute> is an attribute
 being queried and <info> can be either:
 
index 2dbe486eb16d8edde01ece856fa1e0ad75053042..e9917b89a91b36c87892ea1c08522f17be3c0d89 100644 (file)
@@ -15,6 +15,7 @@ SYNOPSIS
 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
+'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
 'git config' [<file-option>] --unset name [value_regex]
 'git config' [<file-option>] --unset-all name [value_regex]
 'git config' [<file-option>] --rename-section old_name new_name
@@ -95,6 +96,14 @@ OPTIONS
        in which section and variable names are lowercased, but subsection
        names are not.
 
+--get-urlmatch name URL::
+       When given a two-part name section.key, the value for
+       section.<url>.key whose <url> part matches the best to the
+       given URL is returned (if no such key exists, the value for
+       section.key is used as a fallback).  When given just the
+       section as name, do so for all the keys in the section and
+       list them.
+
 --global::
        For writing options: write to global `~/.gitconfig` file
        rather than the repository `.git/config`, write to
@@ -295,6 +304,13 @@ Given a .git/config like this:
                gitproxy=proxy-command for kernel.org
                gitproxy=default-proxy ; for all the rest
 
+       ; HTTP
+       [http]
+               sslVerify
+       [http "https://weak.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+
 you can set the filemode to true with
 
 ------------
@@ -380,6 +396,19 @@ RESET=$(git config --get-color "" "reset")
 echo "${WS}your whitespace color or blue reverse${RESET}"
 ------------
 
+For URLs in `https://weak.example.com`, `http.sslVerify` is set to
+false, while it is set to `true` for all others:
+
+------------
+% git config --bool --get-urlmatch http.sslverify https://good.example.com
+true
+% git config --bool --get-urlmatch http.sslverify https://weak.example.com
+false
+% git config --get-urlmatch http https://weak.example.com
+http.cookiefile /tmp/cookie.txt
+http.sslverify false
+------------
+
 include::config.txt[]
 
 GIT
index bf1a02a80d5f4a61f5bbd0d826ae7df3bce58911..4a9cc74d570cdf047ee8bf27504431c4122f7851 100644 (file)
@@ -361,8 +361,8 @@ and control the current import process.  More detailed discussion
        `--cat-blob-fd` or `stdout` if unspecified.
 
 `feature`::
-       Require that fast-import supports the specified feature, or
-       abort if it does not.
+       Enable the specified feature. This requires that fast-import
+       supports the specified feature, and aborts if it does not.
 
 `option`::
        Specify any of the options listed under OPTIONS that do not
index 1e717543470c0440a72812a046bcadf0e52e4953..444b805d350a6111d56b53d19fe155eb7b79038b 100644 (file)
@@ -90,6 +90,10 @@ be in a separate packet, and the list must end with a flush packet.
 --no-progress::
        Do not show the progress.
 
+--check-self-contained-and-connected::
+       Output "connectivity-ok" if the received pack is
+       self-contained and connected.
+
 -v::
        Run verbosely.
 
index e394276b1a2e2edfbbf8ab1dea9fcd30ca66e61f..9e0ef0eaf32f8861ba7090ae2feb6188c2c2f0b7 100644 (file)
@@ -242,6 +242,7 @@ configuration options in linkgit:git-notes[1] to use this workflow).
 Note that the leading character does not have to be a dot; for example,
 you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 
+-q::
 --quiet::
        Do not print the names of the generated files to standard output.
 
index 2402ed68280ded314c340e394da64b3372038113..e158a3b31fce6b7573a3f549a3d9e0ecbf1cdff2 100644 (file)
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 SYNOPSIS
 --------
 [verse]
-'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]
+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force]
 
 DESCRIPTION
 -----------
@@ -72,6 +72,10 @@ automatic consolidation of packs.
 --quiet::
        Suppress all progress reports.
 
+--force::
+       Force `git gc` to run even if there may be another `git gc`
+       instance running on this repository.
+
 Configuration
 -------------
 
index ac2694d04cfc73c9b5ae6e6982d2f7f290ed67fe..34097efea707aa59b6a5703826445f5c3fccabcf 100644 (file)
@@ -62,7 +62,8 @@ produced by --stat etc.
        Note that only message is considered, if also a diff is shown
        its size is not included.
 
--L <start>,<end>:<file>, -L :<regex>:<file>::
+-L <start>,<end>:<file>::
+-L :<regex>:<file>::
 
        Trace the evolution of the line range given by "<start>,<end>"
        (or the funcname regex <regex>) within the <file>.  You may
@@ -71,8 +72,6 @@ produced by --stat etc.
        give zero or one positive revision arguments.
        You can specify this option more than once.
 +
-<start> and <end> can take one of these forms:
-
 include::line-range-format.txt[]
 
 <revision range>::
index d7db2a3737fbdb032747bc6dd2740be1a56dfced..d2fc12ec77b85a5c9457cbfa160e5c05c1a32f43 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
        [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>]
-       <current-file> <base-file> <other-file>
+       [--[no-]diff3] <current-file> <base-file> <other-file>
 
 
 DESCRIPTION
@@ -66,6 +66,9 @@ OPTIONS
 -q::
        Quiet; do not warn about conflicts.
 
+--diff3::
+       Show conflicts in "diff3" style.
+
 --ours::
 --theirs::
 --union::
index 8c7f2f66d86e5d41113d6158b496068cc6b72bce..a74c3713c6e60db404cbca5afeeebfe687012f58 100644 (file)
@@ -186,11 +186,11 @@ In such a case, you can "unwrap" the tag yourself before feeding it
 to `git merge`, or pass `--ff-only` when you do not have any work on
 your own. e.g.
 
----
+----
 git fetch origin
 git merge v1.2.3^0
 git merge --ff-only v1.2.3
----
+----
 
 
 HOW CONFLICTS ARE PRESENTED
index e93fcb49fdca2d5e7987e10b5234c3d216fb47d6..b1f79881efdd181cf996672bc47c7772f21df8fd 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This script is used to move or rename a file, directory or symlink.
+Move or rename a file, directory or symlink.
 
  git mv [-v] [-f] [-n] [-k] <source> <destination>
  git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
@@ -44,6 +44,14 @@ OPTIONS
 --verbose::
        Report the names of files as they are moved.
 
+SUBMODULES
+----------
+Moving a submodule using a gitfile (which means they were cloned
+with a Git version 1.7.8 or newer) will update the gitfile and
+core.worktree setting to make the submodule work in the new location.
+It also will attempt to update the submodule.<name>.path setting in
+the linkgit:gitmodules[5] file and stage that file (unless -n is used).
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 6ef8d599d31ac20982fa74f213b81494ceae162a..beea10b148ce9a24752222081caecb1497150b29 100644 (file)
@@ -102,12 +102,18 @@ include::merge-options.txt[]
 :git-pull: 1
 
 -r::
---rebase::
-       Rebase the current branch on top of the upstream branch after
-       fetching.  If there is a remote-tracking branch corresponding to
-       the upstream branch and the upstream branch was rebased since last
-       fetched, the rebase uses that information to avoid rebasing
-       non-local changes.
+--rebase[=false|true|preserve]::
+       When true, rebase the current branch on top of the upstream
+       branch after fetching. If there is a remote-tracking branch
+       corresponding to the upstream branch and the upstream branch
+       was rebased since last fetched, the rebase uses that information
+       to avoid rebasing non-local changes.
++
+When preserve, also rebase the current branch on top of the upstream
+branch, but pass `--preserve-merges` along to `git rebase` so that
+locally created merge commits will not be flattened.
++
+When false, merge the current branch into the upstream branch.
 +
 See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
 linkgit:git-config[1] if you want to make `git pull` always use
index f7dfe48d2813324e1a699efa530afe8974d7c400..e2992f17a0e4e0e78630ee8cbc1c723a70d855e2 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
           [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
+          [--force-with-lease[=<refname>[:<expect>]]]
           [--no-verify] [<repository> [<refspec>...]]
 
 DESCRIPTION
@@ -130,21 +131,75 @@ already exists on the remote side.
        repository over ssh, and you do not have the program in
        a directory on the default $PATH.
 
+--[no-]force-with-lease::
+--force-with-lease=<refname>::
+--force-with-lease=<refname>:<expect>::
+       Usually, "git push" refuses to update a remote ref that is
+       not an ancestor of the local ref used to overwrite it.
++
+This option bypasses the check, but instead requires that the
+current value of the ref to be the expected value.  "git push"
+fails otherwise.
++
+Imagine that you have to rebase what you have already published.
+You will have to bypass the "must fast-forward" rule in order to
+replace the history you originally published with the rebased history.
+If somebody else built on top of your original history while you are
+rebasing, the tip of the branch at the remote may advance with her
+commit, and blindly pushing with `--force` will lose her work.
++
+This option allows you to say that you expect the history you are
+updating is what you rebased and want to replace. If the remote ref
+still points at the commit you specified, you can be sure that no
+other people did anything to the ref (it is like taking a "lease" on
+the ref without explicitly locking it, and you update the ref while
+making sure that your earlier "lease" is still valid).
++
+`--force-with-lease` alone, without specifying the details, will protect
+all remote refs that are going to be updated by requiring their
+current value to be the same as the remote-tracking branch we have
+for them, unless specified with a `--force-with-lease=<refname>:<expect>`
+option that explicitly states what the expected value is.
++
+`--force-with-lease=<refname>`, without specifying the expected value, will
+protect the named ref (alone), if it is going to be updated, by
+requiring its current value to be the same as the remote-tracking
+branch we have for it.
++
+`--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
+if it is going to be updated, by requiring its current value to be
+the same as the specified value <expect> (which is allowed to be
+different from the remote-tracking branch we have for the refname,
+or we do not even have to have such a remote-tracking branch when
+this form is used).
++
+Note that all forms other than `--force-with-lease=<refname>:<expect>`
+that specifies the expected current value of the ref explicitly are
+still experimental and their semantics may change as we gain experience
+with this feature.
++
+"--no-force-with-lease" will cancel all the previous --force-with-lease on the
+command line.
+
 -f::
 --force::
        Usually, the command refuses to update a remote ref that is
        not an ancestor of the local ref used to overwrite it.
-       This flag disables the check.  This can cause the
-       remote repository to lose commits; use it with care.
-       Note that `--force` applies to all the refs that are pushed,
-       hence using it with `push.default` set to `matching` or with
-       multiple push destinations configured with `remote.*.push`
-       may overwrite refs other than the current branch (including
-       local refs that are strictly behind their remote counterpart).
-       To force a push to only one branch, use a `+` in front of the
-       refspec to push (e.g `git push origin +master` to force a push
-       to the `master` branch). See the `<refspec>...` section above
-       for details.
+       Also, when `--force-with-lease` option is used, the command refuses
+       to update a remote ref whose current value does not match
+       what is expected.
++
+This flag disables these checks, and can cause the remote repository
+to lose commits; use it with care.
++
+Note that `--force` applies to all the refs that are pushed, hence
+using it with `push.default` set to `matching` or with multiple push
+destinations configured with `remote.*.push` may overwrite refs
+other than the current branch (including local refs that are
+strictly behind their remote counterpart).  To force a push to only
+one branch, use a `+` in front of the refspec to push (e.g `git push
+origin +master` to force a push to the `master` branch). See the
+`<refspec>...` section above for details.
 
 --repo=<repository>::
        This option is only relevant if no <repository> argument is
index 2b126c0a77f9901eea1ba5ac37a2fb2d66c4d21f..d068a653778d6713d6ab370379dadf0d6f754856 100644 (file)
@@ -24,9 +24,23 @@ distinguish between them.
 
 OPTIONS
 -------
+
+Operation Modes
+~~~~~~~~~~~~~~~
+
+Each of these options must appear first on the command line.
+
 --parseopt::
        Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
 
+--sq-quote::
+       Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
+       section below). In contrast to the `--sq` option below, this
+       mode does only quoting. Nothing else is done to command input.
+
+Options for --parseopt
+~~~~~~~~~~~~~~~~~~~~~~
+
 --keep-dashdash::
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
@@ -36,10 +50,8 @@ OPTIONS
        the first non-option argument.  This can be used to parse sub-commands
        that take options themselves.
 
---sq-quote::
-       Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
-       section below). In contrast to the `--sq` option below, this
-       mode does only quoting. Nothing else is done to command input.
+Options for Filtering
+~~~~~~~~~~~~~~~~~~~~~
 
 --revs-only::
        Do not output flags and parameters not meant for
@@ -55,6 +67,9 @@ OPTIONS
 --no-flags::
        Do not output flag parameters.
 
+Options for Output
+~~~~~~~~~~~~~~~~~~
+
 --default <arg>::
        If there is no parameter given by the user, use `<arg>`
        instead.
@@ -110,6 +125,17 @@ can be used.
        strip '{caret}' prefix from the object names that already have
        one.
 
+--abbrev-ref[=(strict|loose)]::
+       A non-ambiguous short name of the objects name.
+       The option core.warnAmbiguousRefs is used to select the strict
+       abbreviation mode.
+
+--short::
+--short=number::
+       Instead of outputting the full SHA-1 values of object names try to
+       abbreviate them to a shorter unique name. When no length is specified
+       7 is used. The minimum length is 4.
+
 --symbolic::
        Usually the object names are output in SHA-1 form (with
        possible '{caret}' prefix); this option makes them output in a
@@ -123,16 +149,8 @@ can be used.
        unfortunately named tag "master"), and show them as full
        refnames (e.g. "refs/heads/master").
 
---abbrev-ref[=(strict|loose)]::
-       A non-ambiguous short name of the objects name.
-       The option core.warnAmbiguousRefs is used to select the strict
-       abbreviation mode.
-
---disambiguate=<prefix>::
-       Show every object whose name begins with the given prefix.
-       The <prefix> must be at least 4 hexadecimal digits long to
-       avoid listing each and every object in the repository by
-       mistake.
+Options for Objects
+~~~~~~~~~~~~~~~~~~~
 
 --all::
        Show all refs found in `refs/`.
@@ -155,18 +173,20 @@ shown.  If the pattern does not contain a globbing character (`?`,
        character (`?`, `*`, or `[`), it is turned into a prefix
        match by appending `/*`.
 
---show-toplevel::
-       Show the absolute path of the top-level directory.
+--disambiguate=<prefix>::
+       Show every object whose name begins with the given prefix.
+       The <prefix> must be at least 4 hexadecimal digits long to
+       avoid listing each and every object in the repository by
+       mistake.
 
---show-prefix::
-       When the command is invoked from a subdirectory, show the
-       path of the current directory relative to the top-level
-       directory.
+Options for Files
+~~~~~~~~~~~~~~~~~
 
---show-cdup::
-       When the command is invoked from a subdirectory, show the
-       path of the top-level directory relative to the current
-       directory (typically a sequence of "../", or an empty string).
+--local-env-vars::
+       List the GIT_* environment variables that are local to the
+       repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
+       Only the names of the variables are listed, not their value,
+       even if they are set.
 
 --git-dir::
        Show `$GIT_DIR` if defined. Otherwise show the path to
@@ -188,17 +208,27 @@ print a message to stderr and exit with nonzero status.
 --is-bare-repository::
        When the repository is bare print "true", otherwise "false".
 
---local-env-vars::
-       List the GIT_* environment variables that are local to the
-       repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).
-       Only the names of the variables are listed, not their value,
-       even if they are set.
+--resolve-git-dir <path>::
+       Check if <path> is a valid repository or a gitfile that
+       points at a valid repository, and print the location of the
+       repository.  If <path> is a gitfile then the resolved path
+       to the real repository is printed.
 
---short::
---short=number::
-       Instead of outputting the full SHA-1 values of object names try to
-       abbreviate them to a shorter unique name. When no length is specified
-       7 is used. The minimum length is 4.
+--show-cdup::
+       When the command is invoked from a subdirectory, show the
+       path of the top-level directory relative to the current
+       directory (typically a sequence of "../", or an empty string).
+
+--show-prefix::
+       When the command is invoked from a subdirectory, show the
+       path of the current directory relative to the top-level
+       directory.
+
+--show-toplevel::
+       Show the absolute path of the top-level directory.
+
+Other Options
+~~~~~~~~~~~~~
 
 --since=datestring::
 --after=datestring::
@@ -213,12 +243,6 @@ print a message to stderr and exit with nonzero status.
 <args>...::
        Flags and parameters to be parsed.
 
---resolve-git-dir <path>::
-       Check if <path> is a valid repository or a gitfile that
-       points at a valid repository, and print the location of the
-       repository.  If <path> is a gitfile then the resolved path
-       to the real repository is printed.
-
 
 include::revisions.txt[]
 
index 1d876c2619a414159a121868c5dbaab48e55a708..9d731b453d1af4cbdbf559b72e8064a77696f4a3 100644 (file)
@@ -134,14 +134,16 @@ use the following command:
 git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
 ----------------
 
-Submodules
-~~~~~~~~~~
+SUBMODULES
+----------
 Only submodules using a gitfile (which means they were cloned
 with a Git version 1.7.8 or newer) will be removed from the work
 tree, as their repository lives inside the .git directory of the
 superproject. If a submodule (or one of those nested inside it)
 still uses a .git directory, `git rm` will fail - no matter if forced
-or not - to protect the submodule's history.
+or not - to protect the submodule's history. If it exists the
+submodule.<name> section in the linkgit:gitmodules[5] file will also
+be removed and that file will be staged (unless --cached or -n are used).
 
 A submodule is considered up-to-date when the HEAD is the same as
 recorded in the index, no tracked files are modified and no untracked
index c600b61e2b62013366476807c733837c0e854165..8b63ceb00e71a5e2f09cf7686b0978dcf71d1e03 100644 (file)
@@ -13,43 +13,17 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Shows commit logs and diff output each commit introduces.  The
-command internally invokes 'git rev-list' piped to
-'git diff-tree', and takes command line options for both of
-these commands.
 
-This manual page describes only the most frequently used options.
+Shows commit logs and diff output each commit introduces.
 
+New users are encouraged to use linkgit:git-log[1] instead.  The
+`whatchanged` command is essentially the same as linkgit:git-log[1]
+but defaults to show the raw format diff output and to skip merges.
 
-OPTIONS
--------
--p::
-       Show textual diffs, instead of the Git internal diff
-       output format that is useful only to tell the changed
-       paths and their nature of changes.
+The command is kept primarily for historical reasons; fingers of
+many people who learned Git long before `git log` was invented by
+reading Linux kernel mailing list are trained to type it.
 
--<n>::
-       Limit output to <n> commits.
-
-<since>..<until>::
-       Limit output to between the two named commits (bottom
-       exclusive, top inclusive).
-
--r::
-       Show Git internal diff output, but for the whole tree,
-       not just the top level.
-
--m::
-       By default, differences for merge commits are not shown.
-       With this flag, show differences to that commit from all
-       of its parents.
-+
-However, it is not very useful in general, although it
-*is* useful on a file-by-file basis.
-
-include::pretty-options.txt[]
-
-include::pretty-formats.txt[]
 
 Examples
 --------
index dca11cc96e7f0233fbfa4191cbdd3bb50f91f33e..c4f0ed59576b877a5eab2e36aa874d8c572bb905 100644 (file)
@@ -457,10 +457,25 @@ help ...`.
        linkgit:git-replace[1] for more information.
 
 --literal-pathspecs::
-       Treat pathspecs literally, rather than as glob patterns. This is
-       equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
+       Treat pathspecs literally (i.e. no globbing, no pathspec magic).
+       This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
        variable to `1`.
 
+--glob-pathspecs:
+       Add "glob" magic to all pathspec. This is equivalent to setting
+       the `GIT_GLOB_PATHSPECS` environment variable to `1`. Disabling
+       globbing on individual pathspecs can be done using pathspec
+       magic ":(literal)"
+
+--noglob-pathspecs:
+       Add "literal" magic to all pathspec. This is equivalent to setting
+       the `GIT_NOGLOB_PATHSPECS` environment variable to `1`. Enabling
+       globbing on individual pathspecs can be done using pathspec
+       magic ":(glob)"
+
+--icase-pathspecs:
+       Add "icase" magic to all pathspec. This is equivalent to setting
+       the `GIT_ICASE_PATHSPECS` environment variable to `1`.
 
 GIT COMMANDS
 ------------
@@ -823,7 +838,7 @@ for further details.
 'GIT_FLUSH'::
        If this environment variable is set to "1", then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
-       'git check-attr', 'git check-ignore', and 'git whatchanged' will
+       'git check-attr' and 'git check-ignore' will
        force a flush of the output stream after each record have been
        flushed. If this
        variable is set to "0", the output of these commands will be done
@@ -867,6 +882,18 @@ GIT_LITERAL_PATHSPECS::
        literal paths to Git (e.g., paths previously given to you by
        `git ls-tree`, `--raw` diff output, etc).
 
+GIT_GLOB_PATHSPECS::
+       Setting this variable to `1` will cause Git to treat all
+       pathspecs as glob patterns (aka "glob" magic).
+
+GIT_NOGLOB_PATHSPECS::
+       Setting this variable to `1` will cause Git to treat all
+       pathspecs as literal (aka "literal" magic).
+
+GIT_ICASE_PATHSPECS::
+       Setting this variable to `1` will cause Git to treat all
+       pathspecs as case-insensitive.
+
 
 Discussion[[Discussion]]
 ------------------------
index f538a870c72dec8a022c23d5023cf5252e9925cf..058a3529809e41dfaf29dc510c2cb626dedb5402 100644 (file)
@@ -534,42 +534,9 @@ all, but just show the actual commit message.
 
 In fact, together with the 'git rev-list' program (which generates a
 list of revisions), 'git diff-tree' ends up being a veritable fount of
-changes. A trivial (but very useful) script called 'git whatchanged' is
-included with Git which does exactly this, and shows a log of recent
-activities.
-
-To see the whole history of our pitiful little git-tutorial project, you
-can do
-
-----------------
-$ git log
-----------------
-
-which shows just the log messages, or if we want to see the log together
-with the associated patches use the more complex (and much more
-powerful)
-
-----------------
-$ git whatchanged -p
-----------------
-
-and you will see exactly what has changed in the repository over its
-short history.
-
-[NOTE]
-When using the above two commands, the initial commit will be shown.
-If this is a problem because it is huge, you can hide it by setting
-the log.showroot configuration variable to false. Having this, you
-can still show it for each command just adding the `--root` option,
-which is a flag for 'git diff-tree' accepted by both commands.
-
-With that, you should now be having some inkling of what Git does, and
-can explore on your own.
-
-[NOTE]
-Most likely, you are not directly using the core
-Git Plumbing commands, but using Porcelain such as 'git add', `git-rm'
-and `git-commit'.
+changes.  You can emulate `git log`, `git log -p`, etc. with a trivial
+script that pipes the output of `git rev-list` to `git diff-tree --stdin`,
+which was exactly how early versions of `git log` were implemented.
 
 
 Tagging a version
index 0827f691396ba698d3976eba1eddedd9e7ff3e9e..f1f4ca972791892126f757b2f697758e4e5fe26d 100644 (file)
@@ -120,6 +120,11 @@ connecting (see the 'connect' command under COMMANDS).
 When choosing between 'push' and 'export', Git prefers 'push'.
 Other frontends may have some other order of preference.
 
+'no-private-update'::
+       When using the 'refspec' capability, git normally updates the
+       private ref on successful push. This update is disabled when
+       the remote-helper declares the capability 'no-private-update'.
+
 
 Capabilities for Fetching
 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -143,6 +148,10 @@ Supported commands: 'list', 'fetch'.
 +
 Supported commands: 'list', 'import'.
 
+'check-connectivity'::
+       Can guarantee that when a clone is requested, the received
+       pack is self contained and is connected.
+
 If a helper advertises 'connect', Git will use it if possible and
 fall back to another capability if the helper requests so when
 connecting (see the 'connect' command under COMMANDS).
@@ -176,6 +185,12 @@ applicable refspec takes precedence.  The left-hand of refspecs
 advertised with this capability must cover all refs reported by
 the list command.  If no 'refspec' capability is advertised,
 there is an implied `refspec *:*`.
++
+When writing remote-helpers for decentralized version control
+systems, it is advised to keep a local copy of the repository to
+interact with, and to let the private namespace refs point to this
+local repository, while the refs/remotes namespace is used to track
+the remote repository.
 
 'bidi-import'::
        This modifies the 'import' capability.
@@ -270,6 +285,9 @@ Optionally may output a 'lock <file>' line indicating a file under
 GIT_DIR/objects/pack which is keeping a pack until refs can be
 suitably updated.
 +
+If option 'check-connectivity' is requested, the helper must output
+'connectivity-ok' if the clone is self-contained and connected.
++
 Supported if the helper has the "fetch" capability.
 
 'push' +<src>:<dst>::
@@ -416,6 +434,9 @@ set by Git if the remote helper has the 'option' capability.
        must not rely on this option being set before
        connect request occurs.
 
+'option check-connectivity' \{'true'|'false'\}::
+       Request the helper to check connectivity of a clone.
+
 SEE ALSO
 --------
 linkgit:git-remote[1]
index dba5062b379cdfcdde31ee0f6809e964f979b5f5..13a64d3aac8aca7181a6735abdabd8ed6d28ddc0 100644 (file)
@@ -322,10 +322,54 @@ and a close parentheses `)`, and the remainder is the pattern to match
 against the path.
 +
 The "magic signature" consists of an ASCII symbol that is not
-alphanumeric. Currently only the slash `/` is recognized as a
-"magic signature": it makes the pattern match from the root of
-the working tree, even when you are running the command from
-inside a subdirectory.
+alphanumeric.
++
+--
+top `/`;;
+       The magic word `top` (mnemonic: `/`) makes the pattern match
+       from the root of the working tree, even when you are running
+       the command from inside a subdirectory.
+
+literal;;
+       Wildcards in the pattern such as `*` or `?` are treated
+       as literal characters.
+
+icase;;
+       Case insensitive match.
+
+glob;;
+       Git treats the pattern as a shell glob suitable for
+       consumption by fnmatch(3) with the FNM_PATHNAME flag:
+       wildcards in the pattern will not match a / in the pathname.
+       For example, "Documentation/{asterisk}.html" matches
+       "Documentation/git.html" but not "Documentation/ppc/ppc.html"
+       or "tools/perf/Documentation/perf.html".
++
+Two consecutive asterisks ("`**`") in patterns matched against
+full pathname may have special meaning:
+
+ - A leading "`**`" followed by a slash means match in all
+   directories. For example, "`**/foo`" matches file or directory
+   "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
+   matches file or directory "`bar`" anywhere that is directly
+   under directory "`foo`".
+
+ - A trailing "/**" matches everything inside. For example,
+   "abc/**" matches all files inside directory "abc", relative
+   to the location of the `.gitignore` file, with infinite depth.
+
+ - A slash followed by two consecutive asterisks then a slash
+   matches zero or more directories. For example, "`a/**/b`"
+   matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on.
+
+ - Other consecutive asterisks are considered invalid.
++
+Glob magic is incompatible with literal magic.
+--
++
+Currently only the slash `/` is recognized as the "magic signature",
+but it is envisioned that we will support more types of magic in later
+versions of Git.
 +
 A pathspec with only a colon means "there is no pathspec". This form
 should not be combined with other pathspec.
index 3e7ce72daab23d6dfcf12d1e0dd1dd6be705e56d..d7f26039cac500ff922dd3fb9470912b03718335 100644 (file)
@@ -1,3 +1,5 @@
+<start> and <end> can take one of these forms:
+
 - number
 +
 If <start> or <end> is a number, it specifies an
@@ -7,7 +9,10 @@ absolute line number (lines count from 1).
 - /regex/
 +
 This form will use the first line matching the given
-POSIX regex.  If <end> is a regex, it will search
+POSIX regex. If <start> is a regex, it will search from the end of
+the previous `-L` range, if any, otherwise from the start of file.
+If <start> is ``^/regex/'', it will search from the start of file.
+If <end> is a regex, it will search
 starting at the line given by <start>.
 +
 
@@ -15,11 +20,10 @@ starting at the line given by <start>.
 +
 This is only valid for <end> and will specify a number
 of lines before or after the line given by <start>.
-+
 
-- :regex
 +
-If the option's argument is of the form :regex, it denotes the range
+If ``:<regex>'' is given in place of <start> and <end>, it denotes the range
 from the first funcname line that matches <regex>, up to the next
-funcname line.
-+
+funcname line. ``:<regex>'' searches from the end of the previous `-L` range,
+if any, otherwise from the start of file.
+``^:<regex>'' searches from the start of file.
index 4f63a04d7db0e7b578c5034c2856ba95a7ef5739..540e45568990f89fceb50ad619ca8402e9925983 100644 (file)
@@ -8,6 +8,42 @@ Talk about
 * is_inside_git_dir()
 * is_inside_work_tree()
 * setup_work_tree()
-* get_pathspec()
 
 (Dscho)
+
+Pathspec
+--------
+
+See glossary-context.txt for the syntax of pathspec. In memory, a
+pathspec set is represented by "struct pathspec" and is prepared by
+parse_pathspec(). This function takes several arguments:
+
+- magic_mask specifies what features that are NOT supported by the
+  following code. If a user attempts to use such a feature,
+  parse_pathspec() can reject it early.
+
+- flags specifies other things that the caller wants parse_pathspec to
+  perform.
+
+- prefix and args come from cmd_* functions
+
+get_pathspec() is obsolete and should never be used in new code.
+
+parse_pathspec() helps catch unsupported features and reject them
+politely. At a lower level, different pathspec-related functions may
+not support the same set of features. Such pathspec-sensitive
+functions are guarded with GUARD_PATHSPEC(), which will die in an
+unfriendly way when an unsupported feature is requested.
+
+The command designers are supposed to make sure that GUARD_PATHSPEC()
+never dies. They have to make sure all unsupported features are caught
+by parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC()
+should give the designers all pathspec-sensitive codepaths and what
+features they support.
+
+A similar process is applied when a new pathspec magic is added. The
+designer lifts the GUARD_PATHSPEC restriction in the functions that
+support the new magic. At the same time (s)he has to make sure this
+new feature will be caught at parse_pathspec() in commands that cannot
+handle the new magic in some cases. grepping parse_pathspec() should
+help.
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
new file mode 100644 (file)
index 0000000..a1173ee
--- /dev/null
@@ -0,0 +1,503 @@
+HTTP transfer protocols
+=======================
+
+Git supports two HTTP based transfer protocols.  A "dumb" protocol
+which requires only a standard HTTP server on the server end of the
+connection, and a "smart" protocol which requires a Git aware CGI
+(or server module).  This document describes both protocols.
+
+As a design feature smart clients can automatically upgrade "dumb"
+protocol URLs to smart URLs.  This permits all users to have the
+same published URL, and the peers automatically select the most
+efficient transport available to them.
+
+
+URL Format
+----------
+
+URLs for Git repositories accessed by HTTP use the standard HTTP
+URL syntax documented by RFC 1738, so they are of the form:
+
+  http://<host>:<port>/<path>?<searchpart>
+
+Within this documentation the placeholder $GIT_URL will stand for
+the http:// repository URL entered by the end-user.
+
+Servers SHOULD handle all requests to locations matching $GIT_URL, as
+both the "smart" and "dumb" HTTP protocols used by Git operate
+by appending additional path components onto the end of the user
+supplied $GIT_URL string.
+
+An example of a dumb client requesting for a loose object:
+
+  $GIT_URL:     http://example.com:8080/git/repo.git
+  URL request:  http://example.com:8080/git/repo.git/objects/d0/49f6c27a2244e12041955e262a404c7faba355
+
+An example of a smart request to a catch-all gateway:
+
+  $GIT_URL:     http://example.com/daemon.cgi?svc=git&q=
+  URL request:  http://example.com/daemon.cgi?svc=git&q=/info/refs&service=git-receive-pack
+
+An example of a request to a submodule:
+
+  $GIT_URL:     http://example.com/git/repo.git/path/submodule.git
+  URL request:  http://example.com/git/repo.git/path/submodule.git/info/refs
+
+Clients MUST strip a trailing '/', if present, from the user supplied
+$GIT_URL string to prevent empty path tokens ('//') from appearing
+in any URL sent to a server.  Compatible clients MUST expand
+'$GIT_URL/info/refs' as 'foo/info/refs' and not 'foo//info/refs'.
+
+
+Authentication
+--------------
+
+Standard HTTP authentication is used if authentication is required
+to access a repository, and MAY be configured and enforced by the
+HTTP server software.
+
+Because Git repositories are accessed by standard path components
+server administrators MAY use directory based permissions within
+their HTTP server to control repository access.
+
+Clients SHOULD support Basic authentication as described by RFC 2616.
+Servers SHOULD support Basic authentication by relying upon the
+HTTP server placed in front of the Git server software.
+
+Servers SHOULD NOT require HTTP cookies for the purposes of
+authentication or access control.
+
+Clients and servers MAY support other common forms of HTTP based
+authentication, such as Digest authentication.
+
+
+SSL
+---
+
+Clients and servers SHOULD support SSL, particularly to protect
+passwords when relying on Basic HTTP authentication.
+
+
+Session State
+-------------
+
+The Git over HTTP protocol (much like HTTP itself) is stateless
+from the perspective of the HTTP server side.  All state MUST be
+retained and managed by the client process.  This permits simple
+round-robin load-balancing on the server side, without needing to
+worry about state management.
+
+Clients MUST NOT require state management on the server side in
+order to function correctly.
+
+Servers MUST NOT require HTTP cookies in order to function correctly.
+Clients MAY store and forward HTTP cookies during request processing
+as described by RFC 2616 (HTTP/1.1).  Servers SHOULD ignore any
+cookies sent by a client.
+
+
+General Request Processing
+--------------------------
+
+Except where noted, all standard HTTP behavior SHOULD be assumed
+by both client and server.  This includes (but is not necessarily
+limited to):
+
+If there is no repository at $GIT_URL, or the resource pointed to by a
+location matching $GIT_URL does not exist, the server MUST NOT respond
+with '200 OK' response.  A server SHOULD respond with
+'404 Not Found', '410 Gone', or any other suitable HTTP status code
+which does not imply the resource exists as requested.
+
+If there is a repository at $GIT_URL, but access is not currently
+permitted, the server MUST respond with the '403 Forbidden' HTTP
+status code.
+
+Servers SHOULD support both HTTP 1.0 and HTTP 1.1.
+Servers SHOULD support chunked encoding for both request and response
+bodies.
+
+Clients SHOULD support both HTTP 1.0 and HTTP 1.1.
+Clients SHOULD support chunked encoding for both request and response
+bodies.
+
+Servers MAY return ETag and/or Last-Modified headers.
+
+Clients MAY revalidate cached entities by including If-Modified-Since
+and/or If-None-Match request headers.
+
+Servers MAY return '304 Not Modified' if the relevant headers appear
+in the request and the entity has not changed.  Clients MUST treat
+'304 Not Modified' identical to '200 OK' by reusing the cached entity.
+
+Clients MAY reuse a cached entity without revalidation if the
+Cache-Control and/or Expires header permits caching.  Clients and
+servers MUST follow RFC 2616 for cache controls.
+
+
+Discovering References
+----------------------
+
+All HTTP clients MUST begin either a fetch or a push exchange by
+discovering the references available on the remote repository.
+
+Dumb Clients
+~~~~~~~~~~~~
+
+HTTP clients that only support the "dumb" protocol MUST discover
+references by making a request for the special info/refs file of
+the repository.
+
+Dumb HTTP clients MUST make a GET request to $GIT_URL/info/refs,
+without any search/query parameters.
+
+   C: GET $GIT_URL/info/refs HTTP/1.0
+
+   S: 200 OK
+   S:
+   S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint
+   S: d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master
+   S: 2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0
+   S: a3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}
+
+The Content-Type of the returned info/refs entity SHOULD be
+"text/plain; charset=utf-8", but MAY be any content type.
+Clients MUST NOT attempt to validate the returned Content-Type.
+Dumb servers MUST NOT return a return type starting with
+"application/x-git-".
+
+Cache-Control headers MAY be returned to disable caching of the
+returned entity.
+
+When examining the response clients SHOULD only examine the HTTP
+status code.  Valid responses are '200 OK', or '304 Not Modified'.
+
+The returned content is a UNIX formatted text file describing
+each ref and its known value.  The file SHOULD be sorted by name
+according to the C locale ordering.  The file SHOULD NOT include
+the default ref named 'HEAD'.
+
+  info_refs   =  *( ref_record )
+  ref_record  =  any_ref / peeled_ref
+
+  any_ref     =  obj-id HTAB refname LF
+  peeled_ref  =  obj-id HTAB refname LF
+                obj-id HTAB refname "^{}" LF
+
+Smart Clients
+~~~~~~~~~~~~~
+
+HTTP clients that support the "smart" protocol (or both the
+"smart" and "dumb" protocols) MUST discover references by making
+a parameterized request for the info/refs file of the repository.
+
+The request MUST contain exactly one query parameter,
+'service=$servicename', where $servicename MUST be the service
+name the client wishes to contact to complete the operation.
+The request MUST NOT contain additional query parameters.
+
+   C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0
+
+   dumb server reply:
+   S: 200 OK
+   S:
+   S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint
+   S: d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master
+   S: 2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0
+   S: a3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}
+
+   smart server reply:
+   S: 200 OK
+   S: Content-Type: application/x-git-upload-pack-advertisement
+   S: Cache-Control: no-cache
+   S:
+   S: 001e# service=git-upload-pack\n
+   S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint\0multi_ack\n
+   S: 0042d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n
+   S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n
+   S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n
+
+Dumb Server Response
+^^^^^^^^^^^^^^^^^^^^
+Dumb servers MUST respond with the dumb server reply format.
+
+See the prior section under dumb clients for a more detailed
+description of the dumb server response.
+
+Smart Server Response
+^^^^^^^^^^^^^^^^^^^^^
+If the server does not recognize the requested service name, or the
+requested service name has been disabled by the server administrator,
+the server MUST respond with the '403 Forbidden' HTTP status code.
+
+Otherwise, smart servers MUST respond with the smart server reply
+format for the requested service name.
+
+Cache-Control headers SHOULD be used to disable caching of the
+returned entity.
+
+The Content-Type MUST be 'application/x-$servicename-advertisement'.
+Clients SHOULD fall back to the dumb protocol if another content
+type is returned.  When falling back to the dumb protocol clients
+SHOULD NOT make an additional request to $GIT_URL/info/refs, but
+instead SHOULD use the response already in hand.  Clients MUST NOT
+continue if they do not support the dumb protocol.
+
+Clients MUST validate the status code is either '200 OK' or
+'304 Not Modified'.
+
+Clients MUST validate the first five bytes of the response entity
+matches the regex "^[0-9a-f]{4}#".  If this test fails, clients
+MUST NOT continue.
+
+Clients MUST parse the entire response as a sequence of pkt-line
+records.
+
+Clients MUST verify the first pkt-line is "# service=$servicename".
+Servers MUST set $servicename to be the request parameter value.
+Servers SHOULD include an LF at the end of this line.
+Clients MUST ignore an LF at the end of the line.
+
+Servers MUST terminate the response with the magic "0000" end
+pkt-line marker.
+
+The returned response is a pkt-line stream describing each ref and
+its known value.  The stream SHOULD be sorted by name according to
+the C locale ordering.  The stream SHOULD include the default ref
+named 'HEAD' as the first ref.  The stream MUST include capability
+declarations behind a NUL on the first ref.
+
+  smart_reply     =  PKT-LINE("# service=$servicename" LF)
+                    ref_list
+                    "0000"
+  ref_list        =  empty_list / non_empty_list
+
+  empty_list      =  PKT-LINE(zero-id SP "capabilities^{}" NUL cap-list LF)
+
+  non_empty_list  =  PKT-LINE(obj-id SP name NUL cap_list LF)
+                    *ref_record
+
+  cap-list        =  capability *(SP capability)
+  capability      =  1*(LC_ALPHA / DIGIT / "-" / "_")
+  LC_ALPHA        =  %x61-7A
+
+  ref_record      =  any_ref / peeled_ref
+  any_ref         =  PKT-LINE(obj-id SP name LF)
+  peeled_ref      =  PKT-LINE(obj-id SP name LF)
+                    PKT-LINE(obj-id SP name "^{}" LF
+
+Smart Service git-upload-pack
+------------------------------
+This service reads from the repository pointed to by $GIT_URL.
+
+Clients MUST first perform ref discovery with
+'$GIT_URL/info/refs?service=git-upload-pack'.
+
+   C: POST $GIT_URL/git-upload-pack HTTP/1.0
+   C: Content-Type: application/x-git-upload-pack-request
+   C:
+   C: 0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n
+   C: 0032have 441b40d833fdfa93eb2908e52742248faf0ee993\n
+   C: 0000
+
+   S: 200 OK
+   S: Content-Type: application/x-git-upload-pack-result
+   S: Cache-Control: no-cache
+   S:
+   S: ....ACK %s, continue
+   S: ....NAK
+
+Clients MUST NOT reuse or revalidate a cached reponse.
+Servers MUST include sufficient Cache-Control headers
+to prevent caching of the response.
+
+Servers SHOULD support all capabilities defined here.
+
+Clients MUST send at least one 'want' command in the request body.
+Clients MUST NOT reference an id in a 'want' command which did not
+appear in the response obtained through ref discovery unless the
+server advertises capability "allow-tip-sha1-in-want".
+
+  compute_request   =  want_list
+                      have_list
+                      request_end
+  request_end       =  "0000" / "done"
+
+  want_list         =  PKT-LINE(want NUL cap_list LF)
+                      *(want_pkt)
+  want_pkt          =  PKT-LINE(want LF)
+  want              =  "want" SP id
+  cap_list          =  *(SP capability) SP
+
+  have_list         =  *PKT-LINE("have" SP id LF)
+
+TODO: Document this further.
+TODO: Don't use uppercase for variable names below.
+
+The Negotiation Algorithm
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The computation to select the minimal pack proceeds as follows
+(c = client, s = server):
+
+ init step:
+ (c) Use ref discovery to obtain the advertised refs.
+ (c) Place any object seen into set ADVERTISED.
+
+ (c) Build an empty set, COMMON, to hold the objects that are later
+     determined to be on both ends.
+ (c) Build a set, WANT, of the objects from ADVERTISED the client
+     wants to fetch, based on what it saw during ref discovery.
+
+ (c) Start a queue, C_PENDING, ordered by commit time (popping newest
+     first).  Add all client refs.  When a commit is popped from
+     the queue its parents SHOULD be automatically inserted back.
+     Commits MUST only enter the queue once.
+
+ one compute step:
+ (c) Send one $GIT_URL/git-upload-pack request:
+
+   C: 0032want <WANT #1>...............................
+   C: 0032want <WANT #2>...............................
+   ....
+   C: 0032have <COMMON #1>.............................
+   C: 0032have <COMMON #2>.............................
+   ....
+   C: 0032have <HAVE #1>...............................
+   C: 0032have <HAVE #2>...............................
+   ....
+   C: 0000
+
+     The stream is organized into "commands", with each command
+     appearing by itself in a pkt-line.  Within a command line
+     the text leading up to the first space is the command name,
+     and the remainder of the line to the first LF is the value.
+     Command lines are terminated with an LF as the last byte of
+     the pkt-line value.
+
+     Commands MUST appear in the following order, if they appear
+     at all in the request stream:
+
+       * want
+       * have
+
+     The stream is terminated by a pkt-line flush ("0000").
+
+     A single "want" or "have" command MUST have one hex formatted
+     SHA-1 as its value.  Multiple SHA-1s MUST be sent by sending
+     multiple commands.
+
+     The HAVE list is created by popping the first 32 commits
+     from C_PENDING.  Less can be supplied if C_PENDING empties.
+
+     If the client has sent 256 HAVE commits and has not yet
+     received one of those back from S_COMMON, or the client has
+     emptied C_PENDING it SHOULD include a "done" command to let
+     the server know it won't proceed:
+
+   C: 0009done
+
+  (s) Parse the git-upload-pack request:
+
+      Verify all objects in WANT are directly reachable from refs.
+
+      The server MAY walk backwards through history or through
+      the reflog to permit slightly stale requests.
+
+      If no WANT objects are received, send an error:
+
+TODO: Define error if no want lines are requested.
+
+      If any WANT object is not reachable, send an error:
+
+TODO: Define error if an invalid want is requested.
+
+     Create an empty list, S_COMMON.
+
+     If 'have' was sent:
+
+     Loop through the objects in the order supplied by the client.
+     For each object, if the server has the object reachable from
+     a ref, add it to S_COMMON.  If a commit is added to S_COMMON,
+     do not add any ancestors, even if they also appear in HAVE.
+
+  (s) Send the git-upload-pack response:
+
+     If the server has found a closed set of objects to pack or the
+     request ends with "done", it replies with the pack.
+
+TODO: Document the pack based response
+   S: PACK...
+
+     The returned stream is the side-band-64k protocol supported
+     by the git-upload-pack service, and the pack is embedded into
+     stream 1.  Progress messages from the server side MAY appear
+     in stream 2.
+
+     Here a "closed set of objects" is defined to have at least
+     one path from every WANT to at least one COMMON object.
+
+     If the server needs more information, it replies with a
+     status continue response:
+
+TODO: Document the non-pack response
+
+  (c) Parse the upload-pack response:
+
+TODO: Document parsing response
+
+      Do another compute step.
+
+
+Smart Service git-receive-pack
+------------------------------
+This service reads from the repository pointed to by $GIT_URL.
+
+Clients MUST first perform ref discovery with
+'$GIT_URL/info/refs?service=git-receive-pack'.
+
+   C: POST $GIT_URL/git-receive-pack HTTP/1.0
+   C: Content-Type: application/x-git-receive-pack-request
+   C:
+   C: ....0a53e9ddeaddad63ad106860237bbf53411d11a7 441b40d833fdfa93eb2908e52742248faf0ee993 refs/heads/maint\0 report-status
+   C: 0000
+   C: PACK....
+
+   S: 200 OK
+   S: Content-Type: application/x-git-receive-pack-result
+   S: Cache-Control: no-cache
+   S:
+   S: ....
+
+Clients MUST NOT reuse or revalidate a cached reponse.
+Servers MUST include sufficient Cache-Control headers
+to prevent caching of the response.
+
+Servers SHOULD support all capabilities defined here.
+
+Clients MUST send at least one command in the request body.
+Within the command portion of the request body clients SHOULD send
+the id obtained through ref discovery as old_id.
+
+  update_request  =  command_list
+                    "PACK" <binary data>
+
+  command_list    =  PKT-LINE(command NUL cap_list LF)
+                    *(command_pkt)
+  command_pkt     =  PKT-LINE(command LF)
+  cap_list        =  *(SP capability) SP
+
+  command         =  create / delete / update
+  create          =  zero-id SP new_id SP name
+  delete          =  old_id SP zero-id SP name
+  update          =  old_id SP new_id SP name
+
+TODO: Document this further.
+
+
+References
+----------
+
+link:http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
+link:http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
+link:technical/pack-protocol.txt
+link:technical/protocol-capabilities.txt
index 8b7ae1c140bc9d6d5fe01ea30dfbef0d6eb7ca8e..b7bd95152ea2526cf000911d81b02fe46539816f 100644 (file)
@@ -366,12 +366,6 @@ been detailed!
 
     <linus> Yes, we always write out most recent first
 
-For the other record:
-
-    <pasky> njs`: http://pastebin.com/547965
-
-The 'net never forgets, so that should be good until the end of time.
-
     <njs`> And, yeah, I got the part about deeper-in-history stuff
         having worse IO characteristics, one sort of doesn't care.
 
index 06026eaed7b4eadca91f1e8cd221cfda86a8fd87..b444c18f176cb33a80a1378586d6fa54bccd112a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.4
+DEF_VER=v1.8.4.GIT
 
 LF='
 '
index 3588ca1b6a575e8bb732dcc04b7c59dcb7f15cf4..e2abb7b4ea3d3d02dc6e5a8f67516ad1069e31a4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -69,9 +69,6 @@ all::
 # Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
 # doesn't support GNU extensions like --check and --statistics
 #
-# Define NEEDS_CLIPPED_WRITE if your write(2) cannot write more than
-# INT_MAX bytes at once (e.g. MacOS X).
-#
 # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
 # it specifies.
 #
@@ -580,6 +577,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-string-list
 TEST_PROGRAMS_NEED_X += test-subprocess
 TEST_PROGRAMS_NEED_X += test-svn-fe
+TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
 TEST_PROGRAMS_NEED_X += test-wildmatch
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
@@ -736,6 +734,7 @@ LIB_H += tree-walk.h
 LIB_H += tree.h
 LIB_H += unpack-trees.h
 LIB_H += url.h
+LIB_H += urlmatch.h
 LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += varint.h
@@ -886,6 +885,7 @@ LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += url.o
+LIB_OBJS += urlmatch.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
@@ -1182,6 +1182,9 @@ ifdef NEEDS_SSL_WITH_CRYPTO
 else
        LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto
 endif
+ifdef APPLE_COMMON_CRYPTO
+       LIB_4_CRYPTO += -framework Security -framework CoreFoundation
+endif
 endif
 ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
@@ -1493,11 +1496,6 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS
        MSGFMT += --check --statistics
 endif
 
-ifdef NEEDS_CLIPPED_WRITE
-       BASIC_CFLAGS += -DNEEDS_CLIPPED_WRITE
-       COMPAT_OBJS += compat/clipped-write.o
-endif
-
 ifneq (,$(XDL_FAST_HASH))
        BASIC_CFLAGS += -DXDL_FAST_HASH
 endif
index fce99fb79d16cbeb717ddbe36c7be231a00e4eba..61c3b54134d985cef6071014604d5b0499712df5 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.8.4.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.5.txt
\ No newline at end of file
index d254fa5d5cc32b0254dc863d015bc3d97dc59681..99fadc88d04ea077e5e5d6e6c065f7c54a4eddaf 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -151,7 +151,6 @@ int write_archive_entries(struct archiver_args *args,
        struct archiver_context context;
        struct unpack_trees_options opts;
        struct tree_desc t;
-       struct pathspec pathspec;
        int err;
 
        if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
@@ -186,10 +185,8 @@ int write_archive_entries(struct archiver_args *args,
                git_attr_set_direction(GIT_ATTR_INDEX, &the_index);
        }
 
-       init_pathspec(&pathspec, args->pathspec);
-       err = read_tree_recursive(args->tree, "", 0, 0, &pathspec,
+       err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
                                  write_archive_entry, &context);
-       free_pathspec(&pathspec);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
        return err;
@@ -222,7 +219,7 @@ static int path_exists(struct tree *tree, const char *path)
        struct pathspec pathspec;
        int ret;
 
-       init_pathspec(&pathspec, paths);
+       parse_pathspec(&pathspec, 0, 0, "", paths);
        ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL);
        free_pathspec(&pathspec);
        return ret != 0;
@@ -231,11 +228,18 @@ static int path_exists(struct tree *tree, const char *path)
 static void parse_pathspec_arg(const char **pathspec,
                struct archiver_args *ar_args)
 {
-       ar_args->pathspec = pathspec = get_pathspec("", pathspec);
+       /*
+        * must be consistent with parse_pathspec in path_exists()
+        * Also if pathspec patterns are dependent, we're in big
+        * trouble as we test each one separately
+        */
+       parse_pathspec(&ar_args->pathspec, 0,
+                      PATHSPEC_PREFER_FULL,
+                      "", pathspec);
        if (pathspec) {
                while (*pathspec) {
                        if (**pathspec && !path_exists(ar_args->tree, *pathspec))
-                               die("path not found: %s", *pathspec);
+                               die(_("pathspec '%s' did not match any files"), *pathspec);
                        pathspec++;
                }
        }
index 895afcdc7a00c5f9f16a21f2c9fe361a3e218d37..4a791e1fed4ecb0fc89b309b294acc93793886f0 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -1,6 +1,8 @@
 #ifndef ARCHIVE_H
 #define ARCHIVE_H
 
+#include "pathspec.h"
+
 struct archiver_args {
        const char *base;
        size_t baselen;
@@ -8,7 +10,7 @@ struct archiver_args {
        const unsigned char *commit_sha1;
        const struct commit *commit;
        time_t time;
-       const char **pathspec;
+       struct pathspec pathspec;
        unsigned int verbose : 1;
        unsigned int worktree_attributes : 1;
        unsigned int convert : 1;
index 8266a9cb7091c046f63c7b0137daf0bce0180a12..31ddabd99472a099ccc34810a9821050883bc1fa 100644 (file)
@@ -166,14 +166,16 @@ static void update_callback(struct diff_queue_struct *q,
        }
 }
 
-static void update_files_in_cache(const char *prefix, const char **pathspec,
+static void update_files_in_cache(const char *prefix,
+                                 const struct pathspec *pathspec,
                                  struct update_callback_data *data)
 {
        struct rev_info rev;
 
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
-       init_pathspec(&rev.prune_data, pathspec);
+       if (pathspec)
+               copy_pathspec(&rev.prune_data, pathspec);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = data;
@@ -181,7 +183,8 @@ static void update_files_in_cache(const char *prefix, const char **pathspec,
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 }
 
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+int add_files_to_cache(const char *prefix,
+                      const struct pathspec *pathspec, int flags)
 {
        struct update_callback_data data;
 
@@ -192,23 +195,21 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
 }
 
 #define WARN_IMPLICIT_DOT (1u << 0)
-static char *prune_directory(struct dir_struct *dir, const char **pathspec,
+static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
                             int prefix, unsigned flag)
 {
        char *seen;
-       int i, specs;
+       int i;
        struct dir_entry **src, **dst;
 
-       for (specs = 0; pathspec[specs];  specs++)
-               /* nothing */;
-       seen = xcalloc(specs, 1);
+       seen = xcalloc(pathspec->nr, 1);
 
        src = dst = dir->entries;
        i = dir->nr;
        while (--i >= 0) {
                struct dir_entry *entry = *src++;
-               if (match_pathspec(pathspec, entry->name, entry->len,
-                                  prefix, seen))
+               if (match_pathspec_depth(pathspec, entry->name, entry->len,
+                                        prefix, seen))
                        *dst++ = entry;
                else if (flag & WARN_IMPLICIT_DOT)
                        /*
@@ -222,72 +223,33 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec,
                        warn_pathless_add();
        }
        dir->nr = dst - dir->entries;
-       add_pathspec_matches_against_index(pathspec, seen, specs);
+       add_pathspec_matches_against_index(pathspec, seen);
        return seen;
 }
 
-/*
- * Checks the index to see whether any path in pathspec refers to
- * something inside a submodule.  If so, dies with an error message.
- */
-static void treat_gitlinks(const char **pathspec)
-{
-       int i;
-
-       if (!pathspec || !*pathspec)
-               return;
-
-       for (i = 0; pathspec[i]; i++)
-               pathspec[i] = check_path_for_gitlink(pathspec[i]);
-}
-
-static void refresh(int verbose, const char **pathspec)
+static void refresh(int verbose, const struct pathspec *pathspec)
 {
        char *seen;
-       int i, specs;
+       int i;
 
-       for (specs = 0; pathspec[specs];  specs++)
-               /* nothing */;
-       seen = xcalloc(specs, 1);
+       seen = xcalloc(pathspec->nr, 1);
        refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
                      pathspec, seen, _("Unstaged changes after refreshing the index:"));
-       for (i = 0; i < specs; i++) {
+       for (i = 0; i < pathspec->nr; i++) {
                if (!seen[i])
-                       die(_("pathspec '%s' did not match any files"), pathspec[i]);
+                       die(_("pathspec '%s' did not match any files"),
+                           pathspec->items[i].match);
        }
         free(seen);
 }
 
-/*
- * Normalizes argv relative to prefix, via get_pathspec(), and then
- * runs die_if_path_beyond_symlink() on each path in the normalized
- * list.
- */
-static const char **validate_pathspec(const char **argv, const char *prefix)
-{
-       const char **pathspec = get_pathspec(prefix, argv);
-
-       if (pathspec) {
-               const char **p;
-               for (p = pathspec; *p; p++) {
-                       die_if_path_beyond_symlink(*p, prefix);
-               }
-       }
-
-       return pathspec;
-}
-
 int run_add_interactive(const char *revision, const char *patch_mode,
-                       const char **pathspec)
+                       const struct pathspec *pathspec)
 {
-       int status, ac, pc = 0;
+       int status, ac, i;
        const char **args;
 
-       if (pathspec)
-               while (pathspec[pc])
-                       pc++;
-
-       args = xcalloc(sizeof(const char *), (pc + 5));
+       args = xcalloc(sizeof(const char *), (pathspec->nr + 6));
        ac = 0;
        args[ac++] = "add--interactive";
        if (patch_mode)
@@ -295,11 +257,9 @@ int run_add_interactive(const char *revision, const char *patch_mode,
        if (revision)
                args[ac++] = revision;
        args[ac++] = "--";
-       if (pc) {
-               memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
-               ac += pc;
-       }
-       args[ac] = NULL;
+       for (i = 0; i < pathspec->nr; i++)
+               /* pass original pathspec, to be re-parsed */
+               args[ac++] = pathspec->items[i].original;
 
        status = run_command_v_opt(args, RUN_GIT_CMD);
        free(args);
@@ -308,17 +268,17 @@ int run_add_interactive(const char *revision, const char *patch_mode,
 
 int interactive_add(int argc, const char **argv, const char *prefix, int patch)
 {
-       const char **pathspec = NULL;
+       struct pathspec pathspec;
 
-       if (argc) {
-               pathspec = validate_pathspec(argv, prefix);
-               if (!pathspec)
-                       return -1;
-       }
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_FULL |
+                      PATHSPEC_SYMLINK_LEADING_PATH |
+                      PATHSPEC_PREFIX_ORIGIN,
+                      prefix, argv);
 
        return run_add_interactive(NULL,
                                   patch ? "--patch" : NULL,
-                                  pathspec);
+                                  &pathspec);
 }
 
 static int edit_patch(int argc, const char **argv, const char *prefix)
@@ -446,7 +406,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 {
        int exit_status = 0;
        int newfd;
-       const char **pathspec;
+       struct pathspec pathspec;
        struct dir_struct dir;
        int flags;
        int add_new_files;
@@ -527,14 +487,23 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
                return 0;
        }
-       pathspec = validate_pathspec(argv, prefix);
 
        if (read_cache() < 0)
                die(_("index file corrupt"));
-       treat_gitlinks(pathspec);
+
+       /*
+        * Check the "pathspec '%s' did not match any files" block
+        * below before enabling new magic.
+        */
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_FULL |
+                      PATHSPEC_SYMLINK_LEADING_PATH |
+                      PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE,
+                      prefix, argv);
 
        if (add_new_files) {
                int baselen;
+               struct pathspec empty_pathspec;
 
                /* Set up the default git porcelain excludes */
                memset(&dir, 0, sizeof(dir));
@@ -543,35 +512,49 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        setup_standard_excludes(&dir);
                }
 
+               memset(&empty_pathspec, 0, sizeof(empty_pathspec));
                /* This picks up the paths that are not tracked */
-               baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
-               if (pathspec)
-                       seen = prune_directory(&dir, pathspec, baselen,
+               baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec);
+               if (pathspec.nr)
+                       seen = prune_directory(&dir, &pathspec, baselen,
                                        implicit_dot ? WARN_IMPLICIT_DOT : 0);
        }
 
        if (refresh_only) {
-               refresh(verbose, pathspec);
+               refresh(verbose, &pathspec);
                goto finish;
        }
        if (implicit_dot && prefix)
                refresh_cache(REFRESH_QUIET);
 
-       if (pathspec) {
+       if (pathspec.nr) {
                int i;
 
                if (!seen)
-                       seen = find_pathspecs_matching_against_index(pathspec);
-               for (i = 0; pathspec[i]; i++) {
-                       if (!seen[i] && pathspec[i][0]
-                           && !file_exists(pathspec[i])) {
+                       seen = find_pathspecs_matching_against_index(&pathspec);
+
+               /*
+                * file_exists() assumes exact match
+                */
+               GUARD_PATHSPEC(&pathspec,
+                              PATHSPEC_FROMTOP |
+                              PATHSPEC_LITERAL |
+                              PATHSPEC_GLOB |
+                              PATHSPEC_ICASE);
+
+               for (i = 0; i < pathspec.nr; i++) {
+                       const char *path = pathspec.items[i].match;
+                       if (!seen[i] &&
+                           ((pathspec.items[i].magic &
+                             (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
+                            !file_exists(path))) {
                                if (ignore_missing) {
                                        int dtype = DT_UNKNOWN;
-                                       if (is_excluded(&dir, pathspec[i], &dtype))
-                                               dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
+                                       if (is_excluded(&dir, path, &dtype))
+                                               dir_add_ignored(&dir, path, pathspec.items[i].len);
                                } else
                                        die(_("pathspec '%s' did not match any files"),
-                                           pathspec[i]);
+                                           pathspec.items[i].original);
                        }
                }
                free(seen);
@@ -587,10 +570,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                 */
                update_data.implicit_dot = prefix;
                update_data.implicit_dot_len = strlen(prefix);
-               pathspec = NULL;
+               free_pathspec(&pathspec);
+               memset(&pathspec, 0, sizeof(pathspec));
        }
        update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
-       update_files_in_cache(prefix, pathspec, &update_data);
+       update_files_in_cache(prefix, &pathspec, &update_data);
 
        exit_status |= !!update_data.add_errors;
        if (add_new_files)
index 50912c928f79cc0bcb4acf3ae50fd9fdc32193e8..ef32e4f62415956aa8e2366589d4cd6200d3bf37 100644 (file)
@@ -4363,23 +4363,23 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
                        N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
-               OPT_BOOLEAN(0, "no-add", &no_add,
+               OPT_BOOL(0, "no-add", &no_add,
                        N_("ignore additions made by the patch")),
-               OPT_BOOLEAN(0, "stat", &diffstat,
+               OPT_BOOL(0, "stat", &diffstat,
                        N_("instead of applying the patch, output diffstat for the input")),
                OPT_NOOP_NOARG(0, "allow-binary-replacement"),
                OPT_NOOP_NOARG(0, "binary"),
-               OPT_BOOLEAN(0, "numstat", &numstat,
+               OPT_BOOL(0, "numstat", &numstat,
                        N_("show number of added and deleted lines in decimal notation")),
-               OPT_BOOLEAN(0, "summary", &summary,
+               OPT_BOOL(0, "summary", &summary,
                        N_("instead of applying the patch, output a summary for the input")),
-               OPT_BOOLEAN(0, "check", &check,
+               OPT_BOOL(0, "check", &check,
                        N_("instead of applying the patch, see if the patch is applicable")),
-               OPT_BOOLEAN(0, "index", &check_index,
+               OPT_BOOL(0, "index", &check_index,
                        N_("make sure the patch is applicable to the current index")),
-               OPT_BOOLEAN(0, "cached", &cached,
+               OPT_BOOL(0, "cached", &cached,
                        N_("apply a patch without touching the working tree")),
-               OPT_BOOLEAN(0, "apply", &force_apply,
+               OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &threeway,
                         N_( "attempt three-way merge if a patch does not apply")),
@@ -4399,13 +4399,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
                        N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
-               OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
+               OPT_BOOL('R', "reverse", &apply_in_reverse,
                        N_("apply the patch in reverse")),
-               OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
+               OPT_BOOL(0, "unidiff-zero", &unidiff_zero,
                        N_("don't expect at least one line of context")),
-               OPT_BOOLEAN(0, "reject", &apply_with_reject,
+               OPT_BOOL(0, "reject", &apply_with_reject,
                        N_("leave the rejected hunks in corresponding *.rej files")),
-               OPT_BOOLEAN(0, "allow-overlap", &allow_overlap,
+               OPT_BOOL(0, "allow-overlap", &allow_overlap,
                        N_("allow overlapping hunks")),
                OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
                OPT_BIT(0, "inaccurate-eof", &options,
index e3884e3bb61c7dfb0cd97ddba7bd0558b44c6b8f..3324229025300f7d7de600753c03af3ac22e2152 100644 (file)
@@ -13,10 +13,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
        int next_all = 0;
        int no_checkout = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "next-all", &next_all,
-                           N_("perform 'git bisect next'")),
-               OPT_BOOLEAN(0, "no-checkout", &no_checkout,
-                           N_("update BISECT_HEAD instead of checking out the current commit")),
+               OPT_BOOL(0, "next-all", &next_all,
+                        N_("perform 'git bisect next'")),
+               OPT_BOOL(0, "no-checkout", &no_checkout,
+                        N_("update BISECT_HEAD instead of checking out the current commit")),
                OPT_END()
        };
 
index 079dcd3407881ce0ea988c3f487faf8ca6d3e5d2..6da7233968b6563d85d0689e3b1cae3ebe4027fb 100644 (file)
@@ -22,6 +22,7 @@
 #include "utf8.h"
 #include "userdiff.h"
 #include "line-range.h"
+#include "line-log.h"
 
 static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
 
@@ -408,7 +409,7 @@ static struct origin *find_origin(struct scoreboard *sb,
        paths[0] = origin->path;
        paths[1] = NULL;
 
-       diff_tree_setup_paths(paths, &diff_opts);
+       parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC, 0, "", paths);
        diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
@@ -458,7 +459,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-       diff_tree_release_paths(&diff_opts);
+       free_pathspec(&diff_opts.pathspec);
        if (porigin) {
                /*
                 * Create a freestanding copy that is not part of
@@ -486,15 +487,12 @@ static struct origin *find_rename(struct scoreboard *sb,
        struct origin *porigin = NULL;
        struct diff_options diff_opts;
        int i;
-       const char *paths[2];
 
        diff_setup(&diff_opts);
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = origin->path;
-       paths[0] = NULL;
-       diff_tree_setup_paths(paths, &diff_opts);
        diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
@@ -516,7 +514,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-       diff_tree_release_paths(&diff_opts);
+       free_pathspec(&diff_opts.pathspec);
        return porigin;
 }
 
@@ -1064,7 +1062,6 @@ static int find_copy_in_parent(struct scoreboard *sb,
                               int opt)
 {
        struct diff_options diff_opts;
-       const char *paths[1];
        int i, j;
        int retval;
        struct blame_list *blame_list;
@@ -1078,8 +1075,6 @@ static int find_copy_in_parent(struct scoreboard *sb,
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 
-       paths[0] = NULL;
-       diff_tree_setup_paths(paths, &diff_opts);
        diff_setup_done(&diff_opts);
 
        /* Try "find copies harder" on new path if requested;
@@ -1162,7 +1157,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
        }
        reset_scanned_flag(sb);
        diff_flush(&diff_opts);
-       diff_tree_release_paths(&diff_opts);
+       free_pathspec(&diff_opts.pathspec);
        return retval;
 }
 
@@ -1937,18 +1932,6 @@ static const char *add_prefix(const char *prefix, const char *path)
        return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-/*
- * Parsing of -L option
- */
-static void prepare_blame_range(struct scoreboard *sb,
-                               const char *bottomtop,
-                               long lno,
-                               long *bottom, long *top)
-{
-       if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path))
-               usage(blame_usage);
-}
-
 static int git_blame_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "blame.showroot")) {
@@ -2245,38 +2228,27 @@ static int blame_move_callback(const struct option *option, const char *arg, int
        return 0;
 }
 
-static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
-{
-       const char **bottomtop = option->value;
-       if (!arg)
-               return -1;
-       if (*bottomtop)
-               die("More than one '-L n,m' option given");
-       *bottomtop = arg;
-       return 0;
-}
-
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        const char *path;
        struct scoreboard sb;
        struct origin *o;
-       struct blame_entry *ent;
-       long dashdash_pos, bottom, top, lno;
+       struct blame_entry *ent = NULL;
+       long dashdash_pos, lno;
        const char *final_commit_name = NULL;
        enum object_type type;
 
-       static const char *bottomtop = NULL;
+       static struct string_list range_list;
        static int output_option = 0, opt = 0;
        static int show_stats = 0;
        static const char *revs_file = NULL;
        static const char *contents_from = NULL;
        static const struct option options[] = {
-               OPT_BOOLEAN(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
-               OPT_BOOLEAN('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
-               OPT_BOOLEAN(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
-               OPT_BOOLEAN(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+               OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
+               OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
+               OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
+               OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
                OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
                OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
                OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
@@ -2293,13 +2265,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
                { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback },
                { OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback },
-               OPT_CALLBACK('L', NULL, &bottomtop, N_("n,m"), N_("Process only line range n,m, counting from 1"), blame_bottomtop_callback),
+               OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")),
                OPT__ABBREV(&abbrev),
                OPT_END()
        };
 
        struct parse_opt_ctx_t ctx;
        int cmd_is_annotate = !strcmp(argv[0], "annotate");
+       struct range_set ranges;
+       unsigned int range_i;
+       long anchor;
 
        git_config(git_blame_config, NULL);
        init_revisions(&revs, NULL);
@@ -2492,22 +2467,48 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        num_read_blob++;
        lno = prepare_lines(&sb);
 
-       bottom = top = 0;
-       if (bottomtop)
-               prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
-       if (bottom < 1)
-               bottom = 1;
-       if (top < 1)
-               top = lno;
-       bottom--;
-       if (lno < top || lno < bottom)
-               die("file %s has only %lu lines", path, lno);
-
-       ent = xcalloc(1, sizeof(*ent));
-       ent->lno = bottom;
-       ent->num_lines = top - bottom;
-       ent->suspect = o;
-       ent->s_lno = bottom;
+       if (lno && !range_list.nr)
+               string_list_append(&range_list, xstrdup("1"));
+
+       anchor = 1;
+       range_set_init(&ranges, range_list.nr);
+       for (range_i = 0; range_i < range_list.nr; ++range_i) {
+               long bottom, top;
+               if (parse_range_arg(range_list.items[range_i].string,
+                                   nth_line_cb, &sb, lno, anchor,
+                                   &bottom, &top, sb.path))
+                       usage(blame_usage);
+               if (lno < top || ((lno || bottom) && lno < bottom))
+                       die("file %s has only %lu lines", path, lno);
+               if (bottom < 1)
+                       bottom = 1;
+               if (top < 1)
+                       top = lno;
+               bottom--;
+               range_set_append_unsafe(&ranges, bottom, top);
+               anchor = top + 1;
+       }
+       sort_and_merge_range_set(&ranges);
+
+       for (range_i = ranges.nr; range_i > 0; --range_i) {
+               const struct range *r = &ranges.ranges[range_i - 1];
+               long bottom = r->start;
+               long top = r->end;
+               struct blame_entry *next = ent;
+               ent = xcalloc(1, sizeof(*ent));
+               ent->lno = bottom;
+               ent->num_lines = top - bottom;
+               ent->suspect = o;
+               ent->s_lno = bottom;
+               ent->next = next;
+               if (next)
+                       next->prev = ent;
+               origin_incref(o);
+       }
+       origin_decref(o);
+
+       range_set_release(&ranges);
+       string_list_clear(&range_list, 0);
 
        sb.ent = ent;
        sb.path = path;
index 083689063f66a614ad6f8ee4a84c79e214d1b6e0..0903763702a436dc8b3ffc89aa64b2598e257446 100644 (file)
@@ -797,7 +797,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
                OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
-               OPT_BOOLEAN(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
+               OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &kinds, N_("act on remote-tracking branches"),
                        REF_REMOTE_BRANCH),
@@ -822,10 +822,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
                OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
                OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
-               OPT_BOOLEAN(0, "list", &list, N_("list branch names")),
-               OPT_BOOLEAN('l', "create-reflog", &reflog, N_("create the branch's reflog")),
-               OPT_BOOLEAN(0, "edit-description", &edit_description,
-                           N_("edit the description for the branch")),
+               OPT_BOOL(0, "list", &list, N_("list branch names")),
+               OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
+               OPT_BOOL(0, "edit-description", &edit_description,
+                        N_("edit the description for the branch")),
                OPT__FORCE(&force_create, N_("force creation (when already exists)")),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
@@ -872,7 +872,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (with_commit || merge_filter != NO_FILTER)
                list = 1;
 
-       if (!!delete + !!rename + !!force_create + !!list + !!new_upstream + !!unset_upstream > 1)
+       if (!!delete + !!rename + !!force_create + !!new_upstream +
+           list + unset_upstream > 1)
                usage_with_options(builtin_branch_usage, options);
 
        if (abbrev == -1)
index 425346048bbba66874eb857985bed9937c44e1d6..41afaa534b02d8f8089973f3949ba507bd3cf94b 100644 (file)
@@ -119,6 +119,7 @@ struct expand_data {
        enum object_type type;
        unsigned long size;
        unsigned long disk_size;
+       const char *rest;
 
        /*
         * If mark_query is true, we do not expand anything, but rather
@@ -126,6 +127,13 @@ struct expand_data {
         */
        int mark_query;
 
+       /*
+        * Whether to split the input on whitespace before feeding it to
+        * get_sha1; this is decided during the mark_query phase based on
+        * whether we have a %(rest) token in our format.
+        */
+       int split_on_whitespace;
+
        /*
         * After a mark_query run, this object_info is set up to be
         * passed to sha1_object_info_extended. It will point to the data
@@ -163,6 +171,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                        data->info.disk_sizep = &data->disk_size;
                else
                        strbuf_addf(sb, "%lu", data->disk_size);
+       } else if (is_atom("rest", atom, len)) {
+               if (data->mark_query)
+                       data->split_on_whitespace = 1;
+               else if (data->rest)
+                       strbuf_addstr(sb, data->rest);
        } else
                die("unknown format element: %.*s", len, atom);
 }
@@ -273,7 +286,23 @@ static int batch_objects(struct batch_options *opt)
        warn_on_object_refname_ambiguity = 0;
 
        while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-               int error = batch_one_object(buf.buf, opt, &data);
+               int error;
+
+               if (data.split_on_whitespace) {
+                       /*
+                        * Split at first whitespace, tying off the beginning
+                        * of the string and saving the remainder (or NULL) in
+                        * data.rest.
+                        */
+                       char *p = strpbrk(buf.buf, " \t");
+                       if (p) {
+                               while (*p && strchr(" \t", *p))
+                                       *p++ = '\0';
+                       }
+                       data.rest = p;
+               }
+
+               error = batch_one_object(buf.buf, opt, &data);
                if (error)
                        return error;
        }
index 075d01d30c58d40abb48ff820ba6f8d310f785d0..e9af7b2bfb932d75cdface880be9814885068a50 100644 (file)
@@ -13,14 +13,14 @@ N_("git check-attr --stdin [-z] [-a | --all | attr...] < <list-of-paths>"),
 NULL
 };
 
-static int null_term_line;
+static int nul_term_line;
 
 static const struct option check_attr_options[] = {
-       OPT_BOOLEAN('a', "all", &all_attrs, N_("report all attributes set on file")),
-       OPT_BOOLEAN(0,  "cached", &cached_attrs, N_("use .gitattributes only from the index")),
-       OPT_BOOLEAN(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
-       OPT_BOOLEAN('z', NULL, &null_term_line,
-               N_("input paths are terminated by a null character")),
+       OPT_BOOL('a', "all", &all_attrs, N_("report all attributes set on file")),
+       OPT_BOOL(0,  "cached", &cached_attrs, N_("use .gitattributes only from the index")),
+       OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
+       OPT_BOOL('z', NULL, &nul_term_line,
+                N_("terminate input and output records by a NUL character")),
        OPT_END()
 };
 
@@ -38,8 +38,16 @@ static void output_attr(int cnt, struct git_attr_check *check,
                else if (ATTR_UNSET(value))
                        value = "unspecified";
 
-               quote_c_style(file, NULL, stdout, 0);
-               printf(": %s: %s\n", git_attr_name(check[j].attr), value);
+               if (nul_term_line) {
+                       printf("%s%c" /* path */
+                              "%s%c" /* attrname */
+                              "%s%c" /* attrvalue */,
+                              file, 0, git_attr_name(check[j].attr), 0, value, 0);
+               } else {
+                       quote_c_style(file, NULL, stdout, 0);
+                       printf(": %s: %s\n", git_attr_name(check[j].attr), value);
+               }
+
        }
 }
 
@@ -65,7 +73,7 @@ static void check_attr_stdin_paths(const char *prefix, int cnt,
        struct git_attr_check *check)
 {
        struct strbuf buf, nbuf;
-       int line_termination = null_term_line ? 0 : '\n';
+       int line_termination = nul_term_line ? 0 : '\n';
 
        strbuf_init(&buf, 0);
        strbuf_init(&nbuf, 0);
index 4a8fc707c747596e31dcc6f57abf5f965cdf612f..e2a1cef6843b7db2f7ffb4b9c89003397948c748 100644 (file)
@@ -12,18 +12,18 @@ static const char * const check_ignore_usage[] = {
 NULL
 };
 
-static int null_term_line;
+static int nul_term_line;
 
 static const struct option check_ignore_options[] = {
        OPT__QUIET(&quiet, N_("suppress progress reporting")),
        OPT__VERBOSE(&verbose, N_("be verbose")),
        OPT_GROUP(""),
-       OPT_BOOLEAN(0, "stdin", &stdin_paths,
-                   N_("read file names from stdin")),
-       OPT_BOOLEAN('z', NULL, &null_term_line,
-                   N_("input paths are terminated by a null character")),
-       OPT_BOOLEAN('n', "non-matching", &show_non_matching,
-                   N_("show non-matching input paths")),
+       OPT_BOOL(0, "stdin", &stdin_paths,
+                N_("read file names from stdin")),
+       OPT_BOOL('z', NULL, &nul_term_line,
+                N_("terminate input and output records by a NUL character")),
+       OPT_BOOL('n', "non-matching", &show_non_matching,
+                N_("show non-matching input paths")),
        OPT_END()
 };
 
@@ -31,7 +31,7 @@ static void output_exclude(const char *path, struct exclude *exclude)
 {
        char *bang  = (exclude && exclude->flags & EXC_FLAG_NEGATIVE)  ? "!" : "";
        char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
-       if (!null_term_line) {
+       if (!nul_term_line) {
                if (!verbose) {
                        write_name_quoted(path, stdout, '\n');
                } else {
@@ -64,37 +64,45 @@ static void output_exclude(const char *path, struct exclude *exclude)
 }
 
 static int check_ignore(struct dir_struct *dir,
-                       const char *prefix, const char **pathspec)
+                       const char *prefix, int argc, const char **argv)
 {
-       const char *path, *full_path;
+       const char *full_path;
        char *seen;
        int num_ignored = 0, dtype = DT_UNKNOWN, i;
        struct exclude *exclude;
+       struct pathspec pathspec;
 
-       if (!pathspec || !*pathspec) {
+       if (!argc) {
                if (!quiet)
                        fprintf(stderr, "no pathspec given.\n");
                return 0;
        }
 
+       /*
+        * check-ignore just needs paths. Magic beyond :/ is really
+        * irrelevant.
+        */
+       parse_pathspec(&pathspec,
+                      PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
+                      PATHSPEC_SYMLINK_LEADING_PATH |
+                      PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE |
+                      PATHSPEC_KEEP_ORDER,
+                      prefix, argv);
+
        /*
         * look for pathspecs matching entries in the index, since these
         * should not be ignored, in order to be consistent with
         * 'git status', 'git add' etc.
         */
-       seen = find_pathspecs_matching_against_index(pathspec);
-       for (i = 0; pathspec[i]; i++) {
-               path = pathspec[i];
-               full_path = prefix_path(prefix, prefix
-                                       ? strlen(prefix) : 0, path);
-               full_path = check_path_for_gitlink(full_path);
-               die_if_path_beyond_symlink(full_path, prefix);
+       seen = find_pathspecs_matching_against_index(&pathspec);
+       for (i = 0; i < pathspec.nr; i++) {
+               full_path = pathspec.items[i].match;
                exclude = NULL;
                if (!seen[i]) {
                        exclude = last_exclude_matching(dir, full_path, &dtype);
                }
                if (!quiet && (exclude || show_non_matching))
-                       output_exclude(path, exclude);
+                       output_exclude(pathspec.items[i].original, exclude);
                if (exclude)
                        num_ignored++;
        }
@@ -107,7 +115,7 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
 {
        struct strbuf buf, nbuf;
        char *pathspec[2] = { NULL, NULL };
-       int line_termination = null_term_line ? 0 : '\n';
+       int line_termination = nul_term_line ? 0 : '\n';
        int num_ignored = 0;
 
        strbuf_init(&buf, 0);
@@ -120,7 +128,8 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
                        strbuf_swap(&buf, &nbuf);
                }
                pathspec[0] = buf.buf;
-               num_ignored += check_ignore(dir, prefix, (const char **)pathspec);
+               num_ignored += check_ignore(dir, prefix,
+                                           1, (const char **)pathspec);
                maybe_flush_or_die(stdout, "check-ignore to stdout");
        }
        strbuf_release(&buf);
@@ -142,7 +151,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
                if (argc > 0)
                        die(_("cannot specify pathnames with --stdin"));
        } else {
-               if (null_term_line)
+               if (nul_term_line)
                        die(_("-z only makes sense with --stdin"));
                if (argc == 0)
                        die(_("no path specified"));
@@ -166,7 +175,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
        if (stdin_paths) {
                num_ignored = check_ignore_stdin_paths(&dir, prefix);
        } else {
-               num_ignored = check_ignore(&dir, prefix, argv);
+               num_ignored = check_ignore(&dir, prefix, argc, argv);
                maybe_flush_or_die(stdout, "ignore to stdout");
        }
 
index b1feda7d5ef34f45ed2de018a56149212b51ec0a..69e167b16c284534d84019ba0f781fe2c3a889dd 100644 (file)
@@ -183,12 +183,12 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        int prefix_length;
        int force = 0, quiet = 0, not_new = 0;
        struct option builtin_checkout_index_options[] = {
-               OPT_BOOLEAN('a', "all", &all,
+               OPT_BOOL('a', "all", &all,
                        N_("check out all files in the index")),
                OPT__FORCE(&force, N_("force overwrite of existing files")),
                OPT__QUIET(&quiet,
                        N_("no warning for existing files and files not in index")),
-               OPT_BOOLEAN('n', "no-create", &not_new,
+               OPT_BOOL('n', "no-create", &not_new,
                        N_("don't checkout new files")),
                { OPTION_CALLBACK, 'u', "index", &newfd, NULL,
                        N_("update stat information in the index file"),
@@ -196,9 +196,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
                        N_("paths are separated with NUL character"),
                        PARSE_OPT_NOARG, option_parse_z },
-               OPT_BOOLEAN(0, "stdin", &read_from_stdin,
+               OPT_BOOL(0, "stdin", &read_from_stdin,
                        N_("read list of paths from the standard input")),
-               OPT_BOOLEAN(0, "temp", &to_tempfile,
+               OPT_BOOL(0, "temp", &to_tempfile,
                        N_("write the content to temporary files")),
                OPT_CALLBACK(0, "prefix", NULL, N_("string"),
                        N_("when creating files, prepend <string>"),
index 7025938ae30f2ce3104b27f66bf6e1f294edb810..0f573970372664a16024e83ed9733c94eeee3b50 100644 (file)
@@ -46,7 +46,7 @@ struct checkout_opts {
 
        int branch_exists;
        const char *prefix;
-       const char **pathspec;
+       struct pathspec pathspec;
        struct tree *source_tree;
 };
 
@@ -83,12 +83,9 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        return 0;
 }
 
-static int read_tree_some(struct tree *tree, const char **pathspec)
+static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 {
-       struct pathspec ps;
-       init_pathspec(&ps, pathspec);
-       read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
-       free_pathspec(&ps);
+       read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
 
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@ -228,8 +225,6 @@ static int checkout_paths(const struct checkout_opts *opts,
        int flag;
        struct commit *head;
        int errs = 0;
-       int stage = opts->writeout_stage;
-       int merge = opts->merge;
        int newfd;
        struct lock_file *lock_file;
 
@@ -257,20 +252,18 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        if (opts->patch_mode)
                return run_add_interactive(revision, "--patch=checkout",
-                                          opts->pathspec);
+                                          &opts->pathspec);
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
        newfd = hold_locked_index(lock_file, 1);
-       if (read_cache_preload(opts->pathspec) < 0)
+       if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("corrupt index file"));
 
        if (opts->source_tree)
-               read_tree_some(opts->source_tree, opts->pathspec);
+               read_tree_some(opts->source_tree, &opts->pathspec);
 
-       for (pos = 0; opts->pathspec[pos]; pos++)
-               ;
-       ps_matched = xcalloc(1, pos);
+       ps_matched = xcalloc(1, opts->pathspec.nr);
 
        /*
         * Make sure all pathspecs participated in locating the paths
@@ -304,12 +297,12 @@ static int checkout_paths(const struct checkout_opts *opts,
                 * match_pathspec() for _all_ entries when
                 * opts->source_tree != NULL.
                 */
-               if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce),
+               if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce),
                                   0, ps_matched))
                        ce->ce_flags |= CE_MATCHED;
        }
 
-       if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) {
+       if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
                free(ps_matched);
                return 1;
        }
@@ -327,8 +320,8 @@ static int checkout_paths(const struct checkout_opts *opts,
                                continue;
                        if (opts->force) {
                                warning(_("path '%s' is unmerged"), ce->name);
-                       } else if (stage) {
-                               errs |= check_stage(stage, ce, pos);
+                       } else if (opts->writeout_stage) {
+                               errs |= check_stage(opts->writeout_stage, ce, pos);
                        } else if (opts->merge) {
                                errs |= check_stages((1<<2) | (1<<3), ce, pos);
                        } else {
@@ -352,9 +345,9 @@ static int checkout_paths(const struct checkout_opts *opts,
                                errs |= checkout_entry(ce, &state, NULL);
                                continue;
                        }
-                       if (stage)
-                               errs |= checkout_stage(stage, ce, pos, &state);
-                       else if (merge)
+                       if (opts->writeout_stage)
+                               errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
+                       else if (opts->merge)
                                errs |= checkout_merged(pos, &state);
                        pos = skip_same_name(ce, pos) - 1;
                }
@@ -1002,7 +995,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
 static int checkout_branch(struct checkout_opts *opts,
                           struct branch_info *new)
 {
-       if (opts->pathspec)
+       if (opts->pathspec.nr)
                die(_("paths cannot be used with switching branches"));
 
        if (opts->patch_mode)
@@ -1056,8 +1049,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                           N_("create and checkout a new branch")),
                OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
                           N_("create/reset and checkout a branch")),
-               OPT_BOOLEAN('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
-               OPT_BOOLEAN(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
+               OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+               OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")),
@@ -1066,16 +1059,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
                            3),
                OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")),
-               OPT_BOOLEAN('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-               OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
+               OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
+               OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
                OPT_STRING(0, "conflict", &conflict_style, N_("style"),
                           N_("conflict style (merge or diff3)")),
-               OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
+               OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
-               { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
-                 N_("second guess 'git checkout no-such-branch'"),
-                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+               OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
+                               N_("second guess 'git checkout no-such-branch'")),
                OPT_END(),
        };
 
@@ -1154,9 +1146,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        }
 
        if (argc) {
-               opts.pathspec = get_pathspec(prefix, argv);
+               parse_pathspec(&opts.pathspec, 0,
+                              opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
+                              prefix, argv);
 
-               if (!opts.pathspec)
+               if (!opts.pathspec.nr)
                        die(_("invalid path specification"));
 
                /*
@@ -1188,7 +1182,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        }
 
-       if (opts.patch_mode || opts.pathspec)
+       if (opts.patch_mode || opts.pathspec.nr)
                return checkout_paths(&opts, new.name);
        else
                return checkout_branch(&opts, &new);
index 3c85e152e140741855590f422c599407b6d6ae55..615cd57caf1d4cbeafc73e7037bae81a6915df59 100644 (file)
@@ -15,6 +15,7 @@
 #include "quote.h"
 #include "column.h"
 #include "color.h"
+#include "pathspec.h"
 
 static int force = -1; /* unset */
 static int interactive;
@@ -863,24 +864,23 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
        struct strbuf abs_path = STRBUF_INIT;
        struct dir_struct dir;
-       static const char **pathspec;
+       struct pathspec pathspec;
        struct strbuf buf = STRBUF_INIT;
        struct string_list exclude_list = STRING_LIST_INIT_NODUP;
        struct exclude_list *el;
        struct string_list_item *item;
        const char *qname;
-       char *seen = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet, N_("do not print names of files removed")),
                OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT__FORCE(&force, N_("force")),
                OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
-               OPT_BOOLEAN('d', NULL, &remove_directories,
+               OPT_BOOL('d', NULL, &remove_directories,
                                N_("remove whole directories")),
                { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
                  N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
-               OPT_BOOLEAN('x', NULL, &ignored, N_("remove ignored files, too")),
-               OPT_BOOLEAN('X', NULL, &ignored_only,
+               OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
+               OPT_BOOL('X', NULL, &ignored_only,
                                N_("remove only ignored files")),
                OPT_END()
        };
@@ -925,12 +925,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        for (i = 0; i < exclude_list.nr; i++)
                add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
 
-       pathspec = get_pathspec(prefix, argv);
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_CWD,
+                      prefix, argv);
 
-       fill_directory(&dir, pathspec);
-
-       if (pathspec)
-               seen = xmalloc(argc > 0 ? argc : 1);
+       fill_directory(&dir, &pathspec);
 
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
@@ -961,11 +960,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                if (lstat(ent->name, &st))
                        die_errno("Cannot lstat '%s'", ent->name);
 
-               if (pathspec) {
-                       memset(seen, 0, argc > 0 ? argc : 1);
-                       matches = match_pathspec(pathspec, ent->name, len,
-                                                0, seen);
-               }
+               if (pathspec.nr)
+                       matches = match_pathspec_depth(&pathspec, ent->name,
+                                                      len, 0, NULL);
 
                if (S_ISDIR(st.st_mode)) {
                        if (remove_directories || (matches == MATCHED_EXACTLY)) {
@@ -973,7 +970,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                string_list_append(&del_list, rel);
                        }
                } else {
-                       if (pathspec && !matches)
+                       if (pathspec.nr && !matches)
                                continue;
                        rel = relative_path(ent->name, prefix, &buf);
                        string_list_append(&del_list, rel);
@@ -1019,7 +1016,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                }
                strbuf_reset(&abs_path);
        }
-       free(seen);
 
        strbuf_release(&abs_path);
        strbuf_release(&buf);
index 430307b298b4f9ea03ff4caea283b133fde0077e..ca3eb68d72b0ab9ee683eff3206d28012f27c4fc 100644 (file)
@@ -62,23 +62,22 @@ static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
        OPT_BOOL(0, "progress", &option_progress,
                 N_("force progress reporting")),
-       OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
-                   N_("don't create a checkout")),
-       OPT_BOOLEAN(0, "bare", &option_bare, N_("create a bare repository")),
-       { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL,
-               N_("create a bare repository"),
-               PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       OPT_BOOLEAN(0, "mirror", &option_mirror,
-                   N_("create a mirror repository (implies bare)")),
+       OPT_BOOL('n', "no-checkout", &option_no_checkout,
+                N_("don't create a checkout")),
+       OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")),
+       OPT_HIDDEN_BOOL(0, "naked", &option_bare,
+                       N_("create a bare repository")),
+       OPT_BOOL(0, "mirror", &option_mirror,
+                N_("create a mirror repository (implies bare)")),
        OPT_BOOL('l', "local", &option_local,
                N_("to clone from a local repository")),
-       OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
+       OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks,
                    N_("don't use local hardlinks, always copy")),
-       OPT_BOOLEAN('s', "shared", &option_shared,
+       OPT_BOOL('s', "shared", &option_shared,
                    N_("setup as shared repository")),
-       OPT_BOOLEAN(0, "recursive", &option_recursive,
+       OPT_BOOL(0, "recursive", &option_recursive,
                    N_("initialize submodules in the clone")),
-       OPT_BOOLEAN(0, "recurse-submodules", &option_recursive,
+       OPT_BOOL(0, "recurse-submodules", &option_recursive,
                    N_("initialize submodules in the clone")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
                   N_("directory from which templates will be used")),
index 10acc53f8012f53b6a15a3d006b622bda3409b17..084d70fd4da5d38196cbde1f692bd2d70c0c3800 100644 (file)
@@ -30,6 +30,7 @@
 #include "column.h"
 #include "sequencer.h"
 #include "notes-utils.h"
+#include "mailmap.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [options] [--] <pathspec>..."),
@@ -202,17 +203,15 @@ static int commit_index_files(void)
  * and return the paths that match the given pattern in list.
  */
 static int list_paths(struct string_list *list, const char *with_tree,
-                     const char *prefix, const char **pattern)
+                     const char *prefix, const struct pathspec *pattern)
 {
        int i;
        char *m;
 
-       if (!pattern)
+       if (!pattern->nr)
                return 0;
 
-       for (i = 0; pattern[i]; i++)
-               ;
-       m = xcalloc(1, i);
+       m = xcalloc(1, pattern->nr);
 
        if (with_tree) {
                char *max_prefix = common_prefix(pattern);
@@ -226,7 +225,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
                if (ce->ce_flags & CE_UPDATE)
                        continue;
-               if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
+               if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
                item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
@@ -298,17 +297,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 {
        int fd;
        struct string_list partial;
-       const char **pathspec = NULL;
+       struct pathspec pathspec;
        char *old_index_env = NULL;
        int refresh_flags = REFRESH_QUIET;
 
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_FULL,
+                      prefix, argv);
 
-       if (*argv)
-               pathspec = get_pathspec(prefix, argv);
-
-       if (read_cache_preload(pathspec) < 0)
+       if (read_cache_preload(&pathspec) < 0)
                die(_("index file corrupt"));
 
        if (interactive) {
@@ -350,9 +349,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * (A) if all goes well, commit the real index;
         * (B) on failure, rollback the real index.
         */
-       if (all || (also && pathspec && *pathspec)) {
+       if (all || (also && pathspec.nr)) {
                fd = hold_locked_index(&index_lock, 1);
-               add_files_to_cache(also ? prefix : NULL, pathspec, 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_cache(fd, active_cache, active_nr) ||
@@ -371,7 +370,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * and create commit from the_index.
         * We still need to refresh the index here.
         */
-       if (!only && (!pathspec || !*pathspec)) {
+       if (!only && !pathspec.nr) {
                fd = hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
@@ -416,7 +415,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
 
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
-       if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec))
+       if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
                exit(1);
 
        discard_cache();
@@ -935,6 +934,7 @@ static const char *find_author_by_nickname(const char *name)
        struct rev_info revs;
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
+       struct string_list mailmap = STRING_LIST_INIT_NODUP;
        const char *av[20];
        int ac = 0;
 
@@ -945,13 +945,17 @@ static const char *find_author_by_nickname(const char *name)
        av[++ac] = buf.buf;
        av[++ac] = NULL;
        setup_revisions(ac, av, &revs, NULL);
+       revs.mailmap = &mailmap;
+       read_mailmap(revs.mailmap, NULL);
+
        prepare_revision_walk(&revs);
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = DATE_NORMAL;
                strbuf_release(&buf);
-               format_commit_message(commit, "%an <%ae>", &buf, &ctx);
+               format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+               clear_mailmap(&mailmap);
                return strbuf_detach(&buf, NULL);
        }
        die(_("No existing author found with '%s'"), name);
@@ -1091,7 +1095,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (patch_interactive)
                interactive = 1;
 
-       if (!!also + !!only + !!all + !!interactive > 1)
+       if (also + only + all + interactive > 1)
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
        if (argc == 0 && (also || (only && !amend)))
                die(_("No paths with --include/--only does not make sense."));
@@ -1228,14 +1232,14 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
-               OPT_BOOLEAN('z', "null", &s.null_termination,
-                           N_("terminate entries with NUL")),
+               OPT_BOOL('z', "null", &s.null_termination,
+                        N_("terminate entries with NUL")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
                  N_("mode"),
                  N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
-               OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
-                           N_("show ignored files")),
+               OPT_BOOL(0, "ignored", &show_ignored_in_status,
+                        N_("show ignored files")),
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
                  N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
@@ -1259,11 +1263,12 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
                s.show_ignored_files = 1;
-       if (*argv)
-               s.pathspec = get_pathspec(prefix, argv);
+       parse_pathspec(&s.pathspec, 0,
+                      PATHSPEC_PREFER_FULL,
+                      prefix, argv);
 
-       read_cache_preload(s.pathspec);
-       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+       read_cache_preload(&s.pathspec);
+       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
 
        fd = hold_locked_index(&index_lock, 0);
        if (0 <= fd)
@@ -1434,24 +1439,24 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
                OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
                OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
-               OPT_BOOLEAN(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
-               OPT_BOOLEAN('s', "signoff", &signoff, N_("add Signed-off-by:")),
+               OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
+               OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
-               OPT_BOOLEAN(0, "status", &include_status, N_("include status in commit message template")),
+               OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
                { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
                  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
                /* end commit message options */
 
                OPT_GROUP(N_("Commit contents options")),
-               OPT_BOOLEAN('a', "all", &all, N_("commit all changed files")),
-               OPT_BOOLEAN('i', "include", &also, N_("add specified files to index for commit")),
-               OPT_BOOLEAN(0, "interactive", &interactive, N_("interactively add files")),
-               OPT_BOOLEAN('p', "patch", &patch_interactive, N_("interactively add changes")),
-               OPT_BOOLEAN('o', "only", &only, N_("commit only specified files")),
-               OPT_BOOLEAN('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
-               OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
+               OPT_BOOL('a', "all", &all, N_("commit all changed files")),
+               OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
+               OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
+               OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
+               OPT_BOOL('o', "only", &only, N_("commit only specified files")),
+               OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+               OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
                OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
@@ -1460,19 +1465,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_SET_INT(0, "long", &status_format,
                            N_("show status in long format (default)"),
                            STATUS_FORMAT_LONG),
-               OPT_BOOLEAN('z', "null", &s.null_termination,
-                           N_("terminate entries with NUL")),
-               OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
-               OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
+               OPT_BOOL('z', "null", &s.null_termination,
+                        N_("terminate entries with NUL")),
+               OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
+               OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
                /* end commit contents options */
 
-               { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
-                 N_("ok to record an empty change"),
-                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-               { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
-                 N_("ok to record a change with an empty message"),
-                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+               OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
+                               N_("ok to record an empty change")),
+               OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message,
+                               N_("ok to record a change with an empty message")),
 
                OPT_END()
        };
index 4010c4320a585d84bdb0914d8a9bbc27d38b7f18..20e89fe4e0c7db50267e8e9358c38faea2259fab 100644 (file)
@@ -2,6 +2,7 @@
 #include "cache.h"
 #include "color.h"
 #include "parse-options.h"
+#include "urlmatch.h"
 
 static const char *const builtin_config_usage[] = {
        N_("git config [options]"),
@@ -42,6 +43,7 @@ static int respect_includes = -1;
 #define ACTION_SET_ALL (1<<12)
 #define ACTION_GET_COLOR (1<<13)
 #define ACTION_GET_COLORBOOL (1<<14)
+#define ACTION_GET_URLMATCH (1<<15)
 
 #define TYPE_BOOL (1<<0)
 #define TYPE_INT (1<<1)
@@ -50,15 +52,16 @@ static int respect_includes = -1;
 
 static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Config file location")),
-       OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")),
-       OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
-       OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
+       OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
+       OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
+       OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
        OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
        OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
        OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
        OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
        OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
+       OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
        OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
        OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
        OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
@@ -75,7 +78,7 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
        OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
        OPT_GROUP(N_("Other")),
-       OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")),
+       OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
        OPT_END(),
 };
@@ -102,25 +105,13 @@ struct strbuf_list {
        int alloc;
 };
 
-static int collect_config(const char *key_, const char *value_, void *cb)
+static int format_config(struct strbuf *buf, const char *key_, const char *value_)
 {
-       struct strbuf_list *values = cb;
-       struct strbuf *buf;
-       char value[256];
-       const char *vptr = value;
        int must_free_vptr = 0;
        int must_print_delim = 0;
+       char value[256];
+       const char *vptr = value;
 
-       if (!use_key_regexp && strcmp(key_, key))
-               return 0;
-       if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
-               return 0;
-       if (regexp != NULL &&
-           (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
-               return 0;
-
-       ALLOC_GROW(values->items, values->nr + 1, values->alloc);
-       buf = &values->items[values->nr++];
        strbuf_init(buf, 0);
 
        if (show_keys) {
@@ -128,7 +119,8 @@ static int collect_config(const char *key_, const char *value_, void *cb)
                must_print_delim = 1;
        }
        if (types == TYPE_INT)
-               sprintf(value, "%d", git_config_int(key_, value_?value_:""));
+               sprintf(value, "%"PRId64,
+                       git_config_int64(key_, value_ ? value_ : ""));
        else if (types == TYPE_BOOL)
                vptr = git_config_bool(key_, value_) ? "true" : "false";
        else if (types == TYPE_BOOL_OR_INT) {
@@ -156,15 +148,27 @@ static int collect_config(const char *key_, const char *value_, void *cb)
        strbuf_addch(buf, term);
 
        if (must_free_vptr)
-               /* If vptr must be freed, it's a pointer to a
-                * dynamically allocated buffer, it's safe to cast to
-                * const.
-               */
                free((char *)vptr);
-
        return 0;
 }
 
+static int collect_config(const char *key_, const char *value_, void *cb)
+{
+       struct strbuf_list *values = cb;
+
+       if (!use_key_regexp && strcmp(key_, key))
+               return 0;
+       if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+               return 0;
+       if (regexp != NULL &&
+           (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+               return 0;
+
+       ALLOC_GROW(values->items, values->nr + 1, values->alloc);
+
+       return format_config(&values->items[values->nr++], key_, value_);
+}
+
 static int get_value(const char *key_, const char *regex_)
 {
        int ret = CONFIG_GENERIC_ERROR;
@@ -265,8 +269,8 @@ static char *normalize_value(const char *key, const char *value)
        else {
                normalized = xmalloc(64);
                if (types == TYPE_INT) {
-                       int v = git_config_int(key, value);
-                       sprintf(normalized, "%d", v);
+                       int64_t v = git_config_int64(key, value);
+                       sprintf(normalized, "%"PRId64, v);
                }
                else if (types == TYPE_BOOL)
                        sprintf(normalized, "%s",
@@ -364,6 +368,97 @@ static void check_blob_write(void)
                die("writing config blobs is not supported");
 }
 
+struct urlmatch_current_candidate_value {
+       char value_is_null;
+       struct strbuf value;
+};
+
+static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+{
+       struct string_list *values = cb;
+       struct string_list_item *item = string_list_insert(values, var);
+       struct urlmatch_current_candidate_value *matched = item->util;
+
+       if (!matched) {
+               matched = xmalloc(sizeof(*matched));
+               strbuf_init(&matched->value, 0);
+               item->util = matched;
+       } else {
+               strbuf_reset(&matched->value);
+       }
+
+       if (value) {
+               strbuf_addstr(&matched->value, value);
+               matched->value_is_null = 0;
+       } else {
+               matched->value_is_null = 1;
+       }
+       return 0;
+}
+
+static char *dup_downcase(const char *string)
+{
+       char *result;
+       size_t len, i;
+
+       len = strlen(string);
+       result = xmalloc(len + 1);
+       for (i = 0; i < len; i++)
+               result[i] = tolower(string[i]);
+       result[i] = '\0';
+       return result;
+}
+
+static int get_urlmatch(const char *var, const char *url)
+{
+       char *section_tail;
+       struct string_list_item *item;
+       struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+       struct string_list values = STRING_LIST_INIT_DUP;
+
+       config.collect_fn = urlmatch_collect_fn;
+       config.cascade_fn = NULL;
+       config.cb = &values;
+
+       if (!url_normalize(url, &config.url))
+               die("%s", config.url.err);
+
+       config.section = dup_downcase(var);
+       section_tail = strchr(config.section, '.');
+       if (section_tail) {
+               *section_tail = '\0';
+               config.key = section_tail + 1;
+               show_keys = 0;
+       } else {
+               config.key = NULL;
+               show_keys = 1;
+       }
+
+       git_config_with_options(urlmatch_config_entry, &config,
+                               given_config_file, NULL, respect_includes);
+
+       for_each_string_list_item(item, &values) {
+               struct urlmatch_current_candidate_value *matched = item->util;
+               struct strbuf key = STRBUF_INIT;
+               struct strbuf buf = STRBUF_INIT;
+
+               strbuf_addstr(&key, item->string);
+               format_config(&buf, key.buf,
+                             matched->value_is_null ? NULL : matched->value.buf);
+               fwrite(buf.buf, 1, buf.len, stdout);
+               strbuf_release(&key);
+               strbuf_release(&buf);
+
+               strbuf_release(&matched->value);
+       }
+       string_list_clear(&config.vars, 1);
+       string_list_clear(&values, 1);
+       free(config.url.url);
+
+       free((void *)config.section);
+       return 0;
+}
+
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
        int nongit = !startup_info->have_repository;
@@ -523,6 +618,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_argc(argc, 1, 2);
                return get_value(argv[0], argv[1]);
        }
+       else if (actions == ACTION_GET_URLMATCH) {
+               check_argc(argc, 2, 2);
+               return get_urlmatch(argv[0], argv[1]);
+       }
        else if (actions == ACTION_UNSET) {
                check_blob_write();
                check_argc(argc, 1, 2);
index 7d73722f59b10b5fe302498bb83c9ab3313b89b0..c94e5c30d0851080c49df2edc6d66a739085cf22 100644 (file)
@@ -406,12 +406,12 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 {
        int contains = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "contains",   &contains, N_("find the tag that comes after the commit")),
-               OPT_BOOLEAN(0, "debug",      &debug, N_("debug search strategy on stderr")),
-               OPT_BOOLEAN(0, "all",        &all, N_("use any ref")),
-               OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag, even unannotated")),
-               OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
-               OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")),
+               OPT_BOOL(0, "contains",   &contains, N_("find the tag that comes after the commit")),
+               OPT_BOOL(0, "debug",      &debug, N_("debug search strategy on stderr")),
+               OPT_BOOL(0, "all",        &all, N_("use any ref")),
+               OPT_BOOL(0, "tags",       &tags, N_("use any tag, even unannotated")),
+               OPT_BOOL(0, "long",       &longformat, N_("always use long format")),
+               OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
                            N_("only output exact matches"), 0),
@@ -419,11 +419,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                            N_("consider <n> most recent tags (default: 10)")),
                OPT_STRING(0, "match",       &pattern, N_("pattern"),
                           N_("only consider tags matching <pattern>")),
-               OPT_BOOLEAN(0, "always",     &always,
-                          N_("show abbreviated commit object as fallback")),
+               OPT_BOOL(0, "always",        &always,
+                       N_("show abbreviated commit object as fallback")),
                {OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
-                          N_("append <mark> on dirty working tree (default: \"-dirty\")"),
-                PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+                       N_("append <mark> on dirty working tree (default: \"-dirty\")"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
                OPT_END(),
        };
 
index 46085f862f937b005493319cea25b93bcb10c999..9200069363ff016d167f6885688650158c12e507 100644 (file)
@@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
                rev.combine_merges = rev.dense_combined_merges = 1;
 
-       if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
+       if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
                perror("read_cache_preload");
                return -1;
        }
index 1c737f79216fda2e5395422d8aee2e4f5d6482a4..ce15b23042d8472573b88e0ee9a6032673321ea3 100644 (file)
@@ -43,7 +43,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                usage(diff_cache_usage);
        if (!cached) {
                setup_work_tree();
-               if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) {
+               if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
                        perror("read_cache_preload");
                        return -1;
                }
index 9fc273d8cd78d53a55047e17d44eb89291741167..2fb8c5dc0b6fdc97beb82f4dc3802a8f0dad8211 100644 (file)
@@ -140,7 +140,7 @@ static int builtin_diff_index(struct rev_info *revs,
                usage(builtin_diff_usage);
        if (!cached) {
                setup_work_tree();
-               if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
+               if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
                        perror("read_cache_preload");
                        return -1;
                }
@@ -242,7 +242,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                revs->combine_merges = revs->dense_combined_merges = 1;
 
        setup_work_tree();
-       if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) {
+       if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
                perror("read_cache_preload");
                return -1;
        }
@@ -367,6 +367,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                }
        }
        if (rev.prune_data.nr) {
+               /* builtin_diff_b_f() */
+               GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
                if (!path)
                        path = rev.prune_data.items[0].match;
                paths += rev.prune_data.nr;
index 03e10905902c88c9dc3e0532db862a6fe29ab1ac..78250eab08abf692344277e01cb94fc5f5c2f807 100644 (file)
@@ -681,11 +681,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                             N_("Dump marks to this file")),
                OPT_STRING(0, "import-marks", &import_filename, N_("file"),
                             N_("Import marks from this file")),
-               OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
-                            N_("Fake a tagger when tags lack one")),
-               OPT_BOOLEAN(0, "full-tree", &full_tree,
-                            N_("Output full tree for each commit")),
-               OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
+               OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
+                        N_("Fake a tagger when tags lack one")),
+               OPT_BOOL(0, "full-tree", &full_tree,
+                        N_("Output full tree for each commit")),
+               OPT_BOOL(0, "use-done-feature", &use_done_feature,
                             N_("Use the done feature to terminate the stream")),
                OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
                OPT_END()
index aba44655524ff722d90de09760945f5f30088752..c8e858232a8e7a3536ef4d296efdc618e034c36e 100644 (file)
@@ -1,6 +1,8 @@
 #include "builtin.h"
 #include "pkt-line.h"
 #include "fetch-pack.h"
+#include "remote.h"
+#include "connect.h"
 
 static const char fetch_pack_usage[] =
 "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
@@ -100,6 +102,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        pack_lockfile_ptr = &pack_lockfile;
                        continue;
                }
+               if (!strcmp("--check-self-contained-and-connected", arg)) {
+                       args.check_self_contained_and_connected = 1;
+                       continue;
+               }
                usage(fetch_pack_usage);
        }
 
@@ -152,6 +158,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
        }
+       if (args.check_self_contained_and_connected &&
+           args.self_contained_and_connected) {
+               printf("connectivity-ok\n");
+               fflush(stdout);
+       }
        close(fd[0]);
        close(fd[1]);
        if (finish_connect(conn))
index d784b2e6947f1b5ace0016d92258e29377e9d1a7..9e654efa3bb725edd91f6eb7059b263668e622bc 100644 (file)
@@ -30,13 +30,18 @@ enum {
        TAGS_SET = 2
 };
 
-static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
+static int fetch_prune_config = -1; /* unspecified */
+static int prune = -1; /* unspecified */
+#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
+
+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
 static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int tags = TAGS_DEFAULT, unshallow;
 static const char *depth;
 static const char *upload_pack;
 static struct strbuf default_rla = STRBUF_INIT;
-static struct transport *transport;
+static struct transport *gtransport;
+static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
 
@@ -54,30 +59,39 @@ static int option_parse_recurse_submodules(const struct option *opt,
        return 0;
 }
 
+static int git_fetch_config(const char *k, const char *v, void *cb)
+{
+       if (!strcmp(k, "fetch.prune")) {
+               fetch_prune_config = git_config_bool(k, v);
+               return 0;
+       }
+       return 0;
+}
+
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
-       OPT_BOOLEAN(0, "all", &all,
-                   N_("fetch from all remotes")),
-       OPT_BOOLEAN('a', "append", &append,
-                   N_("append to .git/FETCH_HEAD instead of overwriting")),
+       OPT_BOOL(0, "all", &all,
+                N_("fetch from all remotes")),
+       OPT_BOOL('a', "append", &append,
+                N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
                   N_("path to upload pack on remote end")),
        OPT__FORCE(&force, N_("force overwrite of local branch")),
-       OPT_BOOLEAN('m', "multiple", &multiple,
-                   N_("fetch from multiple remotes")),
+       OPT_BOOL('m', "multiple", &multiple,
+                N_("fetch from multiple remotes")),
        OPT_SET_INT('t', "tags", &tags,
                    N_("fetch all tags and associated objects"), TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
                    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
-       OPT_BOOLEAN('p', "prune", &prune,
-                   N_("prune remote-tracking branches no longer on remote")),
+       OPT_BOOL('p', "prune", &prune,
+                N_("prune remote-tracking branches no longer on remote")),
        { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
                    N_("control recursive fetching of submodules"),
                    PARSE_OPT_OPTARG, option_parse_recurse_submodules },
-       OPT_BOOLEAN(0, "dry-run", &dry_run,
-                   N_("dry run")),
-       OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")),
-       OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
+       OPT_BOOL(0, "dry-run", &dry_run,
+                N_("dry run")),
+       OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+       OPT_BOOL('u', "update-head-ok", &update_head_ok,
                    N_("allow updating of HEAD ref")),
        OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
        OPT_STRING(0, "depth", &depth, N_("depth"),
@@ -95,8 +109,10 @@ static struct option builtin_fetch_options[] = {
 
 static void unlock_pack(void)
 {
-       if (transport)
-               transport_unlock_pack(transport);
+       if (gtransport)
+               transport_unlock_pack(gtransport);
+       if (gsecondary)
+               transport_unlock_pack(gsecondary);
 }
 
 static void unlock_pack_on_signal(int signo)
@@ -720,6 +736,48 @@ static int truncate_fetch_head(void)
        return 0;
 }
 
+static void set_option(struct transport *transport, const char *name, const char *value)
+{
+       int r = transport_set_option(transport, name, value);
+       if (r < 0)
+               die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+                   name, value, transport->url);
+       if (r > 0)
+               warning(_("Option \"%s\" is ignored for %s\n"),
+                       name, transport->url);
+}
+
+static struct transport *prepare_transport(struct remote *remote)
+{
+       struct transport *transport;
+       transport = transport_get(remote, NULL);
+       transport_set_verbosity(transport, verbosity, progress);
+       if (upload_pack)
+               set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
+       if (keep)
+               set_option(transport, TRANS_OPT_KEEP, "yes");
+       if (depth)
+               set_option(transport, TRANS_OPT_DEPTH, depth);
+       return transport;
+}
+
+static void backfill_tags(struct transport *transport, struct ref *ref_map)
+{
+       if (transport->cannot_reuse) {
+               gsecondary = prepare_transport(transport->remote);
+               transport = gsecondary;
+       }
+
+       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
+       transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+       fetch_refs(transport, ref_map);
+
+       if (gsecondary) {
+               transport_disconnect(gsecondary);
+               gsecondary = NULL;
+       }
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
@@ -771,7 +829,10 @@ static int do_fetch(struct transport *transport,
                goto cleanup;
        }
        if (prune) {
-               /* If --tags was specified, pretend the user gave us the canonical tags refspec */
+               /*
+                * If --tags was specified, pretend that the user gave us
+                * the canonical tags refspec
+                */
                if (tags == TAGS_SET) {
                        const char *tags_str = "refs/tags/*:refs/tags/*";
                        struct refspec *tags_refspec, *refspec;
@@ -803,11 +864,8 @@ static int do_fetch(struct transport *transport,
                struct ref **tail = &ref_map;
                ref_map = NULL;
                find_non_local_tags(transport, &ref_map, &tail);
-               if (ref_map) {
-                       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
-                       transport_set_option(transport, TRANS_OPT_DEPTH, "0");
-                       fetch_refs(transport, ref_map);
-               }
+               if (ref_map)
+                       backfill_tags(transport, ref_map);
                free_refs(ref_map);
        }
 
@@ -816,17 +874,6 @@ static int do_fetch(struct transport *transport,
        return retcode;
 }
 
-static void set_option(const char *name, const char *value)
-{
-       int r = transport_set_option(transport, name, value);
-       if (r < 0)
-               die(_("Option \"%s\" value \"%s\" is not valid for %s"),
-                       name, value, transport->url);
-       if (r > 0)
-               warning(_("Option \"%s\" is ignored for %s\n"),
-                       name, transport->url);
-}
-
 static int get_one_remote_for_fetch(struct remote *remote, void *priv)
 {
        struct string_list *list = priv;
@@ -882,7 +929,7 @@ static void add_options_to_argv(struct argv_array *argv)
 {
        if (dry_run)
                argv_array_push(argv, "--dry-run");
-       if (prune)
+       if (prune > 0)
                argv_array_push(argv, "--prune");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
@@ -949,14 +996,17 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
                    "remote name from which new revisions should be fetched."));
 
-       transport = transport_get(remote, NULL);
-       transport_set_verbosity(transport, verbosity, progress);
-       if (upload_pack)
-               set_option(TRANS_OPT_UPLOADPACK, upload_pack);
-       if (keep)
-               set_option(TRANS_OPT_KEEP, "yes");
-       if (depth)
-               set_option(TRANS_OPT_DEPTH, depth);
+       gtransport = prepare_transport(remote);
+
+       if (prune < 0) {
+               /* no command line request */
+               if (0 <= gtransport->remote->prune)
+                       prune = gtransport->remote->prune;
+               else if (0 <= fetch_prune_config)
+                       prune = fetch_prune_config;
+               else
+                       prune = PRUNE_BY_DEFAULT;
+       }
 
        if (argc > 0) {
                int j = 0;
@@ -983,10 +1033,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
        refspec = parse_fetch_refspec(ref_nr, refs);
-       exit_code = do_fetch(transport, refspec, ref_nr);
+       exit_code = do_fetch(gtransport, refspec, ref_nr);
        free_refspec(ref_nr, refspec);
-       transport_disconnect(transport);
-       transport = NULL;
+       transport_disconnect(gtransport);
+       gtransport = NULL;
        return exit_code;
 }
 
@@ -1007,6 +1057,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++)
                strbuf_addf(&default_rla, " %s", argv[i]);
 
+       git_config(git_fetch_config, NULL);
+
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
 
index 7f059c31dfbd11f77c891134ccc34cdc1f65bcb8..1d4083c2ddef07c6488d36720e6a0c48b04c6585 100644 (file)
@@ -867,24 +867,29 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs
 static void print_value(struct refinfo *ref, int atom, int quote_style)
 {
        struct atom_value *v;
+       struct strbuf sb = STRBUF_INIT;
        get_value(ref, atom, &v);
        switch (quote_style) {
        case QUOTE_NONE:
                fputs(v->s, stdout);
                break;
        case QUOTE_SHELL:
-               sq_quote_print(stdout, v->s);
+               sq_quote_buf(&sb, v->s);
                break;
        case QUOTE_PERL:
-               perl_quote_print(stdout, v->s);
+               perl_quote_buf(&sb, v->s);
                break;
        case QUOTE_PYTHON:
-               python_quote_print(stdout, v->s);
+               python_quote_buf(&sb, v->s);
                break;
        case QUOTE_TCL:
-               tcl_quote_print(stdout, v->s);
+               tcl_quote_buf(&sb, v->s);
                break;
        }
+       if (quote_style != QUOTE_NONE) {
+               fputs(sb.buf, stdout);
+               strbuf_release(&sb);
+       }
 }
 
 static int hex1(char ch)
index 9909b6d519f0a7d3d3b53313e7886c9482d98ba5..97ce678c6ba63afeb3deedfd469c4a53a475a1be 100644 (file)
@@ -16,6 +16,7 @@
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
+#define HAS_OBJ   0x0004
 
 static int show_root;
 static int show_tags;
@@ -101,7 +102,7 @@ static int mark_object(struct object *obj, int type, void *data)
        if (obj->flags & REACHABLE)
                return 0;
        obj->flags |= REACHABLE;
-       if (!obj->parsed) {
+       if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_sha1_file(obj->sha1)) {
                        printf("broken link from %7s %s\n",
                                 typename(parent->type), sha1_to_hex(parent->sha1));
@@ -127,16 +128,13 @@ static int traverse_one_object(struct object *obj)
        struct tree *tree = NULL;
 
        if (obj->type == OBJ_TREE) {
-               obj->parsed = 0;
                tree = (struct tree *)obj;
                if (parse_tree(tree) < 0)
                        return 1; /* error already displayed */
        }
        result = fsck_walk(obj, mark_object, obj);
-       if (tree) {
-               free(tree->buffer);
-               tree->buffer = NULL;
-       }
+       if (tree)
+               free_tree_buffer(tree);
        return result;
 }
 
@@ -178,7 +176,7 @@ static void check_reachable_object(struct object *obj)
         * except if it was in a pack-file and we didn't
         * do a full fsck
         */
-       if (!obj->parsed) {
+       if (!(obj->flags & HAS_OBJ)) {
                if (has_sha1_pack(obj->sha1))
                        return; /* it is in pack - forget about it */
                printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
@@ -306,8 +304,7 @@ static int fsck_obj(struct object *obj)
        if (obj->type == OBJ_TREE) {
                struct tree *item = (struct tree *) obj;
 
-               free(item->buffer);
-               item->buffer = NULL;
+               free_tree_buffer(item);
        }
 
        if (obj->type == OBJ_COMMIT) {
@@ -340,6 +337,7 @@ static int fsck_sha1(const unsigned char *sha1)
                return error("%s: object corrupt or missing",
                             sha1_to_hex(sha1));
        }
+       obj->flags |= HAS_OBJ;
        return fsck_obj(obj);
 }
 
@@ -352,6 +350,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
                errors_found |= ERROR_OBJECT;
                return error("%s: object corrupt or missing", sha1_to_hex(sha1));
        }
+       obj->flags = HAS_OBJ;
        return fsck_obj(obj);
 }
 
@@ -611,15 +610,15 @@ static char const * const fsck_usage[] = {
 
 static struct option fsck_opts[] = {
        OPT__VERBOSE(&verbose, N_("be verbose")),
-       OPT_BOOLEAN(0, "unreachable", &show_unreachable, N_("show unreachable objects")),
+       OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")),
        OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")),
-       OPT_BOOLEAN(0, "tags", &show_tags, N_("report tags")),
-       OPT_BOOLEAN(0, "root", &show_root, N_("report root nodes")),
-       OPT_BOOLEAN(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
-       OPT_BOOLEAN(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
-       OPT_BOOLEAN(0, "full", &check_full, N_("also consider packs and alternate objects")),
-       OPT_BOOLEAN(0, "strict", &check_strict, N_("enable more strict checking")),
-       OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
+       OPT_BOOL(0, "tags", &show_tags, N_("report tags")),
+       OPT_BOOL(0, "root", &show_root, N_("report root nodes")),
+       OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
+       OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
+       OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")),
+       OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")),
+       OPT_BOOL(0, "lost-found", &write_lost_and_found,
                                N_("write dangling objects in .git/lost-found")),
        OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
        OPT_END(),
index 6be6c8d65b26c9149aac2b6c1de6e3217c6ceea7..891a2c2ecb046dc29b2d9ce8b9d2982084bf9b19 100644 (file)
@@ -167,19 +167,78 @@ static int need_to_gc(void)
        return 1;
 }
 
+/* return NULL on success, else hostname running the gc */
+static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
+{
+       static struct lock_file lock;
+       static char locking_host[128];
+       char my_host[128];
+       struct strbuf sb = STRBUF_INIT;
+       struct stat st;
+       uintmax_t pid;
+       FILE *fp;
+       int fd, should_exit;
+
+       if (gethostname(my_host, sizeof(my_host)))
+               strcpy(my_host, "unknown");
+
+       fd = hold_lock_file_for_update(&lock, git_path("gc.pid"),
+                                      LOCK_DIE_ON_ERROR);
+       if (!force) {
+               fp = fopen(git_path("gc.pid"), "r");
+               memset(locking_host, 0, sizeof(locking_host));
+               should_exit =
+                       fp != NULL &&
+                       !fstat(fileno(fp), &st) &&
+                       /*
+                        * 12 hour limit is very generous as gc should
+                        * never take that long. On the other hand we
+                        * don't really need a strict limit here,
+                        * running gc --auto one day late is not a big
+                        * problem. --force can be used in manual gc
+                        * after the user verifies that no gc is
+                        * running.
+                        */
+                       time(NULL) - st.st_mtime <= 12 * 3600 &&
+                       fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
+                       /* be gentle to concurrent "gc" on remote hosts */
+                       (strcmp(locking_host, my_host) || !kill(pid, 0));
+               if (fp != NULL)
+                       fclose(fp);
+               if (should_exit) {
+                       if (fd >= 0)
+                               rollback_lock_file(&lock);
+                       *ret_pid = pid;
+                       return locking_host;
+               }
+       }
+
+       strbuf_addf(&sb, "%"PRIuMAX" %s",
+                   (uintmax_t) getpid(), my_host);
+       write_in_full(fd, sb.buf, sb.len);
+       strbuf_release(&sb);
+       commit_lock_file(&lock);
+
+       return NULL;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
        int aggressive = 0;
        int auto_gc = 0;
        int quiet = 0;
+       int force = 0;
+       const char *name;
+       pid_t pid;
 
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, N_("suppress progress reporting")),
                { OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
                        N_("prune unreferenced objects"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
-               OPT_BOOLEAN(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
-               OPT_BOOLEAN(0, "auto", &auto_gc, N_("enable auto-gc mode")),
+               OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
+               OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")),
+               OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")),
                OPT_END()
        };
 
@@ -225,6 +284,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        } else
                add_repack_all_option();
 
+       name = lock_repo_for_gc(force, &pid);
+       if (name) {
+               if (auto_gc)
+                       return 0; /* be quiet on --auto */
+               die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
+                   name, (uintmax_t)pid);
+       }
+
        if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
                return error(FAILED_RUN, pack_refs_cmd.argv[0]);
 
index d3b3b1db1121b37d5a8d0e440e03273526003774..03bc442e3f96ba12ec389f0fc40349a57e073dc7 100644 (file)
@@ -17,6 +17,7 @@
 #include "grep.h"
 #include "quote.h"
 #include "dir.h"
+#include "pathspec.h"
 
 static char const * const grep_usage[] = {
        N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"),
@@ -521,7 +522,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
        if (exc_std)
                setup_standard_excludes(&dir);
 
-       fill_directory(&dir, pathspec->raw);
+       fill_directory(&dir, pathspec);
        for (i = 0; i < dir.nr; i++) {
                const char *name = dir.entries[i]->name;
                int namelen = strlen(name);
@@ -629,7 +630,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        const char *show_in_pager = NULL, *default_pager = "dummy";
        struct grep_opt opt;
        struct object_array list = OBJECT_ARRAY_INIT;
-       const char **paths = NULL;
        struct pathspec pathspec;
        struct string_list path_list = STRING_LIST_INIT_NODUP;
        int i;
@@ -638,20 +638,20 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
 
        struct option options[] = {
-               OPT_BOOLEAN(0, "cached", &cached,
+               OPT_BOOL(0, "cached", &cached,
                        N_("search in index instead of in the work tree")),
                OPT_NEGBIT(0, "no-index", &use_index,
                         N_("find in contents not managed by git"), 1),
-               OPT_BOOLEAN(0, "untracked", &untracked,
+               OPT_BOOL(0, "untracked", &untracked,
                        N_("search in both tracked and untracked files")),
                OPT_SET_INT(0, "exclude-standard", &opt_exclude,
                            N_("search also in ignored files"), 1),
                OPT_GROUP(""),
-               OPT_BOOLEAN('v', "invert-match", &opt.invert,
+               OPT_BOOL('v', "invert-match", &opt.invert,
                        N_("show non-matching lines")),
-               OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case,
+               OPT_BOOL('i', "ignore-case", &opt.ignore_case,
                        N_("case insensitive matching")),
-               OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
+               OPT_BOOL('w', "word-regexp", &opt.word_regexp,
                        N_("match patterns only at word boundaries")),
                OPT_SET_INT('a', "text", &opt.binary,
                        N_("process binary files as text"), GREP_BINARY_TEXT),
@@ -675,26 +675,26 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                            N_("use Perl-compatible regular expressions"),
                            GREP_PATTERN_TYPE_PCRE),
                OPT_GROUP(""),
-               OPT_BOOLEAN('n', "line-number", &opt.linenum, N_("show line numbers")),
+               OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")),
                OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1),
                OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1),
                OPT_NEGBIT(0, "full-name", &opt.relative,
                        N_("show filenames relative to top directory"), 1),
-               OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
+               OPT_BOOL('l', "files-with-matches", &opt.name_only,
                        N_("show only filenames instead of matching lines")),
-               OPT_BOOLEAN(0, "name-only", &opt.name_only,
+               OPT_BOOL(0, "name-only", &opt.name_only,
                        N_("synonym for --files-with-matches")),
-               OPT_BOOLEAN('L', "files-without-match",
+               OPT_BOOL('L', "files-without-match",
                        &opt.unmatch_name_only,
                        N_("show only the names of files without match")),
-               OPT_BOOLEAN('z', "null", &opt.null_following_name,
+               OPT_BOOL('z', "null", &opt.null_following_name,
                        N_("print NUL after filenames")),
-               OPT_BOOLEAN('c', "count", &opt.count,
+               OPT_BOOL('c', "count", &opt.count,
                        N_("show the number of matches instead of matching lines")),
                OPT__COLOR(&opt.color, N_("highlight matches")),
-               OPT_BOOLEAN(0, "break", &opt.file_break,
+               OPT_BOOL(0, "break", &opt.file_break,
                        N_("print empty line between matches from different files")),
-               OPT_BOOLEAN(0, "heading", &opt.heading,
+               OPT_BOOL(0, "heading", &opt.heading,
                        N_("show filename only once above matches from same file")),
                OPT_GROUP(""),
                OPT_CALLBACK('C', "context", &opt, N_("n"),
@@ -706,9 +706,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        N_("show <n> context lines after matches")),
                OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
                        context_callback),
-               OPT_BOOLEAN('p', "show-function", &opt.funcname,
+               OPT_BOOL('p', "show-function", &opt.funcname,
                        N_("show a line with the function name before matches")),
-               OPT_BOOLEAN('W', "function-context", &opt.funcbody,
+               OPT_BOOL('W', "function-context", &opt.funcbody,
                        N_("show the surrounding function")),
                OPT_GROUP(""),
                OPT_CALLBACK('f', NULL, &opt, N_("file"),
@@ -718,7 +718,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "and", &opt, NULL,
                  N_("combine patterns specified with -e"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
-               OPT_BOOLEAN(0, "or", &dummy, ""),
+               OPT_BOOL(0, "or", &dummy, ""),
                { OPTION_CALLBACK, 0, "not", &opt, NULL, "",
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
                { OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
@@ -729,7 +729,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                  close_callback },
                OPT__QUIET(&opt.status_only,
                           N_("indicate hit with exit status without output")),
-               OPT_BOOLEAN(0, "all-match", &opt.all_match,
+               OPT_BOOL(0, "all-match", &opt.all_match,
                        N_("show only matches from files that match all patterns")),
                { OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
                  N_("show parse tree for grep expression"),
@@ -738,8 +738,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
                        N_("pager"), N_("show matching files in the pager"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
-               OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
-                           N_("allow calling of grep(1) (ignored by this build)")),
+               OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
+                        N_("allow calling of grep(1) (ignored by this build)")),
                { OPTION_CALLBACK, 0, "help-all", &options, NULL, N_("show usage"),
                  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
                OPT_END()
@@ -856,8 +856,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        verify_filename(prefix, argv[j], j == i);
        }
 
-       paths = get_pathspec(prefix, argv + i);
-       init_pathspec(&pathspec, paths);
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_CWD |
+                      (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
+                      prefix, argv + i);
        pathspec.max_depth = opt.max_depth;
        pathspec.recursive = 1;
 
index 8d184f1a99120b1ebccac4f5e1d4fdb0a692b3c9..d7fcf4c13c7e0f0855fba51c6b58d2b06c99984e 100644 (file)
@@ -70,10 +70,10 @@ static const char *vpath;
 
 static const struct option hash_object_options[] = {
        OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
-       OPT_BOOLEAN('w', NULL, &write_object, N_("write the object into the object database")),
-       OPT_BOOLEAN( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
-       OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
-       OPT_BOOLEAN( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
+       OPT_BOOL('w', NULL, &write_object, N_("write the object into the object database")),
+       OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
+       OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
+       OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
        OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
        OPT_END()
 };
index 9c1cfac4427ef8d8fbca0834e1c8b644ceddf998..9e9eb4b74e8335f097c7cc9e261b051feda0bf0d 100644 (file)
@@ -770,6 +770,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        if (obj->type == OBJ_TREE) {
                                struct tree *item = (struct tree *) obj;
                                item->buffer = NULL;
+                               obj->parsed = 0;
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
index 2625f9881ad836d1a3e303f4c159d6b76a3ae04f..77d0f5f3fdbcb48d75aee35a67ed57dd134c907e 100644 (file)
@@ -121,7 +121,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
 
        const struct option builtin_log_options[] = {
-               OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
+               OPT__QUIET(&quiet, N_("suppress diff output")),
                OPT_BOOL(0, "source", &source, N_("show source")),
                OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
@@ -503,7 +503,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        init_grep_defaults();
        git_config(git_log_config, NULL);
 
-       init_pathspec(&match_all, NULL);
+       memset(&match_all, 0, sizeof(match_all));
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
@@ -1179,13 +1179,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
                            N_("don't strip/add [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
-               OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
-                           N_("don't output binary diffs")),
-               OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
-                           N_("don't include a patch matching a commit upstream")),
-               { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
+               OPT_BOOL(0, "no-binary", &no_binary_diff,
+                        N_("don't output binary diffs")),
+               OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
+                        N_("don't include a patch matching a commit upstream")),
+               { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL,
                  N_("show patch format instead of default (patch + stat)"),
-                 PARSE_OPT_NONEG | PARSE_OPT_NOARG },
+                 PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1},
                OPT_GROUP(N_("Messaging")),
                { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
                            N_("add email header"), 0, header_callback },
@@ -1210,8 +1210,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
-               OPT_BOOLEAN(0, "quiet", &quiet,
-                           N_("don't print the patch filenames")),
+               OPT__QUIET(&quiet, N_("don't print the patch filenames")),
                OPT_END()
        };
 
index 5cf3e313700e4cbcd26ae1a9f3f99f2819e40702..e1cf6d8547d4aa7f548fb80f0efb8f4e7b1d9c8e 100644 (file)
@@ -13,6 +13,7 @@
 #include "parse-options.h"
 #include "resolve-undo.h"
 #include "string-list.h"
+#include "pathspec.h"
 
 static int abbrev;
 static int show_deleted;
@@ -30,7 +31,7 @@ static int debug_mode;
 static const char *prefix;
 static int max_prefix_len;
 static int prefix_len;
-static const char **pathspec;
+static struct pathspec pathspec;
 static int error_unmatch;
 static char *ps_matched;
 static const char *with_tree;
@@ -63,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
        if (len >= ent->len)
                die("git ls-files: internal error - directory entry not superset of prefix");
 
-       if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
+       if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched))
                return;
 
        fputs(tag, stdout);
@@ -138,7 +139,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
        if (len >= ce_namelen(ce))
                die("git ls-files: internal error - cache entry not superset of prefix");
 
-       if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
+       if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched))
                return;
 
        if (tag && *tag && show_valid_bit &&
@@ -194,7 +195,7 @@ static void show_ru_info(void)
                len = strlen(path);
                if (len < max_prefix_len)
                        continue; /* outside of the prefix */
-               if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+               if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched))
                        continue; /* uninterested */
                for (i = 0; i < 3; i++) {
                        if (!ui->mode[i])
@@ -219,7 +220,9 @@ static void show_files(struct dir_struct *dir)
 
        /* For cached/deleted files we don't need to even do the readdir */
        if (show_others || show_killed) {
-               fill_directory(dir, pathspec);
+               if (!show_others)
+                       dir->flags |= DIR_COLLECT_KILLED_ONLY;
+               fill_directory(dir, &pathspec);
                if (show_others)
                        show_other_files(dir);
                if (show_killed)
@@ -287,21 +290,6 @@ static void prune_cache(const char *prefix)
        active_nr = last;
 }
 
-static void strip_trailing_slash_from_submodules(void)
-{
-       const char **p;
-
-       for (p = pathspec; *p != NULL; p++) {
-               int len = strlen(*p), pos;
-
-               if (len < 1 || (*p)[len - 1] != '/')
-                       continue;
-               pos = cache_name_pos(*p, len - 1);
-               if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
-                       *p = xstrndup(*p, len - 1);
-       }
-}
-
 /*
  * Read the tree specified with --with-tree option
  * (typically, HEAD) into stage #1 and then
@@ -333,13 +321,12 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
        }
 
        if (prefix) {
-               static const char *(matchbuf[2]);
-               matchbuf[0] = prefix;
-               matchbuf[1] = NULL;
-               init_pathspec(&pathspec, matchbuf);
-               pathspec.items[0].nowildcard_len = pathspec.items[0].len;
+               static const char *(matchbuf[1]);
+               matchbuf[0] = NULL;
+               parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+                              PATHSPEC_PREFER_CWD, prefix, matchbuf);
        } else
-               init_pathspec(&pathspec, NULL);
+               memset(&pathspec, 0, sizeof(pathspec));
        if (read_tree(tree, 1, &pathspec))
                die("unable to read tree entries %s", tree_name);
 
@@ -364,15 +351,16 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix)
        }
 }
 
-int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix)
+int report_path_error(const char *ps_matched,
+                     const struct pathspec *pathspec,
+                     const char *prefix)
 {
        /*
         * Make sure all pathspec matched; otherwise it is an error.
         */
        struct strbuf sb = STRBUF_INIT;
-       const char *name;
        int num, errors = 0;
-       for (num = 0; pathspec[num]; num++) {
+       for (num = 0; num < pathspec->nr; num++) {
                int other, found_dup;
 
                if (ps_matched[num])
@@ -380,13 +368,16 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
                /*
                 * The caller might have fed identical pathspec
                 * twice.  Do not barf on such a mistake.
+                * FIXME: parse_pathspec should have eliminated
+                * duplicate pathspec.
                 */
                for (found_dup = other = 0;
-                    !found_dup && pathspec[other];
+                    !found_dup && other < pathspec->nr;
                     other++) {
                        if (other == num || !ps_matched[other])
                                continue;
-                       if (!strcmp(pathspec[other], pathspec[num]))
+                       if (!strcmp(pathspec->items[other].original,
+                                   pathspec->items[num].original))
                                /*
                                 * Ok, we have a match already.
                                 */
@@ -395,9 +386,8 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
                if (found_dup)
                        continue;
 
-               name = quote_path_relative(pathspec[num], prefix, &sb);
                error("pathspec '%s' did not match any file(s) known to git.",
-                     name);
+                     pathspec->items[num].original);
                errors++;
        }
        strbuf_release(&sb);
@@ -461,24 +451,24 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
                        N_("paths are separated with NUL character"),
                        PARSE_OPT_NOARG, option_parse_z },
-               OPT_BOOLEAN('t', NULL, &show_tag,
+               OPT_BOOL('t', NULL, &show_tag,
                        N_("identify the file status with tags")),
-               OPT_BOOLEAN('v', NULL, &show_valid_bit,
+               OPT_BOOL('v', NULL, &show_valid_bit,
                        N_("use lowercase letters for 'assume unchanged' files")),
-               OPT_BOOLEAN('c', "cached", &show_cached,
+               OPT_BOOL('c', "cached", &show_cached,
                        N_("show cached files in the output (default)")),
-               OPT_BOOLEAN('d', "deleted", &show_deleted,
+               OPT_BOOL('d', "deleted", &show_deleted,
                        N_("show deleted files in the output")),
-               OPT_BOOLEAN('m', "modified", &show_modified,
+               OPT_BOOL('m', "modified", &show_modified,
                        N_("show modified files in the output")),
-               OPT_BOOLEAN('o', "others", &show_others,
+               OPT_BOOL('o', "others", &show_others,
                        N_("show other files in the output")),
                OPT_BIT('i', "ignored", &dir.flags,
                        N_("show ignored files in the output"),
                        DIR_SHOW_IGNORED),
-               OPT_BOOLEAN('s', "stage", &show_stage,
+               OPT_BOOL('s', "stage", &show_stage,
                        N_("show staged contents' object name in the output")),
-               OPT_BOOLEAN('k', "killed", &show_killed,
+               OPT_BOOL('k', "killed", &show_killed,
                        N_("show files on the filesystem that need to be removed")),
                OPT_BIT(0, "directory", &dir.flags,
                        N_("show 'other' directories' name only"),
@@ -486,9 +476,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                OPT_NEGBIT(0, "empty-directory", &dir.flags,
                        N_("don't show empty directories"),
                        DIR_HIDE_EMPTY_DIRECTORIES),
-               OPT_BOOLEAN('u', "unmerged", &show_unmerged,
+               OPT_BOOL('u', "unmerged", &show_unmerged,
                        N_("show unmerged files in the output")),
-               OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
+               OPT_BOOL(0, "resolve-undo", &show_resolve_undo,
                            N_("show resolve-undo information")),
                { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
                        N_("skip files matching pattern"),
@@ -504,12 +494,12 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
                        N_("make the output relative to the project top directory"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
-               OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
+               OPT_BOOL(0, "error-unmatch", &error_unmatch,
                        N_("if any <file> is not in the index, treat this as an error")),
                OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
                        N_("pretend that paths removed since <tree-ish> are still present")),
                OPT__ABBREV(&abbrev),
-               OPT_BOOLEAN(0, "debug", &debug_mode, N_("show debugging data")),
+               OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
                OPT_END()
        };
 
@@ -555,23 +545,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        if (require_work_tree && !is_inside_work_tree())
                setup_work_tree();
 
-       pathspec = get_pathspec(prefix, argv);
-
-       /* be nice with submodule paths ending in a slash */
-       if (pathspec)
-               strip_trailing_slash_from_submodules();
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_CWD |
+                      PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+                      prefix, argv);
 
        /* Find common prefix for all pathspec's */
-       max_prefix = common_prefix(pathspec);
+       max_prefix = common_prefix(&pathspec);
        max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
 
        /* Treat unmatching pathspec elements as errors */
-       if (pathspec && error_unmatch) {
-               int num;
-               for (num = 0; pathspec[num]; num++)
-                       ;
-               ps_matched = xcalloc(1, num);
-       }
+       if (pathspec.nr && error_unmatch)
+               ps_matched = xcalloc(1, pathspec.nr);
 
        if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given)
                die("ls-files --ignored needs some exclude pattern");
@@ -598,7 +583,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 
        if (ps_matched) {
                int bad;
-               bad = report_path_error(ps_matched, pathspec, prefix);
+               bad = report_path_error(ps_matched, &pathspec, prefix);
                if (bad)
                        fprintf(stderr, "Did you forget to 'git add'?\n");
 
index fb76e38d849fc6f7b9cc1a534f62efb605a3655c..65ec93184614619cac925c17ef3ebc90cd30a82c 100644 (file)
@@ -10,6 +10,7 @@
 #include "quote.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "pathspec.h"
 
 static int line_termination = '\n';
 #define LS_RECURSIVE 1
@@ -35,7 +36,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
        if (ls_options & LS_RECURSIVE)
                return 1;
 
-       s = pathspec.raw;
+       s = pathspec._raw;
        if (!s)
                return 0;
 
@@ -138,9 +139,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
                        LS_NAME_ONLY),
                OPT_SET_INT(0, "full-name", &chomp_prefix,
                            N_("use full path names"), 0),
-               OPT_BOOLEAN(0, "full-tree", &full_tree,
-                           N_("list entire tree; not just current directory "
-                              "(implies --full-name)")),
+               OPT_BOOL(0, "full-tree", &full_tree,
+                        N_("list entire tree; not just current directory "
+                           "(implies --full-name)")),
                OPT__ABBREV(&abbrev),
                OPT_END()
        };
@@ -166,7 +167,15 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        if (get_sha1(argv[0], sha1))
                die("Not a valid object name %s", argv[0]);
 
-       init_pathspec(&pathspec, get_pathspec(prefix, argv + 1));
+       /*
+        * show_recursive() rolls its own matching code and is
+        * generally ignorant of 'struct pathspec'. The magic mask
+        * cannot be lifted until it is converted to use
+        * match_pathspec_depth() or tree_entry_interesting()
+        */
+       parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
+                      PATHSPEC_PREFER_CWD,
+                      prefix, argv + 1);
        for (i = 0; i < pathspec.nr; i++)
                pathspec.items[i].nowildcard_len = pathspec.items[i].len;
        pathspec.has_wildcard = 0;
index 0c4cd2f9f792cdf47cecf3401eb2bf7c676f7f6e..e88eb93f143ef5883b3e5f8f069fd13f70ff7abf 100644 (file)
@@ -95,11 +95,11 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        int is_ancestor = 0;
 
        struct option options[] = {
-               OPT_BOOLEAN('a', "all", &show_all, N_("output all common ancestors")),
-               OPT_BOOLEAN(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")),
-               OPT_BOOLEAN(0, "independent", &reduce, N_("list revs not reachable from others")),
-               OPT_BOOLEAN(0, "is-ancestor", &is_ancestor,
-                           N_("is the first one ancestor of the other?")),
+               OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
+               OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")),
+               OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")),
+               OPT_BOOL(0, "is-ancestor", &is_ancestor,
+                        N_("is the first one ancestor of the other?")),
                OPT_END()
        };
 
index c0570f24072a8e29a13e00a2f4fe1b5054ebac7c..844f84f40bf0f8b9d95f4e9f0496c010386a9fb8 100644 (file)
@@ -30,7 +30,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int prefixlen = 0;
        struct option options[] = {
-               OPT_BOOLEAN('p', "stdout", &to_stdout, N_("send results to standard output")),
+               OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
                OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
                OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
                            XDL_MERGE_FAVOR_OURS),
index 34a6166b520c8ede9973fbf497b21ac6ecb761ad..a8cf4a248492fc793c433e61e025fb7f3b3eacfe 100644 (file)
@@ -197,15 +197,15 @@ static struct option builtin_merge_options[] = {
        { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
                N_("do not show a diffstat at the end of the merge"),
                PARSE_OPT_NOARG, option_parse_n },
-       OPT_BOOLEAN(0, "stat", &show_diffstat,
+       OPT_BOOL(0, "stat", &show_diffstat,
                N_("show a diffstat at the end of the merge")),
-       OPT_BOOLEAN(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+       OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
        { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
          N_("add (at most <n>) entries from shortlog to merge commit message"),
          PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
-       OPT_BOOLEAN(0, "squash", &squash,
+       OPT_BOOL(0, "squash", &squash,
                N_("create a single commit instead of doing a merge")),
-       OPT_BOOLEAN(0, "commit", &option_commit,
+       OPT_BOOL(0, "commit", &option_commit,
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
@@ -224,12 +224,12 @@ static struct option builtin_merge_options[] = {
                N_("merge commit message (for a non-fast-forward merge)"),
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
-       OPT_BOOLEAN(0, "abort", &abort_current_merge,
+       OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
        OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
        { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-       OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+       OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_END()
 };
 
index 034fec92a11d79f28d80171ca86394817084ee09..aec79d18386b52a943b20e6ebe0dfc9b6f074f0f 100644 (file)
@@ -9,14 +9,16 @@
 #include "cache-tree.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "submodule.h"
 
 static const char * const builtin_mv_usage[] = {
        N_("git mv [options] <source>... <destination>"),
        NULL
 };
 
-static const char **copy_pathspec(const char *prefix, const char **pathspec,
-                                 int count, int base_name)
+static const char **internal_copy_pathspec(const char *prefix,
+                                          const char **pathspec,
+                                          int count, int base_name)
 {
        int i;
        const char **result = xmalloc((count + 1) * sizeof(const char *));
@@ -56,20 +58,21 @@ static struct lock_file lock_file;
 
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
-       int i, newfd;
+       int i, newfd, gitmodules_modified = 0;
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT__DRY_RUN(&show_only, N_("dry run")),
                OPT__FORCE(&force, N_("force move/rename even if target exists")),
-               OPT_BOOLEAN('k', NULL, &ignore_errors, N_("skip move/rename errors")),
+               OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
                OPT_END(),
        };
-       const char **source, **destination, **dest_path;
+       const char **source, **destination, **dest_path, **submodule_gitfile;
        enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
        struct stat st;
        struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
 
+       gitmodules_config();
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, builtin_mv_options,
@@ -81,17 +84,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
-       source = copy_pathspec(prefix, argv, argc, 0);
+       source = internal_copy_pathspec(prefix, argv, argc, 0);
        modes = xcalloc(argc, sizeof(enum update_mode));
-       dest_path = copy_pathspec(prefix, argv + argc, 1, 0);
+       dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
+       submodule_gitfile = xcalloc(argc, sizeof(char *));
 
        if (dest_path[0][0] == '\0')
                /* special case: "." was normalized to "" */
-               destination = copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
        else if (!lstat(dest_path[0], &st) &&
                        S_ISDIR(st.st_mode)) {
                dest_path[0] = add_slash(dest_path[0]);
-               destination = copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
        } else {
                if (argc != 1)
                        die("destination '%s' is not a directory", dest_path[0]);
@@ -117,55 +121,68 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                && lstat(dst, &st) == 0)
                        bad = _("cannot move directory over file");
                else if (src_is_dir) {
-                       const char *src_w_slash = add_slash(src);
-                       int len_w_slash = length + 1;
-                       int first, last;
-
-                       modes[i] = WORKING_DIRECTORY;
-
-                       first = cache_name_pos(src_w_slash, len_w_slash);
-                       if (first >= 0)
-                               die (_("Huh? %.*s is in index?"),
-                                               len_w_slash, src_w_slash);
-
-                       first = -1 - first;
-                       for (last = first; last < active_nr; last++) {
-                               const char *path = active_cache[last]->name;
-                               if (strncmp(path, src_w_slash, len_w_slash))
-                                       break;
-                       }
-                       free((char *)src_w_slash);
-
-                       if (last - first < 1)
-                               bad = _("source directory is empty");
-                       else {
-                               int j, dst_len;
-
-                               if (last - first > 0) {
-                                       source = xrealloc(source,
-                                                       (argc + last - first)
-                                                       * sizeof(char *));
-                                       destination = xrealloc(destination,
-                                                       (argc + last - first)
-                                                       * sizeof(char *));
-                                       modes = xrealloc(modes,
-                                                       (argc + last - first)
-                                                       * sizeof(enum update_mode));
+                       int first = cache_name_pos(src, length);
+                       if (first >= 0) {
+                               struct strbuf submodule_dotgit = STRBUF_INIT;
+                               if (!S_ISGITLINK(active_cache[first]->ce_mode))
+                                       die (_("Huh? Directory %s is in index and no submodule?"), src);
+                               if (!is_staging_gitmodules_ok())
+                                       die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
+                               strbuf_addf(&submodule_dotgit, "%s/.git", src);
+                               submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
+                               if (submodule_gitfile[i])
+                                       submodule_gitfile[i] = xstrdup(submodule_gitfile[i]);
+                               strbuf_release(&submodule_dotgit);
+                       } else {
+                               const char *src_w_slash = add_slash(src);
+                               int last, len_w_slash = length + 1;
+
+                               modes[i] = WORKING_DIRECTORY;
+
+                               first = cache_name_pos(src_w_slash, len_w_slash);
+                               if (first >= 0)
+                                       die (_("Huh? %.*s is in index?"),
+                                                       len_w_slash, src_w_slash);
+
+                               first = -1 - first;
+                               for (last = first; last < active_nr; last++) {
+                                       const char *path = active_cache[last]->name;
+                                       if (strncmp(path, src_w_slash, len_w_slash))
+                                               break;
                                }
+                               free((char *)src_w_slash);
+
+                               if (last - first < 1)
+                                       bad = _("source directory is empty");
+                               else {
+                                       int j, dst_len;
 
-                               dst = add_slash(dst);
-                               dst_len = strlen(dst);
-
-                               for (j = 0; j < last - first; j++) {
-                                       const char *path =
-                                               active_cache[first + j]->name;
-                                       source[argc + j] = path;
-                                       destination[argc + j] =
-                                               prefix_path(dst, dst_len,
-                                                       path + length + 1);
-                                       modes[argc + j] = INDEX;
+                                       if (last - first > 0) {
+                                               source = xrealloc(source,
+                                                               (argc + last - first)
+                                                               * sizeof(char *));
+                                               destination = xrealloc(destination,
+                                                               (argc + last - first)
+                                                               * sizeof(char *));
+                                               modes = xrealloc(modes,
+                                                               (argc + last - first)
+                                                               * sizeof(enum update_mode));
+                                       }
+
+                                       dst = add_slash(dst);
+                                       dst_len = strlen(dst);
+
+                                       for (j = 0; j < last - first; j++) {
+                                               const char *path =
+                                                       active_cache[first + j]->name;
+                                               source[argc + j] = path;
+                                               destination[argc + j] =
+                                                       prefix_path(dst, dst_len,
+                                                               path + length + 1);
+                                               modes[argc + j] = INDEX;
+                                       }
+                                       argc += last - first;
                                }
-                               argc += last - first;
                        }
                } else if (cache_name_pos(src, length) < 0)
                        bad = _("not under version control");
@@ -210,9 +227,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                int pos;
                if (show_only || verbose)
                        printf(_("Renaming %s to %s\n"), src, dst);
-               if (!show_only && mode != INDEX &&
-                               rename(src, dst) < 0 && !ignore_errors)
-                       die_errno (_("renaming '%s' failed"), src);
+               if (!show_only && mode != INDEX) {
+                       if (rename(src, dst) < 0 && !ignore_errors)
+                               die_errno (_("renaming '%s' failed"), src);
+                       if (submodule_gitfile[i])
+                               connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
+                       if (!update_path_in_gitmodules(src, dst))
+                               gitmodules_modified = 1;
+               }
 
                if (mode == WORKING_DIRECTORY)
                        continue;
@@ -223,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        rename_cache_entry_at(pos, dst);
        }
 
+       if (gitmodules_modified)
+               stage_updated_gitmodules();
+
        if (active_cache_changed) {
                if (write_cache(newfd, active_cache, active_nr) ||
                    commit_locked_index(&lock_file))
index 0aaa19e4abeb462b2f1d05d5e03aa29bee810db8..20fcf8c696700993617649da77e5bbf6b416498a 100644 (file)
@@ -310,15 +310,15 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
-               OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
-               OPT_BOOLEAN(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
+               OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
+               OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
                OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
                                   N_("only use refs matching <pattern>")),
                OPT_GROUP(""),
-               OPT_BOOLEAN(0, "all", &all, N_("list all commits reachable from all refs")),
-               OPT_BOOLEAN(0, "stdin", &transform_stdin, N_("read from stdin")),
-               OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")),
-               OPT_BOOLEAN(0, "always",     &always,
+               OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
+               OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
+               OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
+               OPT_BOOL(0, "always",     &always,
                           N_("show abbreviated commit object as fallback")),
                {
                        /* A Hidden OPT_BOOL */
@@ -331,7 +331,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
-       if (!!all + !!transform_stdin + !!argc > 1) {
+       if (all + transform_stdin + !!argc > 1) {
                error("Specify either a list, or --all, not both!");
                usage_with_options(name_rev_usage, opts);
        }
index e4100c49820306b881a20db4bb3af7c13f12c019..d459e23c4225ae0f8084cfd756563c2b7ab85a60 100644 (file)
@@ -483,7 +483,7 @@ static int copy(int argc, const char **argv, const char *prefix)
        const char *rewrite_cmd = NULL;
        struct option options[] = {
                OPT__FORCE(&force, N_("replace existing notes")),
-               OPT_BOOLEAN(0, "stdin", &from_stdin, N_("read objects from stdin")),
+               OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")),
                OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
                           N_("load rewriting config for <command> (implies "
                              "--stdin)")),
@@ -739,13 +739,13 @@ static int merge(int argc, const char **argv, const char *prefix)
                           N_("resolve notes conflicts using the given strategy "
                              "(manual/ours/theirs/union/cat_sort_uniq)")),
                OPT_GROUP(N_("Committing unmerged notes")),
-               { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
+               { OPTION_SET_INT, 0, "commit", &do_commit, NULL,
                        N_("finalize notes merge by committing unmerged notes"),
-                       PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
                OPT_GROUP(N_("Aborting notes merge resolution")),
-               { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
+               { OPTION_SET_INT, 0, "abort", &do_abort, NULL,
                        N_("abort notes merge"),
-                       PARSE_OPT_NOARG | PARSE_OPT_NONEG },
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
                OPT_END()
        };
 
@@ -853,7 +853,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "ignore-missing", &flag,
                        N_("attempt to remove non-existent note is not an error"),
                        IGNORE_MISSING),
-               OPT_BOOLEAN(0, "stdin", &from_stdin,
+               OPT_BOOL(0, "stdin", &from_stdin,
                            N_("read object names from the standard input")),
                OPT_END()
        };
index f069462cb03bbae46dae8d6b97420b3726690c47..4eb0521c815870abb670e8ddb035caa9c6507e67 100644 (file)
@@ -1809,7 +1809,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
 static void try_to_free_from_threads(size_t size)
 {
        read_lock();
-       release_pack_memory(size, -1);
+       release_pack_memory(size);
        read_unlock();
 }
 
index 04f0eaf179044dce1e87137978bcbecca18f3527..7b1b66c36ac14cb29db9dbbbd5b6f4d27b67d91c 100644 (file)
@@ -15,12 +15,14 @@ static const char * const push_usage[] = {
        NULL,
 };
 
-static int thin;
+static int thin = 1;
 static int deleterefs;
 static const char *receivepack;
 static int verbosity;
 static int progress = -1;
 
+static struct push_cas_option cas;
+
 static const char **refspec;
 static int refspec_nr;
 static int refspec_alloc;
@@ -313,8 +315,14 @@ static int push_with_options(struct transport *transport, int flags)
        if (receivepack)
                transport_set_option(transport,
                                     TRANS_OPT_RECEIVEPACK, receivepack);
-       if (thin)
-               transport_set_option(transport, TRANS_OPT_THIN, "yes");
+       transport_set_option(transport, TRANS_OPT_THIN, thin ? "yes" : NULL);
+
+       if (!is_empty_cas(&cas)) {
+               if (!transport->smart_options)
+                       die("underlying transport does not support --%s option",
+                           CAS_OPT_NAME);
+               transport->smart_options->cas = &cas;
+       }
 
        if (verbosity > 0)
                fprintf(stderr, _("Pushing to %s\n"), transport->url);
@@ -446,15 +454,19 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
                OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
-               OPT_BOOLEAN( 0, "delete", &deleterefs, N_("delete refs")),
-               OPT_BOOLEAN( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+               OPT_BOOL( 0, "delete", &deleterefs, N_("delete refs")),
+               OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
                OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
                OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
+               { OPTION_CALLBACK,
+                 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
+                 N_("require old value of ref to be at this value"),
+                 PARSE_OPT_OPTARG, parseopt_push_cas_option },
                { OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"),
                        N_("control recursive pushing of submodules"),
                        PARSE_OPT_OPTARG, option_parse_recurse_submodules },
-               OPT_BOOLEAN( 0 , "thin", &thin, N_("use thin pack")),
+               OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
                OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"),
index e3eb5fc0588a79b2b6c29bccdaf71ba49dc14c1f..b7e71a04f61798ed72282f683a6fcd3c156007ab 100644 (file)
@@ -8,6 +8,7 @@
 #include "commit.h"
 #include "object.h"
 #include "remote.h"
+#include "connect.h"
 #include "transport.h"
 #include "string-list.h"
 #include "sha1-array.h"
@@ -38,6 +39,7 @@ static int quiet;
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
+static int fix_thin = 1;
 static const char *head_name;
 static void *head_name_to_free;
 static int sent_capabilities;
@@ -869,7 +871,8 @@ static const char *unpack(int err_fd)
                keeper[i++] = "--stdin";
                if (fsck_objects)
                        keeper[i++] = "--strict";
-               keeper[i++] = "--fix-thin";
+               if (fix_thin)
+                       keeper[i++] = "--fix-thin";
                keeper[i++] = hdr_arg;
                keeper[i++] = keep_arg;
                keeper[i++] = NULL;
@@ -975,6 +978,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                                stateless_rpc = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--reject-thin-pack-for-testing")) {
+                               fix_thin = 0;
+                               continue;
+                       }
 
                        usage(receive_pack_usage);
                }
index 54184b3d133bb66db272fd21ad3cad53a4a9a5cb..ba27f7cc614214eb32f60b684b3617c39a0066ef 100644 (file)
@@ -94,8 +94,7 @@ static int tree_is_complete(const unsigned char *sha1)
                        complete = 0;
                }
        }
-       free(tree->buffer);
-       tree->buffer = NULL;
+       free_tree_buffer(tree);
 
        if (complete)
                tree->object.flags |= SEEN;
index 5e54d367b82cd91dbba70e2570dc620a3a13cd21..eaac3e20124c48456faab48a6529ed281075ee25 100644 (file)
@@ -160,7 +160,7 @@ static int add(int argc, const char **argv)
        int i;
 
        struct option options[] = {
-               OPT_BOOLEAN('f', "fetch", &fetch, N_("fetch the remote branches")),
+               OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")),
                OPT_SET_INT(0, "tags", &fetch_tags,
                            N_("import all tags and associated objects when fetching"),
                            TAGS_SET),
@@ -1088,7 +1088,7 @@ static int show(int argc, const char **argv)
 {
        int no_query = 0, result = 0, query_flag = 0;
        struct option options[] = {
-               OPT_BOOLEAN('n', NULL, &no_query, N_("do not query remotes")),
+               OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")),
                OPT_END()
        };
        struct ref_states states;
@@ -1195,10 +1195,10 @@ static int set_head(int argc, const char **argv)
        char *head_name = NULL;
 
        struct option options[] = {
-               OPT_BOOLEAN('a', "auto", &opt_a,
-                           N_("set refs/remotes/<name>/HEAD according to remote")),
-               OPT_BOOLEAN('d', "delete", &opt_d,
-                           N_("delete refs/remotes/<name>/HEAD")),
+               OPT_BOOL('a', "auto", &opt_a,
+                        N_("set refs/remotes/<name>/HEAD according to remote")),
+               OPT_BOOL('d', "delete", &opt_d,
+                        N_("delete refs/remotes/<name>/HEAD")),
                OPT_END()
        };
        argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
@@ -1317,8 +1317,8 @@ static int update(int argc, const char **argv)
 {
        int i, prune = 0;
        struct option options[] = {
-               OPT_BOOLEAN('p', "prune", &prune,
-                           N_("prune remotes after fetching")),
+               OPT_BOOL('p', "prune", &prune,
+                        N_("prune remotes after fetching")),
                OPT_END()
        };
        const char **fetch_argv;
@@ -1404,7 +1404,7 @@ static int set_branches(int argc, const char **argv)
 {
        int add_mode = 0;
        struct option options[] = {
-               OPT_BOOLEAN('\0', "add", &add_mode, N_("add branch")),
+               OPT_BOOL('\0', "add", &add_mode, N_("add branch")),
                OPT_END()
        };
 
@@ -1432,11 +1432,11 @@ static int set_url(int argc, const char **argv)
        int urlset_nr;
        struct strbuf name_buf = STRBUF_INIT;
        struct option options[] = {
-               OPT_BOOLEAN('\0', "push", &push_mode,
-                           N_("manipulate push URLs")),
-               OPT_BOOLEAN('\0', "add", &add_mode,
-                           N_("add URL")),
-               OPT_BOOLEAN('\0', "delete", &delete_mode,
+               OPT_BOOL('\0', "push", &push_mode,
+                        N_("manipulate push URLs")),
+               OPT_BOOL('\0', "add", &add_mode,
+                        N_("add URL")),
+               OPT_BOOL('\0', "delete", &delete_mode,
                            N_("delete URLs")),
                OPT_END()
        };
index 59d31152d0068dd4a542d2c8e076c629d508500f..11b0a55ae7f6e3d1a04eeb4026b151144d0315ba 100644 (file)
@@ -118,9 +118,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int list = 0, delete = 0, force = 0;
        struct option options[] = {
-               OPT_BOOLEAN('l', NULL, &list, N_("list replace refs")),
-               OPT_BOOLEAN('d', NULL, &delete, N_("delete replace refs")),
-               OPT_BOOLEAN('f', NULL, &force, N_("replace the ref if it exists")),
+               OPT_BOOL('l', NULL, &list, N_("list replace refs")),
+               OPT_BOOL('d', NULL, &delete, N_("delete replace refs")),
+               OPT_BOOL('f', NULL, &force, N_("replace the ref if it exists")),
                OPT_END()
        };
 
index dc1708e6d6de54b5aaec36d5ff4242bd60183f0a..4e51addb3e135465bdb728bb00c5dc3dd53b7c51 100644 (file)
@@ -6,6 +6,7 @@
 #include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
+#include "pathspec.h"
 
 static const char * const rerere_usage[] = {
        N_("git rerere [clear | forget path... | status | remaining | diff | gc]"),
@@ -68,11 +69,12 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
                return rerere(flags);
 
        if (!strcmp(argv[0], "forget")) {
-               const char **pathspec;
+               struct pathspec pathspec;
                if (argc < 2)
                        warning("'git rerere forget' without paths is deprecated");
-               pathspec = get_pathspec(prefix, argv + 1);
-               return rerere_forget(pathspec);
+               parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
+                              prefix, argv + 1);
+               return rerere_forget(&pathspec);
        }
 
        fd = setup_rerere(&merge_rr, flags);
index afa6e020e83ebac739db474c278e44bc22079848..5e4c551531896ebd993d2a2c2903e4b7d257ed31 100644 (file)
@@ -133,12 +133,13 @@ static void update_index_from_diff(struct diff_queue_struct *q,
        }
 }
 
-static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
+static int read_from_tree(const struct pathspec *pathspec,
+                         unsigned char *tree_sha1)
 {
        struct diff_options opt;
 
        memset(&opt, 0, sizeof(opt));
-       diff_tree_setup_paths(pathspec, &opt);
+       copy_pathspec(&opt.pathspec, pathspec);
        opt.output_format = DIFF_FORMAT_CALLBACK;
        opt.format_callback = update_index_from_diff;
 
@@ -147,7 +148,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
                return 1;
        diffcore_std(&opt);
        diff_flush(&opt);
-       diff_tree_release_paths(&opt);
+       free_pathspec(&opt.pathspec);
 
        return 0;
 }
@@ -174,7 +175,10 @@ static void die_if_unmerged_cache(int reset_type)
 
 }
 
-static const char **parse_args(const char **argv, const char *prefix, const char **rev_ret)
+static void parse_args(struct pathspec *pathspec,
+                      const char **argv, const char *prefix,
+                      int patch_mode,
+                      const char **rev_ret)
 {
        const char *rev = "HEAD";
        unsigned char unused[20];
@@ -216,7 +220,10 @@ static const char **parse_args(const char **argv, const char *prefix, const char
                }
        }
        *rev_ret = rev;
-       return argv[0] ? get_pathspec(prefix, argv) : NULL;
+       parse_pathspec(pathspec, 0,
+                      PATHSPEC_PREFER_FULL |
+                      (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
+                      prefix, argv);
 }
 
 static int update_refs(const char *rev, const unsigned char *sha1)
@@ -246,7 +253,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        int patch_mode = 0, unborn;
        const char *rev;
        unsigned char sha1[20];
-       const char **pathspec = NULL;
+       struct pathspec pathspec;
        const struct option options[] = {
                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
                OPT_SET_INT(0, "mixed", &reset_type,
@@ -258,7 +265,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                N_("reset HEAD, index and working tree"), MERGE),
                OPT_SET_INT(0, "keep", &reset_type,
                                N_("reset HEAD but keep local changes"), KEEP),
-               OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
+               OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
                OPT_END()
        };
 
@@ -266,13 +273,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, git_reset_usage,
                                                PARSE_OPT_KEEP_DASHDASH);
-       pathspec = parse_args(argv, prefix, &rev);
+       parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
        unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
        if (unborn) {
                /* reset on unborn branch: treat as reset to empty tree */
                hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
-       } else if (!pathspec) {
+       } else if (!pathspec.nr) {
                struct commit *commit;
                if (get_sha1_committish(rev, sha1))
                        die(_("Failed to resolve '%s' as a valid revision."), rev);
@@ -293,13 +300,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (patch_mode) {
                if (reset_type != NONE)
                        die(_("--patch is incompatible with --{hard,mixed,soft}"));
-               return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec);
+               return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", &pathspec);
        }
 
        /* git reset tree [--] paths... can be used to
         * load chosen paths from the tree into the index without
         * affecting the working tree nor HEAD. */
-       if (pathspec) {
+       if (pathspec.nr) {
                if (reset_type == MIXED)
                        warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
                else if (reset_type != NONE)
@@ -326,7 +333,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
                int newfd = hold_locked_index(lock, 1);
                if (reset_type == MIXED) {
-                       if (read_from_tree(pathspec, sha1))
+                       if (read_from_tree(&pathspec, sha1))
                                return 1;
                } else {
                        int err = reset_index(sha1, reset_type, quiet);
@@ -347,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        die(_("Could not write new index file."));
        }
 
-       if (!pathspec && !unborn) {
+       if (!pathspec.nr && !unborn) {
                /* Any resets without paths update HEAD to the head being
                 * switched to, saving the previous head in ORIG_HEAD before. */
                update_ref_status = update_refs(rev, sha1);
@@ -355,7 +362,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                if (reset_type == HARD && !update_ref_status && !quiet)
                        print_new_head_line(lookup_commit_reference(sha1));
        }
-       if (!pathspec)
+       if (!pathspec.nr)
                remove_branch_state();
 
        return update_ref_status;
index de894c75773c78afb2c0f1ea4ea5aff8ec7de6bc..c76b89dc5bcccb42f7c2613072b1093a58c7d21c 100644 (file)
@@ -346,9 +346,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                NULL
        };
        static struct option parseopt_opts[] = {
-               OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
+               OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
                                        N_("keep the `--` passed as an arg")),
-               OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
+               OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
                                        N_("stop parsing after the "
                                           "first non-option argument")),
                OPT_END(),
@@ -486,21 +486,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("--sq-quote", argv[1]))
                return cmd_sq_quote(argc - 2, argv + 2);
 
-       if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
-               int i;
-               for (i = 0; local_repo_env[i]; i++)
-                       printf("%s\n", local_repo_env[i]);
-               return 0;
-       }
-
-       if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
-               const char *gitdir = resolve_gitdir(argv[2]);
-               if (!gitdir)
-                       die("not a gitdir '%s'", argv[2]);
-               puts(gitdir);
-               return 0;
-       }
-
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
 
@@ -661,6 +646,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_remote_ref(show_reference, NULL);
                                continue;
                        }
+                       if (!strcmp(arg, "--local-env-vars")) {
+                               int i;
+                               for (i = 0; local_repo_env[i]; i++)
+                                       printf("%s\n", local_repo_env[i]);
+                               continue;
+                       }
                        if (!strcmp(arg, "--show-toplevel")) {
                                const char *work_tree = get_git_work_tree();
                                if (work_tree)
@@ -711,6 +702,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
                                continue;
                        }
+                       if (!strcmp(arg, "--resolve-git-dir")) {
+                               const char *gitdir = resolve_gitdir(argv[i+1]);
+                               if (!gitdir)
+                                       die("not a gitdir '%s'", argv[i+1]);
+                               puts(gitdir);
+                               continue;
+                       }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
                                printf("%s\n", is_inside_git_dir() ? "true"
                                                : "false");
index 1d2648b756907f467edbe1e206535009df4c65e8..8e87acd12e622957df6bc0a93903898361149f91 100644 (file)
@@ -71,44 +71,19 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
                die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
 }
 
-LAST_ARG_MUST_BE_NULL
-static void verify_opt_mutually_compatible(const char *me, ...)
-{
-       const char *opt1, *opt2 = NULL;
-       va_list ap;
-
-       va_start(ap, me);
-       while ((opt1 = va_arg(ap, const char *))) {
-               if (va_arg(ap, int))
-                       break;
-       }
-       if (opt1) {
-               while ((opt2 = va_arg(ap, const char *))) {
-                       if (va_arg(ap, int))
-                               break;
-               }
-       }
-       va_end(ap);
-
-       if (opt1 && opt2)
-               die(_("%s: %s cannot be used with %s"), me, opt1, opt2);
-}
-
 static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 {
        const char * const * usage_str = revert_or_cherry_pick_usage(opts);
        const char *me = action_name(opts);
-       int remove_state = 0;
-       int contin = 0;
-       int rollback = 0;
+       int cmd = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "quit", &remove_state, N_("end revert or cherry-pick sequence")),
-               OPT_BOOLEAN(0, "continue", &contin, N_("resume revert or cherry-pick sequence")),
-               OPT_BOOLEAN(0, "abort", &rollback, N_("cancel revert or cherry-pick sequence")),
-               OPT_BOOLEAN('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
-               OPT_BOOLEAN('e', "edit", &opts->edit, N_("edit the commit message")),
+               OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
+               OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
+               OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
+               OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
+               OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
-               OPT_BOOLEAN('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
+               OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
                OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
                OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
@@ -124,11 +99,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 
        if (opts->action == REPLAY_PICK) {
                struct option cp_extra[] = {
-                       OPT_BOOLEAN('x', NULL, &opts->record_origin, N_("append commit name")),
-                       OPT_BOOLEAN(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
-                       OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
-                       OPT_BOOLEAN(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
-                       OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+                       OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")),
+                       OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
+                       OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
+                       OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
+                       OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
                        OPT_END(),
                };
                if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
@@ -139,23 +114,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                        PARSE_OPT_KEEP_ARGV0 |
                        PARSE_OPT_KEEP_UNKNOWN);
 
-       /* Check for incompatible subcommands */
-       verify_opt_mutually_compatible(me,
-                               "--quit", remove_state,
-                               "--continue", contin,
-                               "--abort", rollback,
-                               NULL);
-
        /* implies allow_empty */
        if (opts->keep_redundant_commits)
                opts->allow_empty = 1;
 
        /* Set the subcommand */
-       if (remove_state)
+       if (cmd == 'q')
                opts->subcommand = REPLAY_REMOVE_STATE;
-       else if (contin)
+       else if (cmd == 'c')
                opts->subcommand = REPLAY_CONTINUE;
-       else if (rollback)
+       else if (cmd == 'a')
                opts->subcommand = REPLAY_ROLLBACK;
        else
                opts->subcommand = REPLAY_NONE;
index 0df0b4d942c09769ff42539f6aaf83dcc31e20fb..9b59ab3a64e00cfe3a6a73d6493001085c31d683 100644 (file)
@@ -11,6 +11,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "submodule.h"
+#include "pathspec.h"
 
 static const char * const builtin_rm_usage[] = {
        N_("git rm [options] [--] <file>..."),
@@ -267,10 +268,10 @@ static int ignore_unmatch = 0;
 static struct option builtin_rm_options[] = {
        OPT__DRY_RUN(&show_only, N_("dry run")),
        OPT__QUIET(&quiet, N_("do not list removed files")),
-       OPT_BOOLEAN( 0 , "cached",         &index_only, N_("only remove from the index")),
+       OPT_BOOL( 0 , "cached",         &index_only, N_("only remove from the index")),
        OPT__FORCE(&force, N_("override the up-to-date check")),
-       OPT_BOOLEAN('r', NULL,             &recursive,  N_("allow recursive removal")),
-       OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
+       OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
+       OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
                                N_("exit with a zero status even if nothing matched")),
        OPT_END(),
 };
@@ -278,9 +279,10 @@ static struct option builtin_rm_options[] = {
 int cmd_rm(int argc, const char **argv, const char *prefix)
 {
        int i, newfd;
-       const char **pathspec;
+       struct pathspec pathspec;
        char *seen;
 
+       gitmodules_config();
        git_config(git_default_config, NULL);
 
        argc = parse_options(argc, argv, prefix, builtin_rm_options,
@@ -311,31 +313,32 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                }
        }
 
-       pathspec = get_pathspec(prefix, argv);
-       refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL);
+       parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv);
+       refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
 
-       seen = NULL;
-       for (i = 0; pathspec[i] ; i++)
-               /* nothing */;
-       seen = xcalloc(i, 1);
+       seen = xcalloc(pathspec.nr, 1);
 
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
-               if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+               if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen))
                        continue;
                ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
                list.entry[list.nr].name = ce->name;
-               list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode);
+               list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
+               if (list.entry[list.nr++].is_submodule &&
+                   !is_staging_gitmodules_ok())
+                       die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
        }
 
-       if (pathspec) {
-               const char *match;
+       if (pathspec.nr) {
+               const char *original;
                int seen_any = 0;
-               for (i = 0; (match = pathspec[i]) != NULL ; i++) {
+               for (i = 0; i < pathspec.nr; i++) {
+                       original = pathspec.items[i].original;
                        if (!seen[i]) {
                                if (!ignore_unmatch) {
                                        die(_("pathspec '%s' did not match any files"),
-                                           match);
+                                           original);
                                }
                        }
                        else {
@@ -343,10 +346,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        }
                        if (!recursive && seen[i] == MATCHED_RECURSIVELY)
                                die(_("not removing '%s' recursively without -r"),
-                                   *match ? match : ".");
+                                   *original ? original : ".");
                }
 
-               if (! seen_any)
+               if (!seen_any)
                        exit(0);
        }
 
@@ -396,13 +399,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         * in the middle)
         */
        if (!index_only) {
-               int removed = 0;
+               int removed = 0, gitmodules_modified = 0;
                for (i = 0; i < list.nr; i++) {
                        const char *path = list.entry[i].name;
                        if (list.entry[i].is_submodule) {
                                if (is_empty_dir(path)) {
                                        if (!rmdir(path)) {
                                                removed = 1;
+                                               if (!remove_path_from_gitmodules(path))
+                                                       gitmodules_modified = 1;
                                                continue;
                                        }
                                } else {
@@ -410,9 +415,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                                        strbuf_addstr(&buf, path);
                                        if (!remove_dir_recursively(&buf, 0)) {
                                                removed = 1;
+                                               if (!remove_path_from_gitmodules(path))
+                                                       gitmodules_modified = 1;
                                                strbuf_release(&buf);
                                                continue;
-                                       }
+                                       } else if (!file_exists(path))
+                                               /* Submodule was removed by user */
+                                               if (!remove_path_from_gitmodules(path))
+                                                       gitmodules_modified = 1;
                                        strbuf_release(&buf);
                                        /* Fallthrough and let remove_path() fail. */
                                }
@@ -424,6 +434,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        if (!removed)
                                die_errno("git rm: '%s'", path);
                }
+               if (gitmodules_modified)
+                       stage_updated_gitmodules();
        }
 
        if (active_cache_changed) {
index 152c4ea092a44ff9e70e293ddeecb4ad7105e25d..4482f16efb66c7fc06c7446a2e992840cc83f798 100644 (file)
@@ -5,6 +5,7 @@
 #include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
+#include "connect.h"
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
@@ -54,6 +55,11 @@ static void print_helper_status(struct ref *ref)
                        msg = "needs force";
                        break;
 
+               case REF_STATUS_REJECT_STALE:
+                       res = "error";
+                       msg = "stale info";
+                       break;
+
                case REF_STATUS_REJECT_ALREADY_EXISTS:
                        res = "error";
                        msg = "already exists";
@@ -102,6 +108,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        int flags;
        unsigned int reject_reasons;
        int progress = -1;
+       struct push_cas_option cas = {0};
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -164,6 +171,22 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                helper_status = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--" CAS_OPT_NAME)) {
+                               if (parse_push_cas_option(&cas, NULL, 0) < 0)
+                                       exit(1);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--no-" CAS_OPT_NAME)) {
+                               if (parse_push_cas_option(&cas, NULL, 1) < 0)
+                                       exit(1);
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) {
+                               if (parse_push_cas_option(&cas,
+                                                         strchr(arg, '=') + 1, 0) < 0)
+                                       exit(1);
+                               continue;
+                       }
                        usage(send_pack_usage);
                }
                if (!dest) {
@@ -224,6 +247,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
                return -1;
 
+       if (!is_empty_cas(&cas))
+               apply_push_cas(&cas, remote, remote_refs);
+
        set_ref_status_for_push(remote_refs, args.send_mirror,
                args.force_update);
 
index 1434f8fee487751abe069250c611bdaed2fa5857..ae73d17b6cbc22f3a7eb30aff5a14cda27c5876c 100644 (file)
@@ -224,12 +224,12 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        int nongit = !startup_info->have_repository;
 
        static const struct option options[] = {
-               OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
-                           N_("sort output according to the number of commits per author")),
-               OPT_BOOLEAN('s', "summary", &log.summary,
-                           N_("Suppress commit descriptions, only provides commit count")),
-               OPT_BOOLEAN('e', "email", &log.email,
-                           N_("Show the email address of each author")),
+               OPT_BOOL('n', "numbered", &log.sort_by_number,
+                        N_("sort output according to the number of commits per author")),
+               OPT_BOOL('s', "summary", &log.summary,
+                        N_("Suppress commit descriptions, only provides commit count")),
+               OPT_BOOL('e', "email", &log.email,
+                        N_("Show the email address of each author")),
                { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"),
                        N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args },
                OPT_END(),
index 9788eb115beb2ab5663bd1d318ce411099589321..001f29ca1b9ef8bfe2a06cda4d98c704584812fe 100644 (file)
@@ -646,30 +646,30 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int dense = 1;
        const char *reflog_base = NULL;
        struct option builtin_show_branch_options[] = {
-               OPT_BOOLEAN('a', "all", &all_heads,
-                           N_("show remote-tracking and local branches")),
-               OPT_BOOLEAN('r', "remotes", &all_remotes,
-                           N_("show remote-tracking branches")),
+               OPT_BOOL('a', "all", &all_heads,
+                        N_("show remote-tracking and local branches")),
+               OPT_BOOL('r', "remotes", &all_remotes,
+                        N_("show remote-tracking branches")),
                OPT__COLOR(&showbranch_use_color,
                            N_("color '*!+-' corresponding to the branch")),
                { OPTION_INTEGER, 0, "more", &extra, N_("n"),
                            N_("show <n> more commits after the common ancestor"),
                            PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
                OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1),
-               OPT_BOOLEAN(0, "no-name", &no_name, N_("suppress naming strings")),
-               OPT_BOOLEAN(0, "current", &with_current_branch,
-                           N_("include the current branch")),
-               OPT_BOOLEAN(0, "sha1-name", &sha1_name,
-                           N_("name commits with their object names")),
-               OPT_BOOLEAN(0, "merge-base", &merge_base,
-                           N_("show possible merge bases")),
-               OPT_BOOLEAN(0, "independent", &independent,
+               OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")),
+               OPT_BOOL(0, "current", &with_current_branch,
+                        N_("include the current branch")),
+               OPT_BOOL(0, "sha1-name", &sha1_name,
+                        N_("name commits with their object names")),
+               OPT_BOOL(0, "merge-base", &merge_base,
+                        N_("show possible merge bases")),
+               OPT_BOOL(0, "independent", &independent,
                            N_("show refs unreachable from any other ref")),
                OPT_SET_INT(0, "topo-order", &sort_order,
                            N_("show commits in topological order"),
                            REV_SORT_IN_GRAPH_ORDER),
-               OPT_BOOLEAN(0, "topics", &topics,
-                           N_("show only commits not on the first branch")),
+               OPT_BOOL(0, "topics", &topics,
+                        N_("show only commits not on the first branch")),
                OPT_SET_INT(0, "sparse", &dense,
                            N_("show merges reachable from only one tip"), 0),
                OPT_SET_INT(0, "date-order", &sort_order,
index 87806ad5b088f5c905757a4fab3c3499ec20a308..9f3f5e370bfb123df5c662bead122b29622fc1dd 100644 (file)
@@ -165,16 +165,15 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
 }
 
 static const struct option show_ref_options[] = {
-       OPT_BOOLEAN(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
-       OPT_BOOLEAN(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
-       OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, "
+       OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
+       OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
+       OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, "
                    "requires exact ref path")),
-       { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
-         N_("show the HEAD reference, even if it would be filtered out"),
-         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       OPT_BOOLEAN(0, "head", &show_head,
+       OPT_HIDDEN_BOOL('h', NULL, &show_head,
+                       N_("show the HEAD reference, even if it would be filtered out")),
+       OPT_BOOL(0, "head", &show_head,
          N_("show the HEAD reference, even if it would be filtered out")),
-       OPT_BOOLEAN('d', "dereference", &deref_tags,
+       OPT_BOOL('d', "dereference", &deref_tags,
                    N_("dereference tags into object IDs")),
        { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
          N_("only show SHA1 hash using <n> digits"),
index af3af3f64935c3f92ed3f85acd44ed2ca2a35b3f..b577af562a4a554f43c64256e112fcbc423edd3d 100644 (file)
@@ -436,26 +436,26 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct ref_lock *lock;
        struct create_tag_options opt;
        char *cleanup_arg = NULL;
-       int annotate = 0, force = 0, lines = -1, list = 0,
-               delete = 0, verify = 0;
+       int annotate = 0, force = 0, lines = -1;
+       int cmdmode = 0;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct commit_list *with_commit = NULL;
        struct option options[] = {
-               OPT_BOOLEAN('l', "list", &list, N_("list tag names")),
+               OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
                { OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
                                N_("print <n> lines of each tag message"),
                                PARSE_OPT_OPTARG, NULL, 1 },
-               OPT_BOOLEAN('d', "delete", &delete, N_("delete tags")),
-               OPT_BOOLEAN('v', "verify", &verify, N_("verify tags")),
+               OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
+               OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
 
                OPT_GROUP(N_("Tag creation options")),
-               OPT_BOOLEAN('a', "annotate", &annotate,
+               OPT_BOOL('a', "annotate", &annotate,
                                        N_("annotated tag, needs a message")),
                OPT_CALLBACK('m', "message", &msg, N_("message"),
                             N_("tag message"), parse_msg_arg),
                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
-               OPT_BOOLEAN('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
+               OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
                        N_("how to strip spaces and #comments from message")),
                OPT_STRING('u', "local-user", &keyid, N_("key id"),
@@ -489,22 +489,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        }
        if (opt.sign)
                annotate = 1;
-       if (argc == 0 && !(delete || verify))
-               list = 1;
+       if (argc == 0 && !cmdmode)
+               cmdmode = 'l';
 
-       if ((annotate || msg.given || msgfile || force) &&
-           (list || delete || verify))
+       if ((annotate || msg.given || msgfile || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
 
-       if (list + delete + verify > 1)
-               usage_with_options(git_tag_usage, options);
        finalize_colopts(&colopts, -1);
-       if (list && lines != -1) {
+       if (cmdmode == 'l' && lines != -1) {
                if (explicitly_enable_column(colopts))
                        die(_("--column and -n are incompatible"));
                colopts = 0;
        }
-       if (list) {
+       if (cmdmode == 'l') {
                int ret;
                if (column_active(colopts)) {
                        struct column_options copts;
@@ -523,9 +520,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die(_("--contains option is only allowed with -l."));
        if (points_at.nr)
                die(_("--points-at option is only allowed with -l."));
-       if (delete)
+       if (cmdmode == 'd')
                return for_each_tag_name(argv, delete_tag);
-       if (verify)
+       if (cmdmode == 'v')
                return for_each_tag_name(argv, verify_tag);
 
        if (msg.given || msgfile) {
index 3f1e7012db1af7d3695e6517956721a313229d65..ba3ffe69a95088d8f5e9978a754cb1f0ee364ba7 100644 (file)
@@ -26,8 +26,8 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)
         * $0 tree-ish basedir ==>
         *      git archive --format-tar --prefix=basedir tree-ish
         */
-       int i;
        const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
+       struct strbuf sb = STRBUF_INIT;
        char *basedir_arg;
        int nargc = 0;
 
@@ -65,11 +65,10 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)
        fprintf(stderr,
                "*** \"git tar-tree\" is now deprecated.\n"
                "*** Running \"git archive\" instead.\n***");
-       for (i = 0; i < nargc; i++) {
-               fputc(' ', stderr);
-               sq_quote_print(stderr, nargv[i]);
-       }
-       fputc('\n', stderr);
+       sq_quote_argv(&sb, nargv, 0);
+       strbuf_addch(&sb, '\n');
+       fputs(sb.buf, stderr);
+       strbuf_release(&sb);
        return cmd_archive(nargc, nargv, prefix);
 }
 
index c3179815164c1ba2b39f3f57690c607dfc3ea0b6..e3a10d706d406845b74b2cc0c9e9a120be6adce0 100644 (file)
@@ -11,6 +11,7 @@
 #include "refs.h"
 #include "resolve-undo.h"
 #include "parse-options.h"
+#include "pathspec.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -546,10 +547,11 @@ static int do_reupdate(int ac, const char **av,
         */
        int pos;
        int has_head = 1;
-       const char **paths = get_pathspec(prefix, av + 1);
        struct pathspec pathspec;
 
-       init_pathspec(&pathspec, paths);
+       parse_pathspec(&pathspec, 0,
+                      PATHSPEC_PREFER_CWD,
+                      prefix, av + 1);
 
        if (read_ref("HEAD", head_sha1))
                /* If there is no HEAD, that means it is an initial
index 51d26848598d7f18749f1f2859d7a74558d60b69..7484d36a65ba1f722f2dda33473bf509f30727c9 100644 (file)
@@ -16,8 +16,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        int delete = 0, no_deref = 0, flags = 0;
        struct option options[] = {
                OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
-               OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")),
-               OPT_BOOLEAN( 0 , "no-deref", &no_deref,
+               OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
+               OPT_BOOL( 0 , "no-deref", &no_deref,
                                        N_("update <refname> not the one it points to")),
                OPT_END(),
        };
index 6b0b6d490440cc59f8ef451c5da9bcff5b3e1563..118c62528b0bb6c919d146d60516825a5336fe75 100644 (file)
@@ -114,7 +114,7 @@ static int stream_to_pack(struct bulk_checkin_state *state,
 
                if (size && !s.avail_in) {
                        ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
-                       if (xread(fd, ibuf, rsize) != rsize)
+                       if (read_in_full(fd, ibuf, rsize) != rsize)
                                die("failed to read %d bytes from '%s'",
                                    (int)rsize, path);
                        offset += rsize;
diff --git a/cache.h b/cache.h
index 85b544f38d934fe68d1e155c86337f1400eea14d..a47b9c03036d41e8067a843dda305bc9b6161255 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -101,9 +101,9 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
 
 #define CACHE_SIGNATURE 0x44495243     /* "DIRC" */
 struct cache_header {
-       unsigned int hdr_signature;
-       unsigned int hdr_version;
-       unsigned int hdr_entries;
+       uint32_t hdr_signature;
+       uint32_t hdr_version;
+       uint32_t hdr_entries;
 };
 
 #define INDEX_FORMAT_LB 2
@@ -115,8 +115,8 @@ struct cache_header {
  * check it for equality in the 32 bits we save.
  */
 struct cache_time {
-       unsigned int sec;
-       unsigned int nsec;
+       uint32_t sec;
+       uint32_t nsec;
 };
 
 struct stat_data {
@@ -189,6 +189,8 @@ struct cache_entry {
 #error "CE_EXTENDED_FLAGS out of range"
 #endif
 
+struct pathspec;
+
 /*
  * Copy the sha1 and stat state of a cache entry from one to
  * another. But we never change the name, or the hash state!
@@ -365,6 +367,9 @@ static inline enum object_type object_type(unsigned int mode)
 #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
 #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
 #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
+#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
+#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
+#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
 
 /*
  * This environment variable is expected to contain a boolean indicating
@@ -412,6 +417,7 @@ extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern char *prefix_path(const char *prefix, int len, const char *path);
+extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 extern const char *prefix_filename(const char *prefix, int len, const char *path);
 extern int check_filename(const char *prefix, const char *name);
 extern void verify_filename(const char *prefix,
@@ -449,7 +455,7 @@ extern void sanitize_stdfds(void);
 
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
-extern int read_index_preload(struct index_state *, const char **pathspec);
+extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 extern int read_index_from(struct index_state *, const char *path);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
@@ -491,28 +497,8 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig
 extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
-#define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
-
-struct pathspec {
-       const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
-       int nr;
-       unsigned int has_wildcard:1;
-       unsigned int recursive:1;
-       int max_depth;
-       struct pathspec_item {
-               const char *match;
-               int len;
-               int nowildcard_len;
-               int flags;
-       } *items;
-};
-
-extern int init_pathspec(struct pathspec *, const char **);
-extern void free_pathspec(struct pathspec *);
 extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 
-extern int limit_pathspec_to_literal(void);
-
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@ -540,7 +526,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 #define REFRESH_IGNORE_MISSING 0x0008  /* ignore non-existent */
 #define REFRESH_IGNORE_SUBMODULES      0x0010  /* ignore submodules */
 #define REFRESH_IN_PORCELAIN   0x0020  /* user friendly output, not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
+extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 
 struct lock_file {
        struct lock_file *next;
@@ -762,6 +748,7 @@ const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
@@ -1038,68 +1025,6 @@ struct pack_entry {
        struct packed_git *p;
 };
 
-struct ref {
-       struct ref *next;
-       unsigned char old_sha1[20];
-       unsigned char new_sha1[20];
-       char *symref;
-       unsigned int
-               force:1,
-               forced_update:1,
-               deletion:1,
-               matched:1;
-
-       /*
-        * Order is important here, as we write to FETCH_HEAD
-        * in numeric order. And the default NOT_FOR_MERGE
-        * should be 0, so that xcalloc'd structures get it
-        * by default.
-        */
-       enum {
-               FETCH_HEAD_MERGE = -1,
-               FETCH_HEAD_NOT_FOR_MERGE = 0,
-               FETCH_HEAD_IGNORE = 1
-       } fetch_head_status;
-
-       enum {
-               REF_STATUS_NONE = 0,
-               REF_STATUS_OK,
-               REF_STATUS_REJECT_NONFASTFORWARD,
-               REF_STATUS_REJECT_ALREADY_EXISTS,
-               REF_STATUS_REJECT_NODELETE,
-               REF_STATUS_REJECT_FETCH_FIRST,
-               REF_STATUS_REJECT_NEEDS_FORCE,
-               REF_STATUS_UPTODATE,
-               REF_STATUS_REMOTE_REJECT,
-               REF_STATUS_EXPECTING_REPORT
-       } status;
-       char *remote_status;
-       struct ref *peer_ref; /* when renaming */
-       char name[FLEX_ARRAY]; /* more */
-};
-
-#define REF_NORMAL     (1u << 0)
-#define REF_HEADS      (1u << 1)
-#define REF_TAGS       (1u << 2)
-
-extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
-
-#define CONNECT_VERBOSE       (1u << 0)
-extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
-extern int finish_connect(struct child_process *conn);
-extern int git_connection_is_socket(struct child_process *conn);
-struct extra_have_objects {
-       int nr, alloc;
-       unsigned char (*array)[20];
-};
-extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
-                                    struct ref **list, unsigned int flags,
-                                    struct extra_have_objects *);
-extern int server_supports(const char *feature);
-extern int parse_feature_request(const char *features, const char *feature);
-extern const char *server_feature_value(const char *feature, int *len_ret);
-extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
-
 extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 /* A hook for count-objects to report invalid files in pack directory */
@@ -1190,6 +1115,7 @@ extern int git_config_with_options(config_fn_t fn, void *,
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
+extern int64_t git_config_int64(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
@@ -1305,7 +1231,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 char **pathspec, int flags);
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
@@ -1339,7 +1265,7 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
-int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
+int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
 char *alias_lookup(const char *alias);
index 88525b37cf461ee922b81fd6848be5efc8e34542..3b92c44880228a94f71a428b0f11bb1caf693c67 100644 (file)
@@ -10,6 +10,7 @@
 #include "refs.h"
 #include "userdiff.h"
 #include "sha1-array.h"
+#include "revision.h"
 
 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
 {
@@ -1305,7 +1306,7 @@ void diff_tree_combined(const unsigned char *sha1,
        int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
 
        diffopts = *opt;
-       diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts);
+       copy_pathspec(&diffopts.pathspec, &opt->pathspec);
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        DIFF_OPT_SET(&diffopts, RECURSIVE);
        DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
@@ -1377,13 +1378,13 @@ void diff_tree_combined(const unsigned char *sha1,
                free(tmp);
        }
 
-       diff_tree_release_paths(&diffopts);
+       free_pathspec(&diffopts.pathspec);
 }
 
 void diff_tree_combined_merge(const struct commit *commit, int dense,
                              struct rev_info *rev)
 {
-       struct commit_list *parent = commit->parents;
+       struct commit_list *parent = get_saved_parents(rev, commit);
        struct sha1_array parents = SHA1_ARRAY_INIT;
 
        while (parent) {
index a575564a15c1c340b36d10c90da8fa343692264d..de16a3c0a2d6693173678247ba0755e4d7483a1c 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -377,6 +377,22 @@ unsigned commit_list_count(const struct commit_list *l)
        return c;
 }
 
+struct commit_list *copy_commit_list(struct commit_list *list)
+{
+       struct commit_list *head = NULL;
+       struct commit_list **pp = &head;
+       while (list) {
+               struct commit_list *new;
+               new = xmalloc(sizeof(struct commit_list));
+               new->item = list->item;
+               new->next = NULL;
+               *pp = new;
+               pp = &new->next;
+               list = list->next;
+       }
+       return head;
+}
+
 void free_commit_list(struct commit_list *list)
 {
        while (list) {
index d912a9d4ac3fe36b2e69d48b1725f01bdf289b26..90a5a3c361e02cd7a9562eff2ca1b15f77be0941 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -62,6 +62,9 @@ struct commit_list *commit_list_insert_by_date(struct commit *item,
                                    struct commit_list **list);
 void commit_list_sort_by_date(struct commit_list **list);
 
+/* Shallow copy of the input list */
+struct commit_list *copy_commit_list(struct commit_list *list);
+
 void free_commit_list(struct commit_list *list);
 
 /* Commit formats */
@@ -205,7 +208,7 @@ int in_merge_bases_many(struct commit *, int, struct commit **);
 
 extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
 extern int run_add_interactive(const char *revision, const char *patch_mode,
-                              const char **pathspec);
+                              const struct pathspec *pathspec);
 
 static inline int single_parent(struct commit *commit)
 {
diff --git a/compat/apple-common-crypto.h b/compat/apple-common-crypto.h
new file mode 100644 (file)
index 0000000..c8b9b0e
--- /dev/null
@@ -0,0 +1,86 @@
+/* suppress inclusion of conflicting openssl functions */
+#define OPENSSL_NO_MD5
+#define HEADER_HMAC_H
+#define HEADER_SHA_H
+#include <CommonCrypto/CommonHMAC.h>
+#define HMAC_CTX CCHmacContext
+#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len)
+#define HMAC_Update CCHmacUpdate
+#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash)
+#define HMAC_CTX_cleanup(ignore)
+#define EVP_md5(...) kCCHmacAlgMD5
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
+#define APPLE_LION_OR_NEWER
+#include <Security/Security.h>
+/* Apple's TYPE_BOOL conflicts with config.c */
+#undef TYPE_BOOL
+#endif
+
+#ifdef APPLE_LION_OR_NEWER
+#define git_CC_error_check(pattern, err) \
+       do { \
+               if (err) { \
+                       die(pattern, (long)CFErrorGetCode(err)); \
+               } \
+       } while(0)
+
+#define EVP_EncodeBlock git_CC_EVP_EncodeBlock
+static inline int git_CC_EVP_EncodeBlock(unsigned char *out,
+               const unsigned char *in, int inlen)
+{
+       CFErrorRef err;
+       SecTransformRef encoder;
+       CFDataRef input, output;
+       CFIndex length;
+
+       encoder = SecEncodeTransformCreate(kSecBase64Encoding, &err);
+       git_CC_error_check("SecEncodeTransformCreate failed: %ld", err);
+
+       input = CFDataCreate(kCFAllocatorDefault, in, inlen);
+       SecTransformSetAttribute(encoder, kSecTransformInputAttributeName,
+                       input, &err);
+       git_CC_error_check("SecTransformSetAttribute failed: %ld", err);
+
+       output = SecTransformExecute(encoder, &err);
+       git_CC_error_check("SecTransformExecute failed: %ld", err);
+
+       length = CFDataGetLength(output);
+       CFDataGetBytes(output, CFRangeMake(0, length), out);
+
+       CFRelease(output);
+       CFRelease(input);
+       CFRelease(encoder);
+
+       return (int)strlen((const char *)out);
+}
+
+#define EVP_DecodeBlock git_CC_EVP_DecodeBlock
+static int inline git_CC_EVP_DecodeBlock(unsigned char *out,
+               const unsigned char *in, int inlen)
+{
+       CFErrorRef err;
+       SecTransformRef decoder;
+       CFDataRef input, output;
+       CFIndex length;
+
+       decoder = SecDecodeTransformCreate(kSecBase64Encoding, &err);
+       git_CC_error_check("SecEncodeTransformCreate failed: %ld", err);
+
+       input = CFDataCreate(kCFAllocatorDefault, in, inlen);
+       SecTransformSetAttribute(decoder, kSecTransformInputAttributeName,
+                       input, &err);
+       git_CC_error_check("SecTransformSetAttribute failed: %ld", err);
+
+       output = SecTransformExecute(decoder, &err);
+       git_CC_error_check("SecTransformExecute failed: %ld", err);
+
+       length = CFDataGetLength(output);
+       CFDataGetBytes(output, CFRangeMake(0, length), out);
+
+       CFRelease(output);
+       CFRelease(input);
+       CFRelease(decoder);
+
+       return (int)strlen((const char *)out);
+}
+#endif /* APPLE_LION_OR_NEWER */
diff --git a/compat/clipped-write.c b/compat/clipped-write.c
deleted file mode 100644 (file)
index b8f98ff..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "../git-compat-util.h"
-#undef write
-
-/*
- * Version of write that will write at most INT_MAX bytes.
- * Workaround a xnu bug on Mac OS X
- */
-ssize_t clipped_write(int fildes, const void *buf, size_t nbyte)
-{
-       if (nbyte > INT_MAX)
-               nbyte = INT_MAX;
-       return write(fildes, buf, nbyte);
-}
index bb92c436f70790ba907991f3307aa0fe4afdf0f5..22ee9ef1cfb388fdcb7a2686653a6b10359bca59 100644 (file)
@@ -1086,6 +1086,12 @@ int mingw_kill(pid_t pid, int sig)
                errno = err_win_to_posix(GetLastError());
                CloseHandle(h);
                return -1;
+       } else if (pid > 0 && sig == 0) {
+               HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+               if (h) {
+                       CloseHandle(h);
+                       return 0;
+               }
        }
 
        errno = EINVAL;
index 7980abd1a71d0676f35eb300c46a30a7298e82f3..95fe849e42d3b9e1f7bc9590fcfac93016f38146 100644 (file)
@@ -48,11 +48,8 @@ void probe_utf8_pathname_composition(char *path, int len)
        if (output_fd >= 0) {
                close(output_fd);
                strcpy(path + len, auml_nfd);
-               /* Indicate to the user, that we can configure it to true */
-               if (!access(path, R_OK))
-                       git_config_set("core.precomposeunicode", "false");
-               /* To be backward compatible, set precomposed_unicode to 0 */
-               precomposed_unicode = 0;
+               precomposed_unicode = access(path, R_OK) ? 0 : 1;
+               git_config_set("core.precomposeunicode", precomposed_unicode ? "true" : "false");
                strcpy(path + len, auml_nfc);
                if (unlink(path))
                        die_errno(_("failed to unlink '%s'"), path);
index e13a7b65e7615da7d788fddd001716a17baef5db..6588cf579f2e44533192027c739b4823562b2275 100644 (file)
--- a/config.c
+++ b/config.c
@@ -27,9 +27,9 @@ struct config_source {
        struct strbuf value;
        struct strbuf var;
 
-       int (*fgetc)(struct config_source *c);
-       int (*ungetc)(int c, struct config_source *conf);
-       long (*ftell)(struct config_source *c);
+       int (*do_fgetc)(struct config_source *c);
+       int (*do_ungetc)(int c, struct config_source *conf);
+       long (*do_ftell)(struct config_source *c);
 };
 
 static struct config_source *cf;
@@ -217,13 +217,13 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 
 static int get_next_char(void)
 {
-       int c = cf->fgetc(cf);
+       int c = cf->do_fgetc(cf);
 
        if (c == '\r') {
                /* DOS like systems */
-               c = cf->fgetc(cf);
+               c = cf->do_fgetc(cf);
                if (c != '\n') {
-                       cf->ungetc(c, cf);
+                       cf->do_ungetc(c, cf);
                        c = '\r';
                }
        }
@@ -468,7 +468,7 @@ static int parse_unit_factor(const char *end, uintmax_t *val)
        return 0;
 }
 
-static int git_parse_long(const char *value, long *ret)
+static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
 {
        if (value && *value) {
                char *end;
@@ -480,21 +480,25 @@ static int git_parse_long(const char *value, long *ret)
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor))
+               if (!parse_unit_factor(end, &factor)) {
+                       errno = EINVAL;
                        return 0;
+               }
                uval = abs(val);
                uval *= factor;
-               if ((uval > maximum_signed_value_of_type(long)) ||
-                   (abs(val) > uval))
+               if (uval > max || abs(val) > uval) {
+                       errno = ERANGE;
                        return 0;
+               }
                val *= factor;
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
-int git_parse_ulong(const char *value, unsigned long *ret)
+int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
 {
        if (value && *value) {
                char *end;
@@ -506,29 +510,75 @@ int git_parse_ulong(const char *value, unsigned long *ret)
                if (errno == ERANGE)
                        return 0;
                oldval = val;
-               if (!parse_unit_factor(end, &val))
+               if (!parse_unit_factor(end, &val)) {
+                       errno = EINVAL;
                        return 0;
-               if ((val > maximum_unsigned_value_of_type(long)) ||
-                   (oldval > val))
+               }
+               if (val > max || oldval > val) {
+                       errno = ERANGE;
                        return 0;
+               }
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
-static void die_bad_config(const char *name)
+static int git_parse_int(const char *value, int *ret)
 {
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+static int git_parse_int64(const char *value, int64_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+       uintmax_t tmp;
+       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+static void die_bad_number(const char *name, const char *value)
+{
+       const char *reason = errno == ERANGE ?
+                            "out of range" :
+                            "invalid unit";
+       if (!value)
+               value = "";
+
        if (cf && cf->name)
-               die("bad config value for '%s' in %s", name, cf->name);
-       die("bad config value for '%s'", name);
+               die("bad numeric config value '%s' for '%s' in %s: %s",
+                   value, name, cf->name, reason);
+       die("bad numeric config value '%s' for '%s': %s", value, name, reason);
 }
 
 int git_config_int(const char *name, const char *value)
 {
-       long ret = 0;
-       if (!git_parse_long(value, &ret))
-               die_bad_config(name);
+       int ret;
+       if (!git_parse_int(value, &ret))
+               die_bad_number(name, value);
+       return ret;
+}
+
+int64_t git_config_int64(const char *name, const char *value)
+{
+       int64_t ret;
+       if (!git_parse_int64(value, &ret))
+               die_bad_number(name, value);
        return ret;
 }
 
@@ -536,7 +586,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
 {
        unsigned long ret;
        if (!git_parse_ulong(value, &ret))
-               die_bad_config(name);
+               die_bad_number(name, value);
        return ret;
 }
 
@@ -559,10 +609,10 @@ static int git_config_maybe_bool_text(const char *name, const char *value)
 
 int git_config_maybe_bool(const char *name, const char *value)
 {
-       long v = git_config_maybe_bool_text(name, value);
+       int v = git_config_maybe_bool_text(name, value);
        if (0 <= v)
                return v;
-       if (git_parse_long(value, &v))
+       if (git_parse_int(value, &v))
                return !!v;
        return -1;
 }
@@ -992,9 +1042,9 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
                top.u.file = f;
                top.name = filename;
                top.die_on_error = 1;
-               top.fgetc = config_file_fgetc;
-               top.ungetc = config_file_ungetc;
-               top.ftell = config_file_ftell;
+               top.do_fgetc = config_file_fgetc;
+               top.do_ungetc = config_file_ungetc;
+               top.do_ftell = config_file_ftell;
 
                ret = do_config_from(&top, fn, data);
 
@@ -1013,9 +1063,9 @@ int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
        top.u.buf.pos = 0;
        top.name = name;
        top.die_on_error = 0;
-       top.fgetc = config_buf_fgetc;
-       top.ungetc = config_buf_ungetc;
-       top.ftell = config_buf_ftell;
+       top.do_fgetc = config_buf_fgetc;
+       top.do_ungetc = config_buf_ungetc;
+       top.do_ftell = config_buf_ftell;
 
        return do_config_from(&top, fn, data);
 }
@@ -1196,7 +1246,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = cf->ftell(cf);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                break;
@@ -1223,19 +1273,19 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
-               store.offset[store.seen] = cf->ftell(cf);
+               store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = cf->ftell(cf);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = cf->ftell(cf);
+                                       store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
        }
index b27f51d4862c67f2dcc61c93f9e2db46f8e44bf6..7d615314f41f33df245efcecbe49f2aee3c90ad7 100644 (file)
@@ -95,7 +95,6 @@ ifeq ($(uname_S),Darwin)
        NO_MEMMEM = YesPlease
        USE_ST_TIMESPEC = YesPlease
        HAVE_DEV_TTY = YesPlease
-       NEEDS_CLIPPED_WRITE = YesPlease
        COMPAT_OBJS += compat/precompose_utf8.o
        BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
 endif
index a0783d4867c5e6a9496e11ed043f666a402c5db9..a80ebd316c34bb0f0cf3d5a04c33123bb2847421 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "connect.h"
 #include "url.h"
 
 static char *server_capabilities;
diff --git a/connect.h b/connect.h
new file mode 100644 (file)
index 0000000..9dff25c
--- /dev/null
+++ b/connect.h
@@ -0,0 +1,13 @@
+#ifndef CONNECT_H
+#define CONNECT_H
+
+#define CONNECT_VERBOSE       (1u << 0)
+extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+extern int finish_connect(struct child_process *conn);
+extern int git_connection_is_socket(struct child_process *conn);
+extern int server_supports(const char *feature);
+extern int parse_feature_request(const char *features, const char *feature);
+extern const char *server_feature_value(const char *feature, int *len_ret);
+extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
+
+#endif
index 5da920ecd9ec1b64760db863d266fdc9c47502de..e1b73130723a57e7f641236331699e67bb0d4d82 100644 (file)
@@ -2580,7 +2580,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then
                                --*=*|*.) ;;
                                *) c="$c " ;;
                                esac
-                               array[$#array+1]="$c"
+                               array[${#array[@]}+1]="$c"
                        done
                        compset -P '*[=:]'
                        compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
index a81ef5a482d48b114a81e242109e2f0dec0839ed..d6c61b2bdecc5ca89e21ece5df7c2e4bbec9373c 100644 (file)
 # the colored output of "git status -sb" and are available only when
 # using __git_ps1 for PROMPT_COMMAND or precmd.
 
+# check whether printf supports -v
+__git_printf_supports_v=
+printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
+
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
 __git_ps1_show_upstream ()
@@ -433,7 +437,7 @@ __git_ps1 ()
        local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
 
        if [ $pcmode = yes ]; then
-               if [[ -n ${ZSH_VERSION-} ]]; then
+               if [ "${__git_printf_supports_v-}" != yes ]; then
                        gitstring=$(printf -- "$printf_format" "$gitstring")
                else
                        printf -v gitstring -- "$printf_format" "$gitstring"
index d80f7d1b6e6928dc7d24a56720caf13220f95236..fb6429b64be3cf7011ce69950987a8b328e0e758 100755 (executable)
@@ -59,11 +59,11 @@ sub import_commits {
 }
 
 sub get_blame {
-       my ($commits, $source, $start, $len, $from) = @_;
-       $len = 1 unless defined($len);
-       return if $len == 0;
+       my ($commits, $source, $from, $ranges) = @_;
+       return unless @$ranges;
        open my $f, '-|',
-               qw(git blame --porcelain -C), '-L', "$start,+$len",
+               qw(git blame --porcelain -C),
+               map({"-L$_->[0],+$_->[1]"} @$ranges),
                '--since', $since, "$from^", '--', $source or die;
        while (<$f>) {
                if (/^([0-9a-f]{40}) \d+ \d+ \d+$/) {
@@ -76,8 +76,17 @@ sub get_blame {
        close $f;
 }
 
+sub blame_sources {
+       my ($sources, $commits) = @_;
+       for my $s (keys %$sources) {
+               for my $id (keys %{$sources->{$s}}) {
+                       get_blame($commits, $s, $id, $sources->{$s}{$id});
+               }
+       }
+}
+
 sub scan_patches {
-       my ($commits, $id, $f) = @_;
+       my ($sources, $id, $f) = @_;
        my $source;
        while (<$f>) {
                if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) {
@@ -90,7 +99,8 @@ sub scan_patches {
                } elsif (/^--- /) {
                        die "Cannot parse hunk source: $_\n";
                } elsif (/^@@ -(\d+)(?:,(\d+))?/ && $source) {
-                       get_blame($commits, $source, $1, $2, $id);
+                       my $len = defined($2) ? $2 : 1;
+                       push @{$sources->{$source}{$id}}, [$1, $len] if $len;
                }
        }
 }
@@ -163,13 +173,16 @@ for (@ARGV) {
        }
 }
 
-my %commits;
+my %sources;
 for (@files) {
-       scan_patch_file(\%commits, $_);
+       scan_patch_file(\%sources, $_);
 }
 if (@rev_args) {
-       scan_rev_args(\%commits, \@rev_args)
+       scan_rev_args(\%sources, \@rev_args)
 }
+
+my %commits;
+blame_sources(\%sources, \%commits);
 import_commits(\%commits);
 
 my $contacts = {};
diff --git a/contrib/examples/git-log.sh b/contrib/examples/git-log.sh
new file mode 100755 (executable)
index 0000000..c2ea71c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+#
+
+USAGE='[--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [git-rev-list options]'
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
+
+revs=$(git-rev-parse --revs-only --no-flags --default HEAD "$@") || exit
+[ "$revs" ] || {
+       die "No HEAD ref"
+}
+git-rev-list --pretty $(git-rev-parse --default HEAD "$@") |
+LESS=-S ${PAGER:-less}
diff --git a/contrib/examples/git-whatchanged.sh b/contrib/examples/git-whatchanged.sh
new file mode 100755 (executable)
index 0000000..1fb9feb
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]'
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
+
+diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit
+case "$0" in
+*whatchanged)
+       count=
+       test -z "$diff_tree_flags" &&
+               diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
+       diff_tree_default_flags='-c -M --abbrev' ;;
+*show)
+       count=-n1
+       test -z "$diff_tree_flags" &&
+               diff_tree_flags=$(git-repo-config --get show.difftree)
+       diff_tree_default_flags='--cc --always' ;;
+esac
+test -z "$diff_tree_flags" &&
+       diff_tree_flags="$diff_tree_default_flags"
+
+rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") &&
+diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") &&
+
+eval "git-rev-list $count $rev_list_args" |
+eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" |
+LESS="$LESS -S" ${PAGER:-less}
index 153115029df0764a3f42812a9a2bd8992360c4bd..8ee410f8435c8794ca7f4fabd8ebdead439891c1 100755 (executable)
@@ -242,6 +242,9 @@ generate_email_header()
        cat <<-EOF
        To: $recipients
        Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
+       MIME-Version: 1.0
+       Content-Type: text/plain; charset=utf-8
+       Content-Transfer-Encoding: 8bit
        X-Git-Refname: $refname
        X-Git-Reftype: $refname_type
        X-Git-Oldrev: $oldrev
@@ -471,7 +474,7 @@ generate_delete_branch_email()
        echo "       was  $oldrev"
        echo ""
        echo $LOGBEGIN
-       git show -s --pretty=oneline $oldrev
+       git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
        echo $LOGEND
 }
 
@@ -547,11 +550,11 @@ generate_atag_email()
                # performed on them
                if [ -n "$prevtag" ]; then
                        # Show changes since the previous release
-                       git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
+                       git shortlog "$prevtag..$newrev"
                else
                        # No previous tag, show all the changes since time
                        # began
-                       git rev-list --pretty=short $newrev | git shortlog
+                       git shortlog $newrev
                fi
                ;;
        *)
@@ -571,7 +574,7 @@ generate_delete_atag_email()
        echo "       was  $oldrev"
        echo ""
        echo $LOGBEGIN
-       git show -s --pretty=oneline $oldrev
+       git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
        echo $LOGEND
 }
 
@@ -617,7 +620,7 @@ generate_general_email()
        echo ""
        if [ "$newrev_type" = "commit" ]; then
                echo $LOGBEGIN
-               git show --no-color --root -s --pretty=medium $newrev
+               git diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev
                echo $LOGEND
        else
                # What can we do here?  The tag marks an object that is not
@@ -636,7 +639,7 @@ generate_delete_general_email()
        echo "       was  $oldrev"
        echo ""
        echo $LOGBEGIN
-       git show -s --pretty=oneline $oldrev
+       git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
        echo $LOGEND
 }
 
index 76fcd4defcc3ac751aa0c85b6388f6d408b476ca..f206f9655b4c078c66cb885fd079c4a7b95f7949 100644 (file)
@@ -24,6 +24,11 @@ INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \
 
 all: build
 
+test: all
+       $(MAKE) -C t
+
+check: perlcritic test
+
 install_pm:
        install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
 
@@ -41,4 +46,7 @@ clean:
        rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
 
 perlcritic:
-       perlcritic -2 *.perl
+       perlcritic -5 $(SCRIPT_PERL)
+       -perlcritic -2 $(SCRIPT_PERL)
+
+.PHONY: all test check install_pm install clean perlcritic
index f8d7d2ca6c0926912200ddcadf8359c1b9bfef44..c9a4805ec1f0d5767af7a5f27428db5a02fa94a1 100755 (executable)
@@ -590,6 +590,9 @@ sub mw_capabilities {
        print {*STDOUT} "import\n";
        print {*STDOUT} "list\n";
        print {*STDOUT} "push\n";
+       if ($dumb_push) {
+               print {*STDOUT} "no-private-update\n";
+       }
        print {*STDOUT} "\n";
        return;
 }
@@ -1211,7 +1214,6 @@ sub mw_push_revision {
                }
                if (!$dumb_push) {
                        run_git(qq(notes --ref=${remotename}/mediawiki add -f -m "mediawiki_revision: ${mw_revision}" ${sha1_commit}));
-                       run_git(qq(update-ref -m "Git-MediaWiki push" refs/mediawiki/${remotename}/master ${sha1_commit} ${sha1_child}));
                }
        }
 
index c3a3cac77b875b2cb925971e2baca141f9c4f163..1e0044b69fc5c87ec03833b6fb1bf434a9e0acc0 100755 (executable)
@@ -674,7 +674,7 @@ def parse_reset(parser):
     parsed_refs[ref] = mark_to_rev(from_mark)
 
 def do_export(parser):
-    global parsed_refs, dirname
+    global parsed_refs, dirname, transports
 
     parser.next()
 
@@ -699,7 +699,8 @@ def do_export(parser):
             branch.generate_revision_history(revid, marks.get_tip(name))
 
             if name in peers:
-                peer = bzrlib.branch.Branch.open(peers[name])
+                peer = bzrlib.branch.Branch.open(peers[name],
+                                                 possible_transports=transports)
                 try:
                     peer.bzrdir.push_branch(branch, revision_id=revid)
                 except bzrlib.errors.DivergedBranches:
@@ -769,25 +770,28 @@ def do_list(parser):
     print
 
 def clone(path, remote_branch):
+    global transports
     try:
-        bdir = bzrlib.bzrdir.BzrDir.create(path)
+        bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
     except bzrlib.errors.AlreadyControlDirError:
-        bdir = bzrlib.bzrdir.BzrDir.open(path)
+        bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports)
     repo = bdir.find_repository()
     repo.fetch(remote_branch.repository)
     return remote_branch.sprout(bdir, repository=repo)
 
 def get_remote_branch(name):
-    global dirname, branches
+    global dirname, branches, transports
 
-    remote_branch = bzrlib.branch.Branch.open(branches[name])
+    remote_branch = bzrlib.branch.Branch.open(branches[name],
+                                              possible_transports=transports)
     if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
         return remote_branch
 
     branch_path = os.path.join(dirname, 'clone', name)
 
     try:
-        branch = bzrlib.branch.Branch.open(branch_path)
+        branch = bzrlib.branch.Branch.open(branch_path,
+                                           possible_transports=transports)
     except bzrlib.errors.NotBranchError:
         # clone
         branch = clone(branch_path, remote_branch)
@@ -821,17 +825,19 @@ def find_branches(repo):
             yield name, branch.base
 
 def get_repo(url, alias):
-    global dirname, peer, branches
+    global dirname, peer, branches, transports
 
     normal_url = bzrlib.urlutils.normalize_url(url)
-    origin = bzrlib.bzrdir.BzrDir.open(url)
+    origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
     is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
 
     shared_path = os.path.join(gitdir, 'bzr')
     try:
-        shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
+        shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path,
+                                               possible_transports=transports)
     except bzrlib.errors.NotBranchError:
-        shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
+        shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path,
+                                                 possible_transports=transports)
     try:
         shared_repo = shared_dir.open_repository()
     except bzrlib.errors.NoRepositoryPresent:
@@ -844,7 +850,8 @@ def get_repo(url, alias):
         else:
             # check and remove old organization
             try:
-                bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
+                bdir = bzrlib.bzrdir.BzrDir.open(clone_path,
+                                                 possible_transports=transports)
                 bdir.destroy_repository()
             except bzrlib.errors.NotBranchError:
                 pass
@@ -897,6 +904,7 @@ def main(args):
     global files_cache
     global is_tmp
     global branches, peers
+    global transports
 
     alias = args[1]
     url = args[2]
@@ -909,6 +917,7 @@ def main(args):
     marks = None
     branches = {}
     peers = {}
+    transports = []
 
     if alias[5:] == url:
         is_tmp = True
index 0194c67fb1db1dc6fdd4bd90b67d04cfb7a34e1d..c27603965ab6970dd71003603d6a77e637a22ab5 100755 (executable)
@@ -391,11 +391,24 @@ def get_repo(url, alias):
             os.makedirs(dirname)
     else:
         shared_path = os.path.join(gitdir, 'hg')
-        if not os.path.exists(shared_path):
-            try:
-                hg.clone(myui, {}, url, shared_path, update=False, pull=True)
-            except:
-                die('Repository error')
+
+        # check and upgrade old organization
+        hg_path = os.path.join(shared_path, '.hg')
+        if os.path.exists(shared_path) and not os.path.exists(hg_path):
+            repos = os.listdir(shared_path)
+            for x in repos:
+                local_hg = os.path.join(shared_path, x, 'clone', '.hg')
+                if not os.path.exists(local_hg):
+                    continue
+                if not os.path.exists(hg_path):
+                    shutil.move(local_hg, hg_path)
+                shutil.rmtree(os.path.join(shared_path, x, 'clone'))
+
+        # setup shared repo (if not there)
+        try:
+            hg.peer(myui, {}, shared_path, create=True)
+        except error.RepoError:
+            pass
 
         if not os.path.exists(dirname):
             os.makedirs(dirname)
@@ -1124,7 +1137,7 @@ def do_option(parser):
 
 def fix_path(alias, repo, orig_url):
     url = urlparse.urlparse(orig_url, 'file')
-    if url.scheme != 'file' or os.path.isabs(url.path):
+    if url.scheme != 'file' or os.path.isabs(os.path.expanduser(url.path)):
         return
     abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
     cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
index 93385e12baa0d90ae475bd02500edf5ddcce320c..3797ce6041981d1facda44d3a95d4448dbb8a091 100644 (file)
@@ -155,7 +155,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
                entries = 0xfffffffeU / RABIN_WINDOW;
        }
        hsize = entries / 4;
-       for (i = 4; (1u << i) < hsize && i < 31; i++);
+       for (i = 4; (1u << i) < hsize; i++);
        hsize = 1 << i;
        hmask = hsize - 1;
 
index b6f4b21637fe66a614a0dd6581f90fc12e80a6f8..346cac651da725af07a304194d8d8e04b2f080b7 100644 (file)
@@ -87,10 +87,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 {
        int entries, i;
        int diff_unmerged_stage = revs->max_count;
-       int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
        unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
                              ? CE_MATCH_RACY_IS_DIRTY : 0);
 
+       if (option & DIFF_SILENT_ON_REMOVED)
+               handle_deprecated_show_diff_q(&revs->diffopt);
+
        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
        if (diff_unmerged_stage < 0)
@@ -137,8 +139,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                        perror(ce->name);
                                        continue;
                                }
-                               if (silent_on_removed)
-                                       continue;
                                wt_mode = 0;
                        }
                        dpath->mode = wt_mode;
@@ -204,8 +204,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                perror(ce->name);
                                continue;
                        }
-                       if (silent_on_removed)
-                               continue;
                        diff_addremove(&revs->diffopt, '-', ce->ce_mode,
                                       ce->sha1, !is_null_sha1(ce->sha1),
                                       ce->name, 0);
@@ -476,7 +474,6 @@ static int diff_cache(struct rev_info *revs,
        opts.dst_index = NULL;
        opts.pathspec = &revs->diffopt.pathspec;
        opts.pathspec->recursive = 1;
-       opts.pathspec->max_depth = -1;
 
        init_tree_desc(&t, tree->buffer, tree->size);
        return unpack_trees(1, &t, &opts);
@@ -502,7 +499,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
        struct rev_info revs;
 
        init_revisions(&revs, NULL);
-       init_pathspec(&revs.prune_data, opt->pathspec.raw);
+       copy_pathspec(&revs.prune_data, &opt->pathspec);
        revs.diffopt = *opt;
 
        if (diff_cache(&revs, tree_sha1, NULL, 1))
index e66fdf33da9e3dd3da561aadffec938e6bc2ad49..e301aafff9ff230313425eea5987b18056b72c98 100644 (file)
@@ -187,7 +187,7 @@ void diff_no_index(struct rev_info *revs,
 {
        int i, prefixlen;
        int no_index = 0;
-       unsigned options = 0;
+       unsigned deprecated_show_diff_q_option_used = 0;
        const char *paths[2];
 
        /* Were we asked to do --no-index explicitly? */
@@ -225,7 +225,7 @@ void diff_no_index(struct rev_info *revs,
                if (!strcmp(argv[i], "--no-index"))
                        i++;
                else if (!strcmp(argv[i], "-q")) {
-                       options |= DIFF_SILENT_ON_REMOVED;
+                       deprecated_show_diff_q_option_used = 1;
                        i++;
                }
                else if (!strcmp(argv[i], "--"))
@@ -260,6 +260,9 @@ void diff_no_index(struct rev_info *revs,
        revs->max_count = -2;
        diff_setup_done(&revs->diffopt);
 
+       if (deprecated_show_diff_q_option_used)
+               handle_deprecated_show_diff_q(&revs->diffopt);
+
        setup_diff_pager(&revs->diffopt);
        DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
 
diff --git a/diff.c b/diff.c
index 266112ca6104450497fb9be504c2404cced595f0..a04a34d0487f231481a7c9c475ac4a3e92b5ed2d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -669,7 +669,7 @@ static void emit_rewrite_diff(const char *name_a,
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
        ecbdata.found_changesp = &o->found_changes;
-       ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+       ecbdata.ws_rule = whitespace_rule(name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
                mmfile_t mf1, mf2;
@@ -2252,7 +2252,7 @@ static void builtin_diff(const char *name_a,
                        (!two->mode || S_ISGITLINK(two->mode))) {
                const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
-               show_submodule_summary(o->file, one ? one->path : two->path,
+               show_submodule_summary(o->file, one->path ? one->path : two->path,
                                line_prefix,
                                one->sha1, two->sha1, two->dirty_submodule,
                                meta, del, add, reset);
@@ -2372,7 +2372,7 @@ static void builtin_diff(const char *name_a,
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
                ecbdata.found_changesp = &o->found_changes;
-               ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+               ecbdata.ws_rule = whitespace_rule(name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.opt = o;
@@ -3503,6 +3503,88 @@ static int parse_submodule_opt(struct diff_options *options, const char *value)
        return 1;
 }
 
+static const char diff_status_letters[] = {
+       DIFF_STATUS_ADDED,
+       DIFF_STATUS_COPIED,
+       DIFF_STATUS_DELETED,
+       DIFF_STATUS_MODIFIED,
+       DIFF_STATUS_RENAMED,
+       DIFF_STATUS_TYPE_CHANGED,
+       DIFF_STATUS_UNKNOWN,
+       DIFF_STATUS_UNMERGED,
+       DIFF_STATUS_FILTER_AON,
+       DIFF_STATUS_FILTER_BROKEN,
+       '\0',
+};
+
+static unsigned int filter_bit['Z' + 1];
+
+static void prepare_filter_bits(void)
+{
+       int i;
+
+       if (!filter_bit[DIFF_STATUS_ADDED]) {
+               for (i = 0; diff_status_letters[i]; i++)
+                       filter_bit[(int) diff_status_letters[i]] = (1 << i);
+       }
+}
+
+static unsigned filter_bit_tst(char status, const struct diff_options *opt)
+{
+       return opt->filter & filter_bit[(int) status];
+}
+
+static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
+{
+       int i, optch;
+
+       prepare_filter_bits();
+
+       /*
+        * If there is a negation e.g. 'd' in the input, and we haven't
+        * initialized the filter field with another --diff-filter, start
+        * from full set of bits, except for AON.
+        */
+       if (!opt->filter) {
+               for (i = 0; (optch = optarg[i]) != '\0'; i++) {
+                       if (optch < 'a' || 'z' < optch)
+                               continue;
+                       opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1;
+                       opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON];
+                       break;
+               }
+       }
+
+       for (i = 0; (optch = optarg[i]) != '\0'; i++) {
+               unsigned int bit;
+               int negate;
+
+               if ('a' <= optch && optch <= 'z') {
+                       negate = 1;
+                       optch = toupper(optch);
+               } else {
+                       negate = 0;
+               }
+
+               bit = (0 <= optch && optch <= 'Z') ? filter_bit[optch] : 0;
+               if (!bit)
+                       return optarg[i];
+               if (negate)
+                       opt->filter &= ~bit;
+               else
+                       opt->filter |= bit;
+       }
+       return 0;
+}
+
+/* Used only by "diff-files" and "diff --no-index" */
+void handle_deprecated_show_diff_q(struct diff_options *opt)
+{
+       warning("'diff -q' and 'diff-files -q' are deprecated.");
+       warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs.");
+       parse_diff_filter_opt("d", opt);
+}
+
 static void enable_patch_output(int *fmt) {
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
@@ -3732,7 +3814,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                return argcount;
        }
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
-               options->filter = optarg;
+               int offending = parse_diff_filter_opt(optarg, options);
+               if (offending)
+                       die("unknown change class '%c' in --diff-filter=%s",
+                           offending, optarg);
                return argcount;
        }
        else if (!strcmp(arg, "--abbrev"))
@@ -4524,27 +4609,32 @@ void diff_flush(struct diff_options *options)
        }
 }
 
-static void diffcore_apply_filter(const char *filter)
+static int match_filter(const struct diff_options *options, const struct diff_filepair *p)
+{
+       return (((p->status == DIFF_STATUS_MODIFIED) &&
+                ((p->score &&
+                  filter_bit_tst(DIFF_STATUS_FILTER_BROKEN, options)) ||
+                 (!p->score &&
+                  filter_bit_tst(DIFF_STATUS_MODIFIED, options)))) ||
+               ((p->status != DIFF_STATUS_MODIFIED) &&
+                filter_bit_tst(p->status, options)));
+}
+
+static void diffcore_apply_filter(struct diff_options *options)
 {
        int i;
        struct diff_queue_struct *q = &diff_queued_diff;
        struct diff_queue_struct outq;
+
        DIFF_QUEUE_CLEAR(&outq);
 
-       if (!filter)
+       if (!options->filter)
                return;
 
-       if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
+       if (filter_bit_tst(DIFF_STATUS_FILTER_AON, options)) {
                int found;
                for (i = found = 0; !found && i < q->nr; i++) {
-                       struct diff_filepair *p = q->queue[i];
-                       if (((p->status == DIFF_STATUS_MODIFIED) &&
-                            ((p->score &&
-                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
-                             (!p->score &&
-                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
-                           ((p->status != DIFF_STATUS_MODIFIED) &&
-                            strchr(filter, p->status)))
+                       if (match_filter(options, q->queue[i]))
                                found++;
                }
                if (found)
@@ -4562,14 +4652,7 @@ static void diffcore_apply_filter(const char *filter)
                /* Only the matching ones */
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
-
-                       if (((p->status == DIFF_STATUS_MODIFIED) &&
-                            ((p->score &&
-                              strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
-                             (!p->score &&
-                              strchr(filter, DIFF_STATUS_MODIFIED)))) ||
-                           ((p->status != DIFF_STATUS_MODIFIED) &&
-                            strchr(filter, p->status)))
+                       if (match_filter(options, p))
                                diff_q(&outq, p);
                        else
                                diff_free_filepair(p);
@@ -4676,7 +4759,7 @@ void diffcore_std(struct diff_options *options)
        if (!options->found_follow)
                /* See try_to_follow_renames() in tree-diff.c */
                diff_resolve_rename_copy();
-       diffcore_apply_filter(options->filter);
+       diffcore_apply_filter(options);
 
        if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
                DIFF_OPT_SET(options, HAS_CHANGES);
diff --git a/diff.h b/diff.h
index 78b4091dd5b59fe4442262cd79cf71f8a4a2b98f..44092c2176468257644a19f787038a1ff3033eaa 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -5,6 +5,7 @@
 #define DIFF_H
 
 #include "tree-walk.h"
+#include "pathspec.h"
 
 struct rev_info;
 struct diff_options;
@@ -103,12 +104,15 @@ enum diff_words_type {
 };
 
 struct diff_options {
-       const char *filter;
        const char *orderfile;
        const char *pickaxe;
        const char *single_follow;
        const char *a_prefix, *b_prefix;
        unsigned flags;
+
+       /* diff-filter bits */
+       unsigned int filter;
+
        int use_color;
        int context;
        int interhunkcontext;
@@ -179,8 +183,6 @@ const char *diff_line_prefix(struct diff_options *);
 
 extern const char mime_boundary_leader[];
 
-extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
-extern void diff_tree_release_paths(struct diff_options *);
 extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
                     const char *base, struct diff_options *opt);
 extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
@@ -338,6 +340,8 @@ extern int parse_rename_score(const char **cp_p);
 
 extern long parse_algorithm_value(const char *value);
 
+extern void handle_deprecated_show_diff_q(struct diff_options *);
+
 extern int print_stat_summary(FILE *fp, int files,
                              int insertions, int deletions);
 extern void setup_diff_pager(struct diff_options *);
diff --git a/dir.c b/dir.c
index 910bfcde4e432fae3bf57ec951258bc5a02bebb6..b439ff0e63481bebb4039901b029bad978b46929 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -11,6 +11,7 @@
 #include "dir.h"
 #include "refs.h"
 #include "wildmatch.h"
+#include "pathspec.h"
 
 struct path_simplify {
        int len;
@@ -51,26 +52,32 @@ int fnmatch_icase(const char *pattern, const char *string, int flags)
        return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
 }
 
-inline int git_fnmatch(const char *pattern, const char *string,
-                      int flags, int prefix)
+inline int git_fnmatch(const struct pathspec_item *item,
+                      const char *pattern, const char *string,
+                      int prefix)
 {
-       int fnm_flags = 0;
-       if (flags & GFNM_PATHNAME)
-               fnm_flags |= FNM_PATHNAME;
        if (prefix > 0) {
-               if (strncmp(pattern, string, prefix))
+               if (ps_strncmp(item, pattern, string, prefix))
                        return FNM_NOMATCH;
                pattern += prefix;
                string += prefix;
        }
-       if (flags & GFNM_ONESTAR) {
+       if (item->flags & PATHSPEC_ONESTAR) {
                int pattern_len = strlen(++pattern);
                int string_len = strlen(string);
                return string_len < pattern_len ||
-                      strcmp(pattern,
-                             string + string_len - pattern_len);
+                       ps_strcmp(item, pattern,
+                                 string + string_len - pattern_len);
        }
-       return fnmatch(pattern, string, fnm_flags);
+       if (item->magic & PATHSPEC_GLOB)
+               return wildmatch(pattern, string,
+                                WM_PATHNAME |
+                                (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0),
+                                NULL);
+       else
+               /* wildmatch has not learned no FNM_PATHNAME mode yet */
+               return fnmatch(pattern, string,
+                              item->magic & PATHSPEC_ICASE ? FNM_CASEFOLD : 0);
 }
 
 static int fnmatch_icase_mem(const char *pattern, int patternlen,
@@ -102,26 +109,40 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen,
        return match_status;
 }
 
-static size_t common_prefix_len(const char **pathspec)
+static size_t common_prefix_len(const struct pathspec *pathspec)
 {
-       const char *n, *first;
+       int n;
        size_t max = 0;
-       int literal = limit_pathspec_to_literal();
 
-       if (!pathspec)
-               return max;
-
-       first = *pathspec;
-       while ((n = *pathspec++)) {
-               size_t i, len = 0;
-               for (i = 0; first == n || i < max; i++) {
-                       char c = n[i];
-                       if (!c || c != first[i] || (!literal && is_glob_special(c)))
+       /*
+        * ":(icase)path" is treated as a pathspec full of
+        * wildcard. In other words, only prefix is considered common
+        * prefix. If the pathspec is abc/foo abc/bar, running in
+        * subdir xyz, the common prefix is still xyz, not xuz/abc as
+        * in non-:(icase).
+        */
+       GUARD_PATHSPEC(pathspec,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE);
+
+       for (n = 0; n < pathspec->nr; n++) {
+               size_t i = 0, len = 0, item_len;
+               if (pathspec->items[n].magic & PATHSPEC_ICASE)
+                       item_len = pathspec->items[n].prefix;
+               else
+                       item_len = pathspec->items[n].nowildcard_len;
+               while (i < item_len && (n == 0 || i < max)) {
+                       char c = pathspec->items[n].match[i];
+                       if (c != pathspec->items[0].match[i])
                                break;
                        if (c == '/')
                                len = i + 1;
+                       i++;
                }
-               if (first == n || len < max) {
+               if (n == 0 || len < max) {
                        max = len;
                        if (!max)
                                break;
@@ -134,14 +155,14 @@ static size_t common_prefix_len(const char **pathspec)
  * Returns a copy of the longest leading path common among all
  * pathspecs.
  */
-char *common_prefix(const char **pathspec)
+char *common_prefix(const struct pathspec *pathspec)
 {
        unsigned long len = common_prefix_len(pathspec);
 
-       return len ? xmemdupz(*pathspec, len) : NULL;
+       return len ? xmemdupz(pathspec->items[0].match, len) : NULL;
 }
 
-int fill_directory(struct dir_struct *dir, const char **pathspec)
+int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
 {
        size_t len;
 
@@ -152,7 +173,7 @@ int fill_directory(struct dir_struct *dir, const char **pathspec)
        len = common_prefix_len(pathspec);
 
        /* Read the directory and prune it */
-       read_directory(dir, pathspec ? *pathspec : "", len, pathspec);
+       read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec);
        return len;
 }
 
@@ -171,113 +192,6 @@ int within_depth(const char *name, int namelen,
        return 1;
 }
 
-/*
- * Does 'match' match the given name?
- * A match is found if
- *
- * (1) the 'match' string is leading directory of 'name', or
- * (2) the 'match' string is a wildcard and matches 'name', or
- * (3) the 'match' string is exactly the same as 'name'.
- *
- * and the return value tells which case it was.
- *
- * It returns 0 when there is no match.
- */
-static int match_one(const char *match, const char *name, int namelen)
-{
-       int matchlen;
-       int literal = limit_pathspec_to_literal();
-
-       /* If the match was just the prefix, we matched */
-       if (!*match)
-               return MATCHED_RECURSIVELY;
-
-       if (ignore_case) {
-               for (;;) {
-                       unsigned char c1 = tolower(*match);
-                       unsigned char c2 = tolower(*name);
-                       if (c1 == '\0' || (!literal && is_glob_special(c1)))
-                               break;
-                       if (c1 != c2)
-                               return 0;
-                       match++;
-                       name++;
-                       namelen--;
-               }
-       } else {
-               for (;;) {
-                       unsigned char c1 = *match;
-                       unsigned char c2 = *name;
-                       if (c1 == '\0' || (!literal && is_glob_special(c1)))
-                               break;
-                       if (c1 != c2)
-                               return 0;
-                       match++;
-                       name++;
-                       namelen--;
-               }
-       }
-
-       /*
-        * If we don't match the matchstring exactly,
-        * we need to match by fnmatch
-        */
-       matchlen = strlen(match);
-       if (strncmp_icase(match, name, matchlen)) {
-               if (literal)
-                       return 0;
-               return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
-       }
-
-       if (namelen == matchlen)
-               return MATCHED_EXACTLY;
-       if (match[matchlen-1] == '/' || name[matchlen] == '/')
-               return MATCHED_RECURSIVELY;
-       return 0;
-}
-
-/*
- * Given a name and a list of pathspecs, returns the nature of the
- * closest (i.e. most specific) match of the name to any of the
- * pathspecs.
- *
- * The caller typically calls this multiple times with the same
- * pathspec and seen[] array but with different name/namelen
- * (e.g. entries from the index) and is interested in seeing if and
- * how each pathspec matches all the names it calls this function
- * with.  A mark is left in the seen[] array for each pathspec element
- * indicating the closest type of match that element achieved, so if
- * seen[n] remains zero after multiple invocations, that means the nth
- * pathspec did not match any names, which could indicate that the
- * user mistyped the nth pathspec.
- */
-int match_pathspec(const char **pathspec, const char *name, int namelen,
-               int prefix, char *seen)
-{
-       int i, retval = 0;
-
-       if (!pathspec)
-               return 1;
-
-       name += prefix;
-       namelen -= prefix;
-
-       for (i = 0; pathspec[i] != NULL; i++) {
-               int how;
-               const char *match = pathspec[i] + prefix;
-               if (seen && seen[i] == MATCHED_EXACTLY)
-                       continue;
-               how = match_one(match, name, namelen);
-               if (how) {
-                       if (retval < how)
-                               retval = how;
-                       if (seen && seen[i] < how)
-                               seen[i] = how;
-               }
-       }
-       return retval;
-}
-
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -297,11 +211,44 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
        const char *match = item->match + prefix;
        int matchlen = item->len - prefix;
 
+       /*
+        * The normal call pattern is:
+        * 1. prefix = common_prefix_len(ps);
+        * 2. prune something, or fill_directory
+        * 3. match_pathspec_depth()
+        *
+        * 'prefix' at #1 may be shorter than the command's prefix and
+        * it's ok for #2 to match extra files. Those extras will be
+        * trimmed at #3.
+        *
+        * Suppose the pathspec is 'foo' and '../bar' running from
+        * subdir 'xyz'. The common prefix at #1 will be empty, thanks
+        * to "../". We may have xyz/foo _and_ XYZ/foo after #2. The
+        * user does not want XYZ/foo, only the "foo" part should be
+        * case-insensitive. We need to filter out XYZ/foo here. In
+        * other words, we do not trust the caller on comparing the
+        * prefix part when :(icase) is involved. We do exact
+        * comparison ourselves.
+        *
+        * Normally the caller (common_prefix_len() in fact) does
+        * _exact_ matching on name[-prefix+1..-1] and we do not need
+        * to check that part. Be defensive and check it anyway, in
+        * case common_prefix_len is changed, or a new caller is
+        * introduced that does not use common_prefix_len.
+        *
+        * If the penalty turns out too high when prefix is really
+        * long, maybe change it to
+        * strncmp(match, name, item->prefix - prefix)
+        */
+       if (item->prefix && (item->magic & PATHSPEC_ICASE) &&
+           strncmp(item->match, name - prefix, item->prefix))
+               return 0;
+
        /* If the match was just the prefix, we matched */
        if (!*match)
                return MATCHED_RECURSIVELY;
 
-       if (matchlen <= namelen && !strncmp(match, name, matchlen)) {
+       if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) {
                if (matchlen == namelen)
                        return MATCHED_EXACTLY;
 
@@ -310,8 +257,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
        }
 
        if (item->nowildcard_len < item->len &&
-           !git_fnmatch(match, name,
-                        item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+           !git_fnmatch(item, match, name,
                         item->nowildcard_len - prefix))
                return MATCHED_FNMATCH;
 
@@ -339,8 +285,17 @@ int match_pathspec_depth(const struct pathspec *ps,
 {
        int i, retval = 0;
 
+       GUARD_PATHSPEC(ps,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE);
+
        if (!ps->nr) {
-               if (!ps->recursive || ps->max_depth == -1)
+               if (!ps->recursive ||
+                   !(ps->magic & PATHSPEC_MAXDEPTH) ||
+                   ps->max_depth == -1)
                        return MATCHED_RECURSIVELY;
 
                if (within_depth(name, namelen, 0, ps->max_depth))
@@ -357,7 +312,9 @@ int match_pathspec_depth(const struct pathspec *ps,
                if (seen && seen[i] == MATCHED_EXACTLY)
                        continue;
                how = match_pathspec_item(ps->items+i, prefix, name, namelen);
-               if (ps->recursive && ps->max_depth != -1 &&
+               if (ps->recursive &&
+                   (ps->magic & PATHSPEC_MAXDEPTH) &&
+                   ps->max_depth != -1 &&
                    how && how != MATCHED_FNMATCH) {
                        int len = ps->items[i].len;
                        if (name[len] == '/')
@@ -380,7 +337,7 @@ int match_pathspec_depth(const struct pathspec *ps,
 /*
  * Return the length of the "simple" part of a path match limiter.
  */
-static int simple_length(const char *match)
+int simple_length(const char *match)
 {
        int len = -1;
 
@@ -392,7 +349,7 @@ static int simple_length(const char *match)
        }
 }
 
-static int no_wildcard(const char *string)
+int no_wildcard(const char *string)
 {
        return string[simple_length(string)] == '\0';
 }
@@ -472,15 +429,14 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
        unsigned long sz;
        enum object_type type;
        void *data;
-       struct index_state *istate = &the_index;
 
        len = strlen(path);
-       pos = index_name_pos(istate, path, len);
+       pos = cache_name_pos(path, len);
        if (pos < 0)
                return NULL;
-       if (!ce_skip_worktree(istate->cache[pos]))
+       if (!ce_skip_worktree(active_cache[pos]))
                return NULL;
-       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;
@@ -927,13 +883,13 @@ enum exist_status {
 };
 
 /*
- * Do not use the alphabetically stored index to look up
+ * Do not use the alphabetically sorted index to look up
  * the directory name; instead, use the case insensitive
  * name hash.
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
 {
-       const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+       const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case);
        unsigned char endchar;
 
        if (!ce)
@@ -1175,14 +1131,51 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          int dtype, struct dirent *de)
 {
        int exclude;
+       int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case);
+
        if (dtype == DT_UNKNOWN)
                dtype = get_dtype(de, path->buf, path->len);
 
        /* Always exclude indexed files */
-       if (dtype != DT_DIR &&
-           cache_name_exists(path->buf, path->len, ignore_case))
+       if (dtype != DT_DIR && has_path_in_index)
                return path_none;
 
+       /*
+        * When we are looking at a directory P in the working tree,
+        * there are three cases:
+        *
+        * (1) P exists in the index.  Everything inside the directory P in
+        * the working tree needs to go when P is checked out from the
+        * index.
+        *
+        * (2) P does not exist in the index, but there is P/Q in the index.
+        * We know P will stay a directory when we check out the contents
+        * of the index, but we do not know yet if there is a directory
+        * P/Q in the working tree to be killed, so we need to recurse.
+        *
+        * (3) P does not exist in the index, and there is no P/Q in the index
+        * to require P to be a directory, either.  Only in this case, we
+        * know that everything inside P will not be killed without
+        * recursing.
+        */
+       if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
+           (dtype == DT_DIR) &&
+           !has_path_in_index) {
+               /*
+                * NEEDSWORK: directory_exists_in_index_icase()
+                * assumes that one byte past the given path is
+                * readable and has '/', which needs to be fixed, but
+                * until then, work it around in the caller.
+                */
+               strbuf_addch(path, '/');
+               if (directory_exists_in_index(path->buf, path->len - 1) ==
+                   index_nonexistent) {
+                       strbuf_setlen(path, path->len - 1);
+                       return path_none;
+               }
+               strbuf_setlen(path, path->len - 1);
+       }
+
        exclude = is_excluded(dir, path->buf, &dtype);
 
        /*
@@ -1381,14 +1374,25 @@ static int treat_leading_path(struct dir_struct *dir,
        return rc;
 }
 
-int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
+int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
 {
        struct path_simplify *simplify;
 
+       /*
+        * Check out create_simplify()
+        */
+       if (pathspec)
+               GUARD_PATHSPEC(pathspec,
+                              PATHSPEC_FROMTOP |
+                              PATHSPEC_MAXDEPTH |
+                              PATHSPEC_LITERAL |
+                              PATHSPEC_GLOB |
+                              PATHSPEC_ICASE);
+
        if (has_symlink_leading_path(path, len))
                return dir->nr;
 
-       simplify = create_simplify(pathspec);
+       simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        if (!len || treat_leading_path(dir, path, len, simplify))
                read_directory_recursive(dir, path, len, 0, simplify);
        free_simplify(simplify);
@@ -1568,71 +1572,6 @@ int remove_path(const char *name)
        return 0;
 }
 
-static int pathspec_item_cmp(const void *a_, const void *b_)
-{
-       struct pathspec_item *a, *b;
-
-       a = (struct pathspec_item *)a_;
-       b = (struct pathspec_item *)b_;
-       return strcmp(a->match, b->match);
-}
-
-int init_pathspec(struct pathspec *pathspec, const char **paths)
-{
-       const char **p = paths;
-       int i;
-
-       memset(pathspec, 0, sizeof(*pathspec));
-       if (!p)
-               return 0;
-       while (*p)
-               p++;
-       pathspec->raw = paths;
-       pathspec->nr = p - paths;
-       if (!pathspec->nr)
-               return 0;
-
-       pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr);
-       for (i = 0; i < pathspec->nr; i++) {
-               struct pathspec_item *item = pathspec->items+i;
-               const char *path = paths[i];
-
-               item->match = path;
-               item->len = strlen(path);
-               item->flags = 0;
-               if (limit_pathspec_to_literal()) {
-                       item->nowildcard_len = item->len;
-               } else {
-                       item->nowildcard_len = simple_length(path);
-                       if (item->nowildcard_len < item->len) {
-                               pathspec->has_wildcard = 1;
-                               if (path[item->nowildcard_len] == '*' &&
-                                   no_wildcard(path + item->nowildcard_len + 1))
-                                       item->flags |= PATHSPEC_ONESTAR;
-                       }
-               }
-       }
-
-       qsort(pathspec->items, pathspec->nr,
-             sizeof(struct pathspec_item), pathspec_item_cmp);
-
-       return 0;
-}
-
-void free_pathspec(struct pathspec *pathspec)
-{
-       free(pathspec->items);
-       pathspec->items = NULL;
-}
-
-int limit_pathspec_to_literal(void)
-{
-       static int flag = -1;
-       if (flag < 0)
-               flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
-       return flag;
-}
-
 /*
  * Frees memory within dir which was allocated for exclude lists and
  * the exclude_stack.  Does not free dir itself.
diff --git a/dir.h b/dir.h
index 3d6b80c933b395644c005f6d8df4697215ff000a..9b7e4e77d8b11bab92a91a6ce3e8920e50d23f9b 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -80,7 +80,8 @@ struct dir_struct {
                DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
                DIR_NO_GITLINKS = 1<<3,
                DIR_COLLECT_IGNORED = 1<<4,
-               DIR_SHOW_IGNORED_TOO = 1<<5
+               DIR_SHOW_IGNORED_TOO = 1<<5,
+               DIR_COLLECT_KILLED_ONLY = 1<<6
        } flags;
        struct dir_entry **entries;
        struct dir_entry **ignored;
@@ -128,15 +129,16 @@ struct dir_struct {
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
-extern char *common_prefix(const char **pathspec);
-extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
+extern int simple_length(const char *match);
+extern int no_wildcard(const char *string);
+extern char *common_prefix(const struct pathspec *pathspec);
 extern int match_pathspec_depth(const struct pathspec *pathspec,
                                const char *name, int namelen,
                                int prefix, char *seen);
 extern int within_depth(const char *name, int namelen, int depth, int max_depth);
 
-extern int fill_directory(struct dir_struct *dir, const char **pathspec);
-extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
+extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec);
+extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec);
 
 extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename,
                                 int *dtype, struct exclude_list *el);
@@ -198,10 +200,9 @@ extern int fnmatch_icase(const char *pattern, const char *string, int flags);
 /*
  * The prefix part of pattern must not contains wildcards.
  */
-#define GFNM_PATHNAME 1                /* similar to FNM_PATHNAME */
-#define GFNM_ONESTAR  2                /* there is only _one_ wildcard, a star */
-
-extern int git_fnmatch(const char *pattern, const char *string,
-                      int flags, int prefix);
+struct pathspec_item;
+extern int git_fnmatch(const struct pathspec_item *item,
+                      const char *pattern, const char *string,
+                      int prefix);
 
 #endif
index 27bdecdaf3d99d3179737e07be971d2458408660..0abbd8dc3a0ec91acd0c143b6d9b5ad41ac90417 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -37,7 +37,7 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
                return error("Terminal is dumb, but EDITOR unset");
 
        if (strcmp(editor, ":")) {
-               const char *args[] = { editor, path, NULL };
+               const char *args[] = { editor, real_path(path), NULL };
                struct child_process p;
                int ret, sig;
 
index 23f625f561dfd664a460827d507e7c0b720e9849..21db3fc46d58231fd1345d65ae6cf1104901e55e 100644 (file)
@@ -1568,7 +1568,8 @@ static int tree_content_set(
 static int tree_content_remove(
        struct tree_entry *root,
        const char *p,
-       struct tree_entry *backup_leaf)
+       struct tree_entry *backup_leaf,
+       int allow_root)
 {
        struct tree_content *t;
        const char *slash1;
@@ -1583,6 +1584,12 @@ static int tree_content_remove(
 
        if (!root->tree)
                load_tree(root);
+
+       if (!*p && allow_root) {
+               e = root;
+               goto del_entry;
+       }
+
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
@@ -1599,7 +1606,7 @@ static int tree_content_remove(
                                goto del_entry;
                        if (!e->tree)
                                load_tree(e);
-                       if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
+                       if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) {
                                for (n = 0; n < e->tree->entry_count; n++) {
                                        if (e->tree->entries[n]->versions[1].mode) {
                                                hashclr(root->versions[1].sha1);
@@ -1629,7 +1636,8 @@ static int tree_content_remove(
 static int tree_content_get(
        struct tree_entry *root,
        const char *p,
-       struct tree_entry *leaf)
+       struct tree_entry *leaf,
+       int allow_root)
 {
        struct tree_content *t;
        const char *slash1;
@@ -1641,31 +1649,39 @@ static int tree_content_get(
                n = slash1 - p;
        else
                n = strlen(p);
-       if (!n)
+       if (!n && !allow_root)
                die("Empty path component found in input");
 
        if (!root->tree)
                load_tree(root);
+
+       if (!n) {
+               e = root;
+               goto found_entry;
+       }
+
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
                if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
-                       if (!slash1) {
-                               memcpy(leaf, e, sizeof(*leaf));
-                               if (e->tree && is_null_sha1(e->versions[1].sha1))
-                                       leaf->tree = dup_tree_content(e->tree);
-                               else
-                                       leaf->tree = NULL;
-                               return 1;
-                       }
+                       if (!slash1)
+                               goto found_entry;
                        if (!S_ISDIR(e->versions[1].mode))
                                return 0;
                        if (!e->tree)
                                load_tree(e);
-                       return tree_content_get(e, slash1 + 1, leaf);
+                       return tree_content_get(e, slash1 + 1, leaf, 0);
                }
        }
        return 0;
+
+found_entry:
+       memcpy(leaf, e, sizeof(*leaf));
+       if (e->tree && is_null_sha1(e->versions[1].sha1))
+               leaf->tree = dup_tree_content(e->tree);
+       else
+               leaf->tree = NULL;
+       return 1;
 }
 
 static int update_branch(struct branch *b)
@@ -2179,7 +2195,7 @@ static uintmax_t do_change_note_fanout(
                        }
 
                        /* Rename fullpath to realpath */
-                       if (!tree_content_remove(orig_root, fullpath, &leaf))
+                       if (!tree_content_remove(orig_root, fullpath, &leaf, 0))
                                die("Failed to remove path %s", fullpath);
                        tree_content_set(orig_root, realpath,
                                leaf.versions[1].sha1,
@@ -2314,7 +2330,7 @@ static void file_change_m(struct branch *b)
 
        /* Git does not track empty, non-toplevel directories. */
        if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
-               tree_content_remove(&b->branch_tree, p, NULL);
+               tree_content_remove(&b->branch_tree, p, NULL, 0);
                return;
        }
 
@@ -2375,7 +2391,7 @@ static void file_change_d(struct branch *b)
                        die("Garbage after path in: %s", command_buf.buf);
                p = uq.buf;
        }
-       tree_content_remove(&b->branch_tree, p, NULL);
+       tree_content_remove(&b->branch_tree, p, NULL, 1);
 }
 
 static void file_change_cr(struct branch *b, int rename)
@@ -2413,9 +2429,9 @@ static void file_change_cr(struct branch *b, int rename)
 
        memset(&leaf, 0, sizeof(leaf));
        if (rename)
-               tree_content_remove(&b->branch_tree, s, &leaf);
+               tree_content_remove(&b->branch_tree, s, &leaf, 1);
        else
-               tree_content_get(&b->branch_tree, s, &leaf);
+               tree_content_get(&b->branch_tree, s, &leaf, 1);
        if (!leaf.versions[1].mode)
                die("Path %s not in branch", s);
        if (!*d) {      /* C "path/to/subdir" "" */
@@ -2521,7 +2537,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        }
 
        construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
-       if (tree_content_remove(&b->branch_tree, path, NULL))
+       if (tree_content_remove(&b->branch_tree, path, NULL, 0))
                b->num_notes--;
 
        if (is_null_sha1(sha1))
@@ -3051,6 +3067,8 @@ static void parse_ls(struct branch *b)
                struct object_entry *e = parse_treeish_dataref(&p);
                root = new_tree_entry();
                hashcpy(root->versions[1].sha1, e->idx.sha1);
+               if (!is_null_sha1(root->versions[1].sha1))
+                       root->versions[1].mode = S_IFDIR;
                load_tree(root);
                if (*p++ != ' ')
                        die("Missing space after tree-ish: %s", command_buf.buf);
@@ -3065,7 +3083,7 @@ static void parse_ls(struct branch *b)
                        die("Garbage after path in: %s", command_buf.buf);
                p = uq.buf;
        }
-       tree_content_get(root, p, &leaf);
+       tree_content_get(root, p, &leaf, 1);
        /*
         * A directory in preparation would have a sha1 of zero
         * until it is saved.  Save, for simplicity.
index 6684348c0ec9ea742eb98666b56a0bfcef316560..094267fd80cdb09feec75c89aa2f4931f11e583d 100644 (file)
@@ -9,6 +9,7 @@
 #include "fetch-pack.h"
 #include "remote.h"
 #include "run-command.h"
+#include "connect.h"
 #include "transport.h"
 #include "version.h"
 #include "prio-queue.h"
@@ -897,6 +898,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                packet_flush(fd[1]);
        if (args->depth > 0)
                setup_alternate_shallow();
+       else
+               alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
                die("git fetch-pack: fetch failed.");
 
@@ -987,7 +990,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        }
        ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
 
-       if (alternate_shallow_file) {
+       if (args->depth > 0 && alternate_shallow_file) {
                if (*alternate_shallow_file == '\0') { /* --unshallow */
                        unlink_or_warn(git_path("shallow"));
                        rollback_lock_file(&shallow_lock);
index 40f08bab24df917986de73c8d7f4d9d36fe658da..461cbf39b2affa71e27dd1a95c24d6c65a938d62 100644 (file)
@@ -2,6 +2,7 @@
 #define FETCH_PACK_H
 
 #include "string-list.h"
+#include "run-command.h"
 
 struct fetch_pack_args {
        const char *uploadpack;
index 115cb1da42cc314f75a5b032d90798e5a2ce68c5..9549de6318147839c73b60b1fc72ceb44d9adb47 100644 (file)
@@ -185,11 +185,6 @@ typedef unsigned long uintptr_t;
 #define probe_utf8_pathname_composition(a,b)
 #endif
 
-#ifdef NEEDS_CLIPPED_WRITE
-ssize_t clipped_write(int fildes, const void *buf, size_t nbyte);
-#define write(x,y,z) clipped_write((x),(y),(z))
-#endif
-
 #ifdef MKDIR_WO_TRAILING_SLASH
 #define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
 extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
@@ -330,6 +325,16 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
+#ifndef NO_OPENSSL
+#ifdef APPLE_COMMON_CRYPTO
+#include "compat/apple-common-crypto.h"
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif /* APPLE_COMMON_CRYPTO */
+#include <openssl/x509v3.h>
+#endif /* NO_OPENSSL */
+
 /*
  * Let callers be aware of the constant return value; this can help
  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,
@@ -514,7 +519,7 @@ int inet_pton(int af, const char *src, void *dst);
 const char *inet_ntop(int af, const void *src, char *dst, size_t size);
 #endif
 
-extern void release_pack_memory(size_t, int);
+extern void release_pack_memory(size_t);
 
 typedef void (*try_to_free_t)(size_t);
 extern try_to_free_t set_try_to_free_routine(try_to_free_t);
index ac2a005fdb23c48d8451188ffd7b1c8194b0295f..98e8fe43d2fdde1b76e8f10f4c9473e23c8f347c 100755 (executable)
@@ -283,11 +283,12 @@ while read commit parents; do
 
        case "$filter_subdir" in
        "")
-               git read-tree -i -m $commit
+               GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
                ;;
        *)
                # The commit may not have the subdirectory at all
-               err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+               err=$(GIT_ALLOW_NULL_SHA1=1 \
+                     git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
                        if ! git rev-parse -q --verify $commit:"$filter_subdir"
                        then
                                rm -f "$GIT_INDEX_FILE"
index 31e71ff8b248339749bbcd045acdf602a1a21463..a53a6dc406c26daaf263944d38b53b27db271072 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -2180,9 +2180,13 @@ def streamOneP4File(self, file, contents):
             git_mode = "100755"
         if type_base == "symlink":
             git_mode = "120000"
-            # p4 print on a symlink contains "target\n"; remove the newline
+            # p4 print on a symlink sometimes contains "target\n";
+            # if it does, remove the newline
             data = ''.join(contents)
-            contents = [data[:-1]]
+            if data[-1] == '\n':
+                contents = [data[:-1]]
+            else:
+                contents = [data]
 
         if type_base == "utf16":
             # p4 delivers different text in the python output to -G
index f0df41c841644208c89230516677db162498be26..e11d9a05802432c623b9c2419461558f29f2d233 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -38,15 +38,19 @@ Please, commit your changes before you can merge.")"
 test -z "$(git ls-files -u)" || die_conflict
 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
 
+bool_or_string_config () {
+       git config --bool "$1" 2>/dev/null || git config "$1"
+}
+
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
 log_arg= verbosity= progress= recurse_submodules= verify_signatures=
-merge_args= edit=
+merge_args= edit= rebase_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
-rebase=$(git config --bool branch.$curr_branch_short.rebase)
+rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
 if test -z "$rebase"
 then
-       rebase=$(git config --bool pull.rebase)
+       rebase=$(bool_or_string_config pull.rebase)
 fi
 dry_run=
 while :
@@ -110,6 +114,9 @@ do
                esac
                merge_args="$merge_args$xx "
                ;;
+       -r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)
+               rebase="${1#*=}"
+               ;;
        -r|--r|--re|--reb|--reba|--rebas|--rebase)
                rebase=true
                ;;
@@ -145,6 +152,20 @@ do
        shift
 done
 
+case "$rebase" in
+preserve)
+       rebase=true
+       rebase_args=--preserve-merges
+       ;;
+true|false|'')
+       ;;
+*)
+       echo "Invalid value for --rebase, should be true, false, or preserve"
+       usage
+       exit 1
+       ;;
+esac
+
 error_on_no_merge_candidates () {
        exec >&2
        for opt
@@ -292,7 +313,7 @@ fi
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
-       eval="git-rebase $diffstat $strategy_args $merge_args $verbosity"
+       eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
        eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
        ;;
 *)
index 83d6d4676bc1cadcec5ea5889fcf4a0769ee8d31..10bf318d0d4ecc03dcf52d3f665410c48d26bdfd 100644 (file)
@@ -352,8 +352,9 @@ pick_one_preserving_merges () {
                        msg_content="$(commit_message $sha1)"
                        # No point in merging the first parent, that's HEAD
                        new_parents=${new_parents# $first_parent}
+                       merge_args="--no-log --no-ff"
                        if ! do_with_author output eval \
-                       'git merge --no-ff $strategy_args -m "$msg_content" $new_parents'
+                       'git merge $merge_args $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
@@ -671,7 +672,7 @@ skip_unnecessary_picks () {
                                ;;
                        esac
                        ;;
-               3,#*|3,)
+               3,"$comment_char"*|3,)
                        # copy comments
                        ;;
                *)
@@ -689,6 +690,32 @@ skip_unnecessary_picks () {
        die "Could not skip unnecessary pick commands"
 }
 
+transform_todo_ids () {
+       while read -r command rest
+       do
+               case "$command" in
+               "$comment_char"* | exec)
+                       # Be careful for oddball commands like 'exec'
+                       # that do not have a SHA-1 at the beginning of $rest.
+                       ;;
+               *)
+                       sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) &&
+                       rest="$sha1 ${rest#* }"
+                       ;;
+               esac
+               printf '%s\n' "$command${rest:+ }$rest"
+       done <"$todo" >"$todo.new" &&
+       mv -f "$todo.new" "$todo"
+}
+
+expand_todo_ids() {
+       transform_todo_ids
+}
+
+collapse_todo_ids() {
+       transform_todo_ids --short=7
+}
+
 # Rearrange the todo list that has both "pick sha1 msg" and
 # "pick sha1 fixup!/squash! msg" appears in it so that the latter
 # comes immediately after the former, and change "pick" to
@@ -841,6 +868,7 @@ skip)
 edit-todo)
        git stripspace --strip-comments <"$todo" >"$todo".new
        mv -f "$todo".new "$todo"
+       collapse_todo_ids
        append_todo_help
        git stripspace --comment-lines >>"$todo" <<\EOF
 
@@ -852,6 +880,7 @@ EOF
 
        git_sequence_editor "$todo" ||
                die "Could not execute editor"
+       expand_todo_ids
 
        exit
        ;;
@@ -1008,6 +1037,8 @@ git_sequence_editor "$todo" ||
 has_action "$todo" ||
        die_abort "Nothing to do"
 
+expand_todo_ids
+
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
index 2109070d00411dbd9f1ba70e55587091d50a8a08..6d2f282d32212b605a1237bae224ecbba05cacd1 100755 (executable)
@@ -38,6 +38,7 @@ do
                        echo "*export-marks $gitmarks"
                fi
                test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
+               test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
                echo
                ;;
        list)
index 7a964ad2ff70a8746c52ab30d945ebff95246edd..e15be516369e5e5a9096bb957966938e134e616c 100644 (file)
@@ -53,7 +53,7 @@ die () {
 die_with_status () {
        status=$1
        shift
-       echo >&2 "$*"
+       printf >&2 '%s\n' "$*"
        exit "$status"
 }
 
diff --git a/git.c b/git.c
index 2025f77d0181ba646e3c793a693e5d71348b966c..b3893e73c93c59431516bccb86e12e8f64d39319 100644 (file)
--- a/git.c
+++ b/git.c
@@ -147,6 +147,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--glob-pathspecs")) {
+                       setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1);
+                       if (envchanged)
+                               *envchanged = 1;
+               } else if (!strcmp(cmd, "--noglob-pathspecs")) {
+                       setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1);
+                       if (envchanged)
+                               *envchanged = 1;
+               } else if (!strcmp(cmd, "--icase-pathspecs")) {
+                       setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--shallow-file")) {
                        (*argv)++;
                        (*argc)--;
index f429f75897ed10684d017aeaeb93f1c9d9478e0b..68c77f6f8ffd120f0011eb07accdc2849afca834 100755 (executable)
@@ -4035,8 +4035,8 @@ sub print_search_form {
              $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
              $cgi->popup_menu(-name => 'st', -default => 'commit',
                               -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
-             $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
-             " search:\n",
+             " " . $cgi->a({-href => href(action=>"search_help"),
+                            -title => "search help" }, "?") . " search:\n",
              $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
              "<span title=\"Extended regular expression\">" .
              $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
@@ -6468,7 +6468,7 @@ sub git_summary {
        print "<div class=\"title\">&nbsp;</div>\n";
        print "<table class=\"projects_list\">\n" .
              "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
-        unless ($omit_owner) {
+        if ($owner and not $omit_owner) {
                print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
         }
        if (defined $cd{'rfc2822'}) {
@@ -6631,6 +6631,7 @@ sub git_blame_common {
                        $hash_base, '--', $file_name
                        or die_error(500, "Open git-blame --porcelain failed");
        }
+       binmode $fd, ':utf8';
 
        # incremental blame data returns early
        if ($format eq 'data') {
index cb86d2d029637ec64f70cc9e1cdbeeea972e8771..3b4d83382306772ac0b62743b0e23094f3d9f257 100644 (file)
@@ -68,12 +68,13 @@ div.page_path {
 }
 
 div.page_footer {
-       height: 17px;
+       height: 22px;
        padding: 4px 8px;
        background-color: #d9d8d1;
 }
 
 div.page_footer_text {
+       line-height: 22px;
        float: left;
        color: #555555;
        font-style: italic;
@@ -548,8 +549,7 @@ a.linenr {
 
 a.rss_logo {
        float: right;
-       padding: 3px 0px;
-       width: 35px;
+       padding: 3px 5px;
        line-height: 10px;
        border: 1px solid;
        border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
index 6dad188b5f31d47fdce86e5104aa3c6b899ebed7..eea158a8785c4d7c87724146c141edadac0f8dfe 100644 (file)
@@ -1330,8 +1330,7 @@ static struct object_list **process_tree(struct tree *tree,
                        break;
                }
 
-       free(tree->buffer);
-       tree->buffer = NULL;
+       free_tree_buffer(tree);
        return p;
 }
 
diff --git a/http.c b/http.c
index 2d086aedfac9a8ea384c53f3b1963149b880ed31..f3e1439d58a00328ece38a6eead63309fe7f58ca 100644 (file)
--- a/http.c
+++ b/http.c
@@ -3,6 +3,7 @@
 #include "sideband.h"
 #include "run-command.h"
 #include "url.h"
+#include "urlmatch.h"
 #include "credential.h"
 #include "version.h"
 #include "pkt-line.h"
@@ -45,6 +46,7 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static const char *curl_cookie_file;
+static int curl_save_cookies;
 static struct credential http_auth = CREDENTIAL_INIT;
 static int http_proactive_auth;
 static const char *user_agent;
@@ -160,8 +162,7 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.sslcainfo", var))
                return git_config_string(&ssl_cainfo, var, value);
        if (!strcmp("http.sslcertpasswordprotected", var)) {
-               if (git_config_bool(var, value))
-                       ssl_cert_password_required = 1;
+               ssl_cert_password_required = git_config_bool(var, value);
                return 0;
        }
        if (!strcmp("http.ssltry", var)) {
@@ -200,6 +201,10 @@ static int http_options(const char *var, const char *value, void *cb)
 
        if (!strcmp("http.cookiefile", var))
                return git_config_string(&curl_cookie_file, var, value);
+       if (!strcmp("http.savecookies", var)) {
+               curl_save_cookies = git_config_bool(var, value);
+               return 0;
+       }
 
        if (!strcmp("http.postbuffer", var)) {
                http_post_buffer = git_config_int(var, value);
@@ -341,10 +346,20 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 {
        char *low_speed_limit;
        char *low_speed_time;
+       char *normalized_url;
+       struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+
+       config.section = "http";
+       config.key = NULL;
+       config.collect_fn = http_options;
+       config.cascade_fn = git_default_config;
+       config.cb = NULL;
 
        http_is_verbose = 0;
+       normalized_url = url_normalize(url, &config.url);
 
-       git_config(http_options, NULL);
+       git_config(urlmatch_config_entry, &config);
+       free(normalized_url);
 
        curl_global_init(CURL_GLOBAL_ALL);
 
@@ -513,6 +528,8 @@ struct active_request_slot *get_active_slot(void)
        slot->callback_data = NULL;
        slot->callback_func = NULL;
        curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
+       if (curl_save_cookies)
+               curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
index d6b65e204c6009e5c30f358810198319b70eda25..6f5cc4f782ea82fc14ee596c7bac96e2b6f907e7 100644 (file)
 #include "prompt.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
-#else
-#ifdef APPLE_COMMON_CRYPTO
-#include <CommonCrypto/CommonHMAC.h>
-#define HMAC_CTX CCHmacContext
-#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len)
-#define HMAC_Update CCHmacUpdate
-#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash)
-#define HMAC_CTX_cleanup(ignore)
-#define EVP_md5() kCCHmacAlgMD5
-#else
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-#endif
-#include <openssl/x509v3.h>
 #endif
 
 static const char imap_send_usage[] = "git imap-send < <mbox>";
index c2d01dccc2a12a767c442de58ed78df130699d74..8b6e497b3ff187ee328cba395f30d3db7f86c0b4 100644 (file)
@@ -23,7 +23,7 @@ static void range_set_grow(struct range_set *rs, size_t extra)
 /* Either initialization would be fine */
 #define RANGE_SET_INIT {0}
 
-static void range_set_init(struct range_set *rs, size_t prealloc)
+void range_set_init(struct range_set *rs, size_t prealloc)
 {
        rs->alloc = rs->nr = 0;
        rs->ranges = NULL;
@@ -31,7 +31,7 @@ static void range_set_init(struct range_set *rs, size_t prealloc)
                range_set_grow(rs, prealloc);
 }
 
-static void range_set_release(struct range_set *rs)
+void range_set_release(struct range_set *rs)
 {
        free(rs->ranges);
        rs->alloc = rs->nr = 0;
@@ -56,7 +56,7 @@ static void range_set_move(struct range_set *dst, struct range_set *src)
 }
 
 /* tack on a _new_ range _at the end_ */
-static void range_set_append_unsafe(struct range_set *rs, long a, long b)
+void range_set_append_unsafe(struct range_set *rs, long a, long b)
 {
        assert(a <= b);
        range_set_grow(rs, 1);
@@ -65,7 +65,7 @@ static void range_set_append_unsafe(struct range_set *rs, long a, long b)
        rs->nr++;
 }
 
-static void range_set_append(struct range_set *rs, long a, long b)
+void range_set_append(struct range_set *rs, long a, long b)
 {
        assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a);
        range_set_append_unsafe(rs, a, b);
@@ -107,7 +107,7 @@ static void range_set_check_invariants(struct range_set *rs)
  * In-place pass of sorting and merging the ranges in the range set,
  * to establish the invariants when we get the ranges from the user
  */
-static void sort_and_merge_range_set(struct range_set *rs)
+void sort_and_merge_range_set(struct range_set *rs)
 {
        int i;
        int o = 0; /* output cursor */
@@ -291,7 +291,6 @@ static void line_log_data_insert(struct line_log_data **list,
 
        if (p) {
                range_set_append_unsafe(&p->ranges, begin, end);
-               sort_and_merge_range_set(&p->ranges);
                free(path);
                return;
        }
@@ -299,7 +298,6 @@ static void line_log_data_insert(struct line_log_data **list,
        p = xcalloc(1, sizeof(struct line_log_data));
        p->path = path;
        range_set_append(&p->ranges, begin, end);
-       sort_and_merge_range_set(&p->ranges);
        if (ip) {
                p->next = ip->next;
                ip->next = p;
@@ -566,12 +564,14 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
        struct nth_line_cb cb_data;
        struct string_list_item *item;
        struct line_log_data *ranges = NULL;
+       struct line_log_data *p;
 
        for_each_string_list_item(item, args) {
                const char *name_part, *range_part;
                char *full_name;
                struct diff_filespec *spec;
                long begin = 0, end = 0;
+               long anchor;
 
                name_part = skip_range_arg(item->string);
                if (!name_part || *name_part != ':' || !name_part[1])
@@ -590,17 +590,23 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
                cb_data.lines = lines;
                cb_data.line_ends = ends;
 
+               p = search_line_log_data(ranges, full_name, NULL);
+               if (p && p->ranges.nr)
+                       anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1;
+               else
+                       anchor = 1;
+
                if (parse_range_arg(range_part, nth_line, &cb_data,
-                                   lines, &begin, &end,
+                                   lines, anchor, &begin, &end,
                                    full_name))
                        die("malformed -L argument '%s'", range_part);
+               if (lines < end || ((lines || begin) && lines < begin))
+                       die("file %s has only %lu lines", name_part, lines);
                if (begin < 1)
                        begin = 1;
                if (end < 1)
                        end = lines;
                begin--;
-               if (lines < end || lines < begin)
-                       die("file %s has only %ld lines", name_part, lines);
                line_log_data_insert(&ranges, full_name, begin, end);
 
                free_filespec(spec);
@@ -608,6 +614,9 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
                ends = NULL;
        }
 
+       for (p = ranges; p; p = p->next)
+               sort_and_merge_range_set(&p->ranges);
+
        return ranges;
 }
 
@@ -751,7 +760,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
                        r = r->next;
                }
                paths[count] = NULL;
-               init_pathspec(&rev->diffopt.pathspec, paths);
+               parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths);
                free(paths);
        }
 }
index 8bea45fd78f0368fef373be080ea5fa77d428c7f..a9212d84e492304b4e57da1e0327189b3b6434ed 100644 (file)
@@ -25,6 +25,18 @@ struct diff_ranges {
        struct range_set target;
 };
 
+extern void range_set_init(struct range_set *, size_t prealloc);
+extern void range_set_release(struct range_set *);
+/* Range includes start; excludes end */
+extern void range_set_append_unsafe(struct range_set *, long start, long end);
+/* New range must begin at or after end of last added range */
+extern void range_set_append(struct range_set *, long start, long end);
+/*
+ * In-place pass of sorting and merging the ranges in the range set,
+ * to sort and make the ranges disjoint.
+ */
+extern void sort_and_merge_range_set(struct range_set *);
+
 /* Linked list of interesting files and their associated ranges.  The
  * list must be kept sorted by path.
  *
index 3942475c2fc8e48c7f0a2d7808f77a4f71a1cde6..de4e32f9424fbc0770756c3f61751fc69bfeb17e 100644 (file)
@@ -6,6 +6,18 @@
 
 /*
  * Parse one item in the -L option
+ *
+ * 'begin' is applicable only to relative range anchors. Absolute anchors
+ * ignore this value.
+ *
+ * When parsing "-L A,B", parse_loc() is called once for A and once for B.
+ *
+ * When parsing A, 'begin' must be a negative number, the absolute value of
+ * which is the line at which relative start-of-range anchors should be
+ * based. Beginning of file is represented by -1.
+ *
+ * When parsing B, 'begin' must be the positive line number immediately
+ * following the line computed for 'A'.
  */
 static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
                             void *data, long lines, long begin, long *ret)
@@ -21,11 +33,13 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
         * for 20 lines, or "-L <something>,-5" for 5 lines ending at
         * <something>.
         */
-       if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+       if (1 <= begin && (spec[0] == '+' || spec[0] == '-')) {
                num = strtol(spec + 1, &term, 10);
                if (term != spec + 1) {
                        if (!ret)
                                return term;
+                       if (num == 0)
+                               die("-L invalid empty range");
                        if (spec[0] == '-')
                                num = 0 - num;
                        if (0 < num)
@@ -40,10 +54,23 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
        }
        num = strtol(spec, &term, 10);
        if (term != spec) {
-               if (ret)
+               if (ret) {
+                       if (num <= 0)
+                               die("-L invalid line number: %ld", num);
                        *ret = num;
+               }
                return term;
        }
+
+       if (begin < 0) {
+               if (spec[0] != '^')
+                       begin = -begin;
+               else {
+                       begin = 1;
+                       spec++;
+               }
+       }
+
        if (spec[0] != '/')
                return spec;
 
@@ -83,7 +110,8 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
        else {
                char errbuf[1024];
                regerror(reg_error, &regexp, errbuf, 1024);
-               die("-L parameter '%s': %s", spec + 1, errbuf);
+               die("-L parameter '%s' starting at line %ld: %s",
+                   spec + 1, begin + 1, errbuf);
        }
 }
 
@@ -136,7 +164,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
 }
 
 static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
-                                       void *cb_data, long lines, long *begin, long *end,
+                                       void *cb_data, long lines, long anchor, long *begin, long *end,
                                        const char *path)
 {
        char *pattern;
@@ -148,6 +176,11 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
        int reg_error;
        regex_t regexp;
 
+       if (*arg == '^') {
+               anchor = 1;
+               arg++;
+       }
+
        assert(*arg == ':');
        term = arg+1;
        while (*term && *term != ':') {
@@ -162,7 +195,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
 
        pattern = xstrndup(arg+1, term-(arg+1));
 
-       start = nth_line_cb(cb_data, 0);
+       anchor--; /* input is in human terms */
+       start = nth_line_cb(cb_data, anchor);
 
        drv = userdiff_find_by_path(path);
        if (drv && drv->funcname.pattern) {
@@ -180,7 +214,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
 
        p = find_funcname_matching_regexp(xecfg, (char*) start, &regexp);
        if (!p)
-               die("-L parameter '%s': no match", pattern);
+               die("-L parameter '%s' starting at line %ld: no match",
+                   pattern, anchor + 1);
        *begin = 0;
        while (p > nth_line_cb(cb_data, *begin))
                (*begin)++;
@@ -208,19 +243,24 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
 }
 
 int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
-                   void *cb_data, long lines, long *begin, long *end,
-                   const char *path)
+                   void *cb_data, long lines, long anchor,
+                   long *begin, long *end, const char *path)
 {
        *begin = *end = 0;
 
-       if (*arg == ':') {
-               arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
+       if (anchor < 1)
+               anchor = 1;
+       if (anchor > lines)
+               anchor = lines + 1;
+
+       if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) {
+               arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, anchor, begin, end, path);
                if (!arg || *arg)
                        return -1;
                return 0;
        }
 
-       arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
+       arg = parse_loc(arg, nth_line_cb, cb_data, lines, -anchor, begin);
 
        if (*arg == ',')
                arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
@@ -238,8 +278,8 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
 
 const char *skip_range_arg(const char *arg)
 {
-       if (*arg == ':')
-               return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL);
+       if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':'))
+               return parse_range_funcname(arg, NULL, NULL, 0, 0, NULL, NULL, NULL);
 
        arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
 
index ae3d0123b447c1c619695c2d0aaf619d1daaa991..83ba3c25e88a4601dc17ce01dc57dfbc33c05b2e 100644 (file)
@@ -9,6 +9,9 @@
  * line 'lno' inside the 'cb_data'.  The caller is expected to already
  * have a suitable map at hand to make this a constant-time lookup.
  *
+ * 'anchor' is the 1-based line at which relative range specifications
+ * should be anchored. Absolute ranges are unaffected by this value.
+ *
  * Returns 0 in case of success and -1 if there was an error.  The
  * actual range is stored in *begin and *end.  The counting starts
  * at 1!  In case of error, the caller should show usage message.
@@ -18,7 +21,7 @@ typedef const char *(*nth_line_fn_t)(void *data, long lno);
 
 extern int parse_range_arg(const char *arg,
                           nth_line_fn_t nth_line_cb,
-                          void *cb_data, long lines,
+                          void *cb_data, long lines, long anchor,
                           long *begin, long *end,
                           const char *path);
 
index 3dd4a960190a1b0016b26dec9187692111b73e3f..c8c3463cad6db4145434ee08f6c3a494e26a8675 100644 (file)
@@ -123,8 +123,7 @@ static void process_tree(struct rev_info *revs,
                                     cb_data);
        }
        strbuf_setlen(base, baselen);
-       free(tree->buffer);
-       tree->buffer = NULL;
+       free_tree_buffer(tree);
 }
 
 static void mark_edge_parents_uninteresting(struct commit *commit,
index a49d8e895d3ad24f00e8504ec3eccfbddead6b4c..8534d91826f3cf05f810a28728ee8bd51649d819 100644 (file)
@@ -738,7 +738,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        sha1 = commit->tree->object.sha1;
 
        /* Root commit? */
-       parents = commit->parents;
+       parents = get_saved_parents(opt, commit);
        if (!parents) {
                if (opt->show_root_diff) {
                        diff_root_tree_sha1(sha1, "", &opt->diffopt);
index 44614fc411fa7948d710c07a0262454aa0e76d0b..a7969c4c2e45df4f139c0bc85520f3bbc34da619 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -153,8 +153,7 @@ static void read_mailmap_line(struct string_list *map, char *buffer,
                if (!strncmp(buffer, abbrev, abblen)) {
                        char *cp;
 
-                       if (repo_abbrev)
-                               free(*repo_abbrev);
+                       free(*repo_abbrev);
                        *repo_abbrev = xmalloc(len);
 
                        for (cp = buffer + abblen; isspace(*cp); cp++)
@@ -193,20 +192,17 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
        return 0;
 }
 
-static void read_mailmap_buf(struct string_list *map,
-                            const char *buf, unsigned long len,
-                            char **repo_abbrev)
+static void read_mailmap_string(struct string_list *map, char *buf,
+                               char **repo_abbrev)
 {
-       while (len) {
-               const char *end = strchrnul(buf, '\n');
-               unsigned long linelen = end - buf + 1;
-               char *line = xmemdupz(buf, linelen);
+       while (*buf) {
+               char *end = strchrnul(buf, '\n');
 
-               read_mailmap_line(map, line, repo_abbrev);
+               if (*end)
+                       *end++ = '\0';
 
-               free(line);
-               buf += linelen;
-               len -= linelen;
+               read_mailmap_line(map, buf, repo_abbrev);
+               buf = end;
        }
 }
 
@@ -230,7 +226,7 @@ static int read_mailmap_blob(struct string_list *map,
        if (type != OBJ_BLOB)
                return error("mailmap is not a blob: %s", name);
 
-       read_mailmap_buf(map, buf, size, repo_abbrev);
+       read_mailmap_string(map, buf, repo_abbrev);
 
        free(buf);
        return 0;
index f95933b0aacce84a8b911634e5c40d5e937a322b..40eb840a52acce0d21a17ce5a898e93f164aef97 100644 (file)
@@ -298,7 +298,7 @@ static int get_files_dirs(struct merge_options *o, struct tree *tree)
 {
        int n;
        struct pathspec match_all;
-       init_pathspec(&match_all, NULL);
+       memset(&match_all, 0, sizeof(match_all));
        if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o))
                return 0;
        n = o->current_file_set.nr + o->current_directory_set.nr;
index ab1885707403e06fe058ece2e0455e25e194224c..94a1a8ae466733b207ae5030f8a8894d66535f91 100644 (file)
@@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
                       sha1_to_hex(mp->remote));
        }
        diff_flush(&opt);
-       diff_tree_release_paths(&opt);
+       free_pathspec(&opt.pathspec);
 
        *num_changes = len;
        return changes;
@@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o,
                       sha1_to_hex(mp->local));
        }
        diff_flush(&opt);
-       diff_tree_release_paths(&opt);
+       free_pathspec(&opt.pathspec);
 }
 
 static void check_notes_merge_worktree(struct notes_merge_options *o)
index b4cb1bfb4332a7e370071ac6699a9cf48e4da44c..564e30cccd550f9acbd493f43c940bab1abba427 100644 (file)
@@ -9,7 +9,7 @@
  * Properties of the created commit:
  * - tree: the result of converting t to a tree object with write_notes_tree().
  * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
- * - author/committer: the default determined by commmit_tree().
+ * - author/committer: the default determined by commit_tree().
  * - commit message: msg
  *
  * The resulting commit SHA1 is stored in result_sha1.
index c2cbca25cc6bb010996063c88c9af3e7b38aad5a..62e9b1cc6831768315dd74eac30348668de22da3 100644 (file)
@@ -43,8 +43,42 @@ static void fix_filename(const char *prefix, const char **file)
        *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
 }
 
+static int opt_command_mode_error(const struct option *opt,
+                                 const struct option *all_opts,
+                                 int flags)
+{
+       const struct option *that;
+       struct strbuf message = STRBUF_INIT;
+       struct strbuf that_name = STRBUF_INIT;
+
+       /*
+        * Find the other option that was used to set the variable
+        * already, and report that this is not compatible with it.
+        */
+       for (that = all_opts; that->type != OPTION_END; that++) {
+               if (that == opt ||
+                   that->type != OPTION_CMDMODE ||
+                   that->value != opt->value ||
+                   that->defval != *(int *)opt->value)
+                       continue;
+
+               if (that->long_name)
+                       strbuf_addf(&that_name, "--%s", that->long_name);
+               else
+                       strbuf_addf(&that_name, "-%c", that->short_name);
+               strbuf_addf(&message, ": incompatible with %s", that_name.buf);
+               strbuf_release(&that_name);
+               opterror(opt, message.buf, flags);
+               strbuf_release(&message);
+               return -1;
+       }
+       return opterror(opt, ": incompatible with something else", flags);
+}
+
 static int get_value(struct parse_opt_ctx_t *p,
-                    const struct option *opt, int flags)
+                    const struct option *opt,
+                    const struct option *all_opts,
+                    int flags)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
@@ -83,6 +117,16 @@ static int get_value(struct parse_opt_ctx_t *p,
                *(int *)opt->value = unset ? 0 : opt->defval;
                return 0;
 
+       case OPTION_CMDMODE:
+               /*
+                * Giving the same mode option twice, although is unnecessary,
+                * is not a grave error, so let it pass.
+                */
+               if (*(int *)opt->value && *(int *)opt->value != opt->defval)
+                       return opt_command_mode_error(opt, all_opts, flags);
+               *(int *)opt->value = opt->defval;
+               return 0;
+
        case OPTION_SET_PTR:
                *(void **)opt->value = unset ? NULL : (void *)opt->defval;
                return 0;
@@ -143,12 +187,13 @@ static int get_value(struct parse_opt_ctx_t *p,
 
 static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
 {
+       const struct option *all_opts = options;
        const struct option *numopt = NULL;
 
        for (; options->type != OPTION_END; options++) {
                if (options->short_name == *p->opt) {
                        p->opt = p->opt[1] ? p->opt + 1 : NULL;
-                       return get_value(p, options, OPT_SHORT);
+                       return get_value(p, options, all_opts, OPT_SHORT);
                }
 
                /*
@@ -177,6 +222,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
 static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                           const struct option *options)
 {
+       const struct option *all_opts = options;
        const char *arg_end = strchr(arg, '=');
        const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
        int abbrev_flags = 0, ambiguous_flags = 0;
@@ -253,7 +299,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                                continue;
                        p->opt = rest + 1;
                }
-               return get_value(p, options, flags ^ opt_flags);
+               return get_value(p, options, all_opts, flags ^ opt_flags);
        }
 
        if (ambiguous_option)
@@ -265,18 +311,20 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        (abbrev_flags & OPT_UNSET) ?  "no-" : "",
                        abbrev_option->long_name);
        if (abbrev_option)
-               return get_value(p, abbrev_option, abbrev_flags);
+               return get_value(p, abbrev_option, all_opts, abbrev_flags);
        return -2;
 }
 
 static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
                            const struct option *options)
 {
+       const struct option *all_opts = options;
+
        for (; options->type != OPTION_END; options++) {
                if (!(options->flags & PARSE_OPT_NODASH))
                        continue;
                if (options->short_name == arg[0] && arg[1] == '\0')
-                       return get_value(p, options, OPT_SHORT);
+                       return get_value(p, options, all_opts, OPT_SHORT);
        }
        return -2;
 }
index 9b94596e4aa113518d502502a0ca1bf804a60cbc..8736006ed73560ebb05abac2debb04d5117dc114 100644 (file)
@@ -13,6 +13,7 @@ enum parse_opt_type {
        OPTION_COUNTUP,
        OPTION_SET_INT,
        OPTION_SET_PTR,
+       OPTION_CMDMODE,
        /* options with arguments (usually) */
        OPTION_STRING,
        OPTION_INTEGER,
@@ -21,9 +22,6 @@ enum parse_opt_type {
        OPTION_FILENAME
 };
 
-/* Deprecated synonym */
-#define OPTION_BOOLEAN OPTION_COUNTUP
-
 enum parse_opt_flags {
        PARSE_OPT_KEEP_DASHDASH = 1,
        PARSE_OPT_STOP_AT_NON_OPTION = 2,
@@ -128,8 +126,12 @@ struct option {
 #define OPT_SET_INT(s, l, v, h, i)  { OPTION_SET_INT, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG, NULL, (i) }
 #define OPT_BOOL(s, l, v, h)        OPT_SET_INT(s, l, v, h, 1)
+#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
 #define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG, NULL, (p) }
+#define OPT_CMDMODE(s, l, v, h, i) { OPTION_CMDMODE, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
 #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 #define OPT_STRING_LIST(s, l, v, a, h) \
diff --git a/path.c b/path.c
index 3d244d3e03ae5c3d5afe452f903ba204849ad03b..9fd28bcd080ef0a64f0b8d8a5d9d680a741d75c3 100644 (file)
--- a/path.c
+++ b/path.c
@@ -543,8 +543,14 @@ const char *relative_path(const char *in, const char *prefix,
  *
  * Note that this function is purely textual.  It does not follow symlinks,
  * verify the existence of the path, or make any system calls.
+ *
+ * prefix_len != NULL is for a specific case of prefix_pathspec():
+ * assume that src == dst and src[0..prefix_len-1] is already
+ * normalized, any time "../" eats up to the prefix_len part,
+ * prefix_len is reduced. In the end prefix_len is the remaining
+ * prefix that has not been overridden by user pathspec.
  */
-int normalize_path_copy(char *dst, const char *src)
+int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
 {
        char *dst0;
 
@@ -619,11 +625,18 @@ int normalize_path_copy(char *dst, const char *src)
                /* Windows: dst[-1] cannot be backslash anymore */
                while (dst0 < dst && dst[-1] != '/')
                        dst--;
+               if (prefix_len && *prefix_len > dst - dst0)
+                       *prefix_len = dst - dst0;
        }
        *dst = '\0';
        return 0;
 }
 
+int normalize_path_copy(char *dst, const char *src)
+{
+       return normalize_path_copy_len(dst, src, NULL);
+}
+
 /*
  * path = Canonical absolute path
  * prefixes = string_list containing normalized, absolute paths without
index 6ea0867493ad3e8cfd613dd57fa5ac9649507b47..ad1a9f5b28c5119275fbcf9a53e62651190de137 100644 (file)
@@ -15,8 +15,8 @@
  * If seen[] has not already been written to, it may make sense
  * to use find_pathspecs_matching_against_index() instead.
  */
-void add_pathspec_matches_against_index(const char **pathspec,
-                                       char *seen, int specs)
+void add_pathspec_matches_against_index(const struct pathspec *pathspec,
+                                       char *seen)
 {
        int num_unmatched = 0, i;
 
@@ -26,14 +26,14 @@ void add_pathspec_matches_against_index(const char **pathspec,
         * mistakenly think that the user gave a pathspec that did not match
         * anything.
         */
-       for (i = 0; i < specs; i++)
+       for (i = 0; i < pathspec->nr; i++)
                if (!seen[i])
                        num_unmatched++;
        if (!num_unmatched)
                return;
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
-               match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+               match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
        }
 }
 
@@ -45,57 +45,430 @@ void add_pathspec_matches_against_index(const char **pathspec,
  * nature of the "closest" (i.e. most specific) matches which each of the
  * given pathspecs achieves against all items in the index.
  */
-char *find_pathspecs_matching_against_index(const char **pathspec)
+char *find_pathspecs_matching_against_index(const struct pathspec *pathspec)
 {
-       char *seen;
-       int i;
-
-       for (i = 0; pathspec[i];  i++)
-               ; /* just counting */
-       seen = xcalloc(i, 1);
-       add_pathspec_matches_against_index(pathspec, seen, i);
+       char *seen = xcalloc(pathspec->nr, 1);
+       add_pathspec_matches_against_index(pathspec, seen);
        return seen;
 }
 
 /*
- * Check the index to see whether path refers to a submodule, or
- * something inside a submodule.  If the former, returns the path with
- * any trailing slash stripped.  If the latter, dies with an error
- * message.
+ * Magic pathspec
+ *
+ * Possible future magic semantics include stuff like:
+ *
+ *     { PATHSPEC_RECURSIVE, '*', "recursive" },
+ *     { PATHSPEC_REGEXP, '\0', "regexp" },
+ *
+ */
+
+static struct pathspec_magic {
+       unsigned bit;
+       char mnemonic; /* this cannot be ':'! */
+       const char *name;
+} pathspec_magic[] = {
+       { PATHSPEC_FROMTOP, '/', "top" },
+       { PATHSPEC_LITERAL,   0, "literal" },
+       { PATHSPEC_GLOB,   '\0', "glob" },
+       { PATHSPEC_ICASE,  '\0', "icase" },
+};
+
+/*
+ * Take an element of a pathspec and check for magic signatures.
+ * Append the result to the prefix. Return the magic bitmap.
+ *
+ * For now, we only parse the syntax and throw out anything other than
+ * "top" magic.
+ *
+ * NEEDSWORK: This needs to be rewritten when we start migrating
+ * get_pathspec() users to use the "struct pathspec" interface.  For
+ * example, a pathspec element may be marked as case-insensitive, but
+ * the prefix part must always match literally, and a single stupid
+ * string cannot express such a case.
  */
-const char *check_path_for_gitlink(const char *path)
+static unsigned prefix_pathspec(struct pathspec_item *item,
+                               unsigned *p_short_magic,
+                               const char **raw, unsigned flags,
+                               const char *prefix, int prefixlen,
+                               const char *elt)
 {
-       int i, path_len = strlen(path);
-       for (i = 0; i < active_nr; i++) {
-               const struct cache_entry *ce = active_cache[i];
-               if (S_ISGITLINK(ce->ce_mode)) {
-                       int ce_len = ce_namelen(ce);
-                       if (path_len <= ce_len || path[ce_len] != '/' ||
-                           memcmp(ce->name, path, ce_len))
-                               /* path does not refer to this
-                                * submodule or anything inside it */
+       static int literal_global = -1;
+       static int glob_global = -1;
+       static int noglob_global = -1;
+       static int icase_global = -1;
+       unsigned magic = 0, short_magic = 0, global_magic = 0;
+       const char *copyfrom = elt, *long_magic_end = NULL;
+       char *match;
+       int i, pathspec_prefix = -1;
+
+       if (literal_global < 0)
+               literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
+       if (literal_global)
+               global_magic |= PATHSPEC_LITERAL;
+
+       if (glob_global < 0)
+               glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
+       if (glob_global)
+               global_magic |= PATHSPEC_GLOB;
+
+       if (noglob_global < 0)
+               noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);
+
+       if (glob_global && noglob_global)
+               die(_("global 'glob' and 'noglob' pathspec settings are incompatible"));
+
+
+       if (icase_global < 0)
+               icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
+       if (icase_global)
+               global_magic |= PATHSPEC_ICASE;
+
+       if ((global_magic & PATHSPEC_LITERAL) &&
+           (global_magic & ~PATHSPEC_LITERAL))
+               die(_("global 'literal' pathspec setting is incompatible "
+                     "with all other global pathspec settings"));
+
+       if (elt[0] != ':' || literal_global) {
+               ; /* nothing to do */
+       } else if (elt[1] == '(') {
+               /* longhand */
+               const char *nextat;
+               for (copyfrom = elt + 2;
+                    *copyfrom && *copyfrom != ')';
+                    copyfrom = nextat) {
+                       size_t len = strcspn(copyfrom, ",)");
+                       if (copyfrom[len] == ',')
+                               nextat = copyfrom + len + 1;
+                       else
+                               /* handle ')' and '\0' */
+                               nextat = copyfrom + len;
+                       if (!len)
                                continue;
-                       if (path_len == ce_len + 1) {
-                               /* path refers to submodule;
-                                * strip trailing slash */
-                               return xstrndup(ce->name, ce_len);
+                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+                               if (strlen(pathspec_magic[i].name) == len &&
+                                   !strncmp(pathspec_magic[i].name, copyfrom, len)) {
+                                       magic |= pathspec_magic[i].bit;
+                                       break;
+                               }
+                               if (!prefixcmp(copyfrom, "prefix:")) {
+                                       char *endptr;
+                                       pathspec_prefix = strtol(copyfrom + 7,
+                                                                &endptr, 10);
+                                       if (endptr - copyfrom != len)
+                                               die(_("invalid parameter for pathspec magic 'prefix'"));
+                                       /* "i" would be wrong, but it does not matter */
+                                       break;
+                               }
+                       }
+                       if (ARRAY_SIZE(pathspec_magic) <= i)
+                               die(_("Invalid pathspec magic '%.*s' in '%s'"),
+                                   (int) len, copyfrom, elt);
+               }
+               if (*copyfrom != ')')
+                       die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
+               long_magic_end = copyfrom;
+               copyfrom++;
+       } else {
+               /* shorthand */
+               for (copyfrom = elt + 1;
+                    *copyfrom && *copyfrom != ':';
+                    copyfrom++) {
+                       char ch = *copyfrom;
+
+                       if (!is_pathspec_magic(ch))
+                               break;
+                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+                               if (pathspec_magic[i].mnemonic == ch) {
+                                       short_magic |= pathspec_magic[i].bit;
+                                       break;
+                               }
+                       if (ARRAY_SIZE(pathspec_magic) <= i)
+                               die(_("Unimplemented pathspec magic '%c' in '%s'"),
+                                   ch, elt);
+               }
+               if (*copyfrom == ':')
+                       copyfrom++;
+       }
+
+       magic |= short_magic;
+       *p_short_magic = short_magic;
+
+       /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */
+       if (noglob_global && !(magic & PATHSPEC_GLOB))
+               global_magic |= PATHSPEC_LITERAL;
+
+       /* --glob-pathspec is overriden by :(literal) */
+       if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
+               global_magic &= ~PATHSPEC_GLOB;
+
+       magic |= global_magic;
+
+       if (pathspec_prefix >= 0 &&
+           (prefixlen || (prefix && *prefix)))
+               die("BUG: 'prefix' magic is supposed to be used at worktree's root");
+
+       if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB))
+               die(_("%s: 'literal' and 'glob' are incompatible"), elt);
+
+       if (pathspec_prefix >= 0) {
+               match = xstrdup(copyfrom);
+               prefixlen = pathspec_prefix;
+       } else if (magic & PATHSPEC_FROMTOP) {
+               match = xstrdup(copyfrom);
+               prefixlen = 0;
+       } else {
+               match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
+               if (!match)
+                       die(_("%s: '%s' is outside repository"), elt, copyfrom);
+       }
+       *raw = item->match = match;
+       /*
+        * Prefix the pathspec (keep all magic) and assign to
+        * original. Useful for passing to another command.
+        */
+       if (flags & PATHSPEC_PREFIX_ORIGIN) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *start = elt;
+               if (prefixlen && !literal_global) {
+                       /* Preserve the actual prefix length of each pattern */
+                       if (short_magic)
+                               die("BUG: prefixing on short magic is not supported");
+                       else if (long_magic_end) {
+                               strbuf_add(&sb, start, long_magic_end - start);
+                               strbuf_addf(&sb, ",prefix:%d", prefixlen);
+                               start = long_magic_end;
                        } else {
-                               die (_("Path '%s' is in submodule '%.*s'"),
-                                    path, ce_len, ce->name);
+                               if (*start == ':')
+                                       start++;
+                               strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
                        }
                }
+               strbuf_add(&sb, start, copyfrom - start);
+               strbuf_addstr(&sb, match);
+               item->original = strbuf_detach(&sb, NULL);
+       } else
+               item->original = elt;
+       item->len = strlen(item->match);
+       item->prefix = prefixlen;
+
+       if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
+           (item->len >= 1 && item->match[item->len - 1] == '/') &&
+           (i = cache_name_pos(item->match, item->len - 1)) >= 0 &&
+           S_ISGITLINK(active_cache[i]->ce_mode)) {
+               item->len--;
+               match[item->len] = '\0';
+       }
+
+       if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE)
+               for (i = 0; i < active_nr; i++) {
+                       struct cache_entry *ce = active_cache[i];
+                       int ce_len = ce_namelen(ce);
+
+                       if (!S_ISGITLINK(ce->ce_mode))
+                               continue;
+
+                       if (item->len <= ce_len || match[ce_len] != '/' ||
+                           memcmp(ce->name, match, ce_len))
+                               continue;
+                       if (item->len == ce_len + 1) {
+                               /* strip trailing slash */
+                               item->len--;
+                               match[item->len] = '\0';
+                       } else
+                               die (_("Pathspec '%s' is in submodule '%.*s'"),
+                                    elt, ce_len, ce->name);
+               }
+
+       if (magic & PATHSPEC_LITERAL)
+               item->nowildcard_len = item->len;
+       else {
+               item->nowildcard_len = simple_length(item->match);
+               if (item->nowildcard_len < prefixlen)
+                       item->nowildcard_len = prefixlen;
+       }
+       item->flags = 0;
+       if (magic & PATHSPEC_GLOB) {
+               /*
+                * FIXME: should we enable ONESTAR in _GLOB for
+                * pattern "* * / * . c"?
+                */
+       } else {
+               if (item->nowildcard_len < item->len &&
+                   item->match[item->nowildcard_len] == '*' &&
+                   no_wildcard(item->match + item->nowildcard_len + 1))
+                       item->flags |= PATHSPEC_ONESTAR;
        }
-       return path;
+
+       /* sanity checks, pathspec matchers assume these are sane */
+       assert(item->nowildcard_len <= item->len &&
+              item->prefix         <= item->len);
+       return magic;
+}
+
+static int pathspec_item_cmp(const void *a_, const void *b_)
+{
+       struct pathspec_item *a, *b;
+
+       a = (struct pathspec_item *)a_;
+       b = (struct pathspec_item *)b_;
+       return strcmp(a->match, b->match);
+}
+
+static void NORETURN unsupported_magic(const char *pattern,
+                                      unsigned magic,
+                                      unsigned short_magic)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int i, n;
+       for (n = i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+               const struct pathspec_magic *m = pathspec_magic + i;
+               if (!(magic & m->bit))
+                       continue;
+               if (sb.len)
+                       strbuf_addstr(&sb, " ");
+               if (short_magic & m->bit)
+                       strbuf_addf(&sb, "'%c'", m->mnemonic);
+               else
+                       strbuf_addf(&sb, "'%s'", m->name);
+               n++;
+       }
+       /*
+        * We may want to substitute "this command" with a command
+        * name. E.g. when add--interactive dies when running
+        * "checkout -p"
+        */
+       die(_("%s: pathspec magic not supported by this command: %s"),
+           pattern, sb.buf);
 }
 
 /*
- * Dies if the given path refers to a file inside a symlinked
- * directory in the index.
+ * Given command line arguments and a prefix, convert the input to
+ * pathspec. die() if any magic in magic_mask is used.
  */
-void die_if_path_beyond_symlink(const char *path, const char *prefix)
+void parse_pathspec(struct pathspec *pathspec,
+                   unsigned magic_mask, unsigned flags,
+                   const char *prefix, const char **argv)
 {
-       if (has_symlink_leading_path(path, strlen(path))) {
-               int len = prefix ? strlen(prefix) : 0;
-               die(_("'%s' is beyond a symbolic link"), path + len);
+       struct pathspec_item *item;
+       const char *entry = argv ? *argv : NULL;
+       int i, n, prefixlen;
+
+       memset(pathspec, 0, sizeof(*pathspec));
+
+       if (flags & PATHSPEC_MAXDEPTH_VALID)
+               pathspec->magic |= PATHSPEC_MAXDEPTH;
+
+       /* No arguments, no prefix -> no pathspec */
+       if (!entry && !prefix)
+               return;
+
+       if ((flags & PATHSPEC_PREFER_CWD) &&
+           (flags & PATHSPEC_PREFER_FULL))
+               die("BUG: PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible");
+
+       /* No arguments with prefix -> prefix pathspec */
+       if (!entry) {
+               static const char *raw[2];
+
+               if (flags & PATHSPEC_PREFER_FULL)
+                       return;
+
+               if (!(flags & PATHSPEC_PREFER_CWD))
+                       die("BUG: PATHSPEC_PREFER_CWD requires arguments");
+
+               pathspec->items = item = xmalloc(sizeof(*item));
+               memset(item, 0, sizeof(*item));
+               item->match = prefix;
+               item->original = prefix;
+               item->nowildcard_len = item->len = strlen(prefix);
+               item->prefix = item->len;
+               raw[0] = prefix;
+               raw[1] = NULL;
+               pathspec->nr = 1;
+               pathspec->_raw = raw;
+               return;
        }
+
+       n = 0;
+       while (argv[n])
+               n++;
+
+       pathspec->nr = n;
+       pathspec->items = item = xmalloc(sizeof(*item) * n);
+       pathspec->_raw = argv;
+       prefixlen = prefix ? strlen(prefix) : 0;
+
+       for (i = 0; i < n; i++) {
+               unsigned short_magic;
+               entry = argv[i];
+
+               item[i].magic = prefix_pathspec(item + i, &short_magic,
+                                               argv + i, flags,
+                                               prefix, prefixlen, entry);
+               if (item[i].magic & magic_mask)
+                       unsupported_magic(entry,
+                                         item[i].magic & magic_mask,
+                                         short_magic);
+
+               if ((flags & PATHSPEC_SYMLINK_LEADING_PATH) &&
+                   has_symlink_leading_path(item[i].match, item[i].len)) {
+                       die(_("pathspec '%s' is beyond a symbolic link"), entry);
+               }
+
+               if (item[i].nowildcard_len < item[i].len)
+                       pathspec->has_wildcard = 1;
+               pathspec->magic |= item[i].magic;
+       }
+
+
+       if (pathspec->magic & PATHSPEC_MAXDEPTH) {
+               if (flags & PATHSPEC_KEEP_ORDER)
+                       die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible");
+               qsort(pathspec->items, pathspec->nr,
+                     sizeof(struct pathspec_item), pathspec_item_cmp);
+       }
+}
+
+/*
+ * N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
+ * based interface - see pathspec.c:parse_pathspec().
+ *
+ * Arguments:
+ *  - prefix - a path relative to the root of the working tree
+ *  - pathspec - a list of paths underneath the prefix path
+ *
+ * Iterates over pathspec, prepending each path with prefix,
+ * and return the resulting list.
+ *
+ * If pathspec is empty, return a singleton list containing prefix.
+ *
+ * If pathspec and prefix are both empty, return an empty list.
+ *
+ * This is typically used by built-in commands such as add.c, in order
+ * to normalize argv arguments provided to the built-in into a list of
+ * paths to process, all relative to the root of the working tree.
+ */
+const char **get_pathspec(const char *prefix, const char **pathspec)
+{
+       struct pathspec ps;
+       parse_pathspec(&ps,
+                      PATHSPEC_ALL_MAGIC &
+                      ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+                      PATHSPEC_PREFER_CWD,
+                      prefix, pathspec);
+       return ps._raw;
+}
+
+void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
+{
+       *dst = *src;
+       dst->items = xmalloc(sizeof(struct pathspec_item) * dst->nr);
+       memcpy(dst->items, src->items,
+              sizeof(struct pathspec_item) * dst->nr);
+}
+
+void free_pathspec(struct pathspec *pathspec)
+{
+       free(pathspec->items);
+       pathspec->items = NULL;
 }
index db0184a1acda155fa2b6d6fa07b6a87dc0683e57..944baeb622cee4aeebb835d01982466cf2c2ce29 100644 (file)
@@ -1,8 +1,92 @@
 #ifndef PATHSPEC_H
 #define PATHSPEC_H
 
-extern char *find_pathspecs_matching_against_index(const char **pathspec);
-extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs);
+/* Pathspec magic */
+#define PATHSPEC_FROMTOP       (1<<0)
+#define PATHSPEC_MAXDEPTH      (1<<1)
+#define PATHSPEC_LITERAL       (1<<2)
+#define PATHSPEC_GLOB          (1<<3)
+#define PATHSPEC_ICASE         (1<<4)
+#define PATHSPEC_ALL_MAGIC       \
+       (PATHSPEC_FROMTOP       | \
+        PATHSPEC_MAXDEPTH      | \
+        PATHSPEC_LITERAL       | \
+        PATHSPEC_GLOB          | \
+        PATHSPEC_ICASE)
+
+#define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
+
+struct pathspec {
+       const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */
+       int nr;
+       unsigned int has_wildcard:1;
+       unsigned int recursive:1;
+       unsigned magic;
+       int max_depth;
+       struct pathspec_item {
+               const char *match;
+               const char *original;
+               unsigned magic;
+               int len, prefix;
+               int nowildcard_len;
+               int flags;
+       } *items;
+};
+
+#define GUARD_PATHSPEC(ps, mask) \
+       do { \
+               if ((ps)->magic & ~(mask))             \
+                       die("BUG:%s:%d: unsupported magic %x",  \
+                           __FILE__, __LINE__, (ps)->magic & ~(mask)); \
+       } while (0)
+
+/* parse_pathspec flags */
+#define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */
+#define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */
+#define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */
+/* strip the trailing slash if the given path is a gitlink */
+#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3)
+/* die if a symlink is part of the given path's directory */
+#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4)
+/*
+ * This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP
+ * (but not the same): it strips the trailing slash if the given path
+ * is a gitlink but also checks and dies if gitlink is part of the
+ * leading path (i.e. the given path goes beyond a submodule). It's
+ * safer than _SLASH_CHEAP and also more expensive.
+ */
+#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5)
+#define PATHSPEC_PREFIX_ORIGIN (1<<6)
+#define PATHSPEC_KEEP_ORDER (1<<7)
+
+extern void parse_pathspec(struct pathspec *pathspec,
+                          unsigned magic_mask,
+                          unsigned flags,
+                          const char *prefix,
+                          const char **args);
+extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
+extern void free_pathspec(struct pathspec *);
+
+static inline int ps_strncmp(const struct pathspec_item *item,
+                            const char *s1, const char *s2, size_t n)
+{
+       if (item->magic & PATHSPEC_ICASE)
+               return strncasecmp(s1, s2, n);
+       else
+               return strncmp(s1, s2, n);
+}
+
+static inline int ps_strcmp(const struct pathspec_item *item,
+                           const char *s1, const char *s2)
+{
+       if (item->magic & PATHSPEC_ICASE)
+               return strcasecmp(s1, s2);
+       else
+               return strcmp(s1, s2);
+}
+
+extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec);
+extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen);
 extern const char *check_path_for_gitlink(const char *path);
 extern void die_if_path_beyond_symlink(const char *path, const char *prefix);
 
index 7a252ef872c92213c8dcd2144a84cdcf787e586a..204fdc673754c82637c59992198a2c8d31e05fa8 100644 (file)
@@ -61,7 +61,7 @@ =head1 SYNOPSIS
                 remote_refs prompt
                 get_tz_offset
                 credential credential_read credential_write
-                temp_acquire temp_release temp_reset temp_path);
+                temp_acquire temp_is_locked temp_release temp_reset temp_path);
 
 
 =head1 DESCRIPTION
@@ -1206,6 +1206,35 @@ sub temp_acquire {
        $temp_fd;
 }
 
+=item temp_is_locked ( NAME )
+
+Returns true if the internal lock created by a previous C<temp_acquire()>
+call with C<NAME> is still in effect.
+
+When temp_acquire is called on a C<NAME>, it internally locks the temporary
+file mapped to C<NAME>.  That lock will not be released until C<temp_release()>
+is called with either the original C<NAME> or the L<File::Handle> that was
+returned from the original call to temp_acquire.
+
+Subsequent attempts to call C<temp_acquire()> with the same C<NAME> will fail
+unless there has been an intervening C<temp_release()> call for that C<NAME>
+(or its corresponding L<File::Handle> that was returned by the original
+C<temp_acquire()> call).
+
+If true is returned by C<temp_is_locked()> for a C<NAME>, an attempt to
+C<temp_acquire()> the same C<NAME> will cause an error unless
+C<temp_release> is first called on that C<NAME> (or its corresponding
+L<File::Handle> that was returned by the original C<temp_acquire()> call).
+
+=cut
+
+sub temp_is_locked {
+       my ($self, $name) = _maybe_self(@_);
+       my $temp_fd = \$TEMP_FILEMAP{$name};
+
+       defined $$temp_fd && $$temp_fd->opened && $TEMP_FILES{$$temp_fd}{locked};
+}
+
 =item temp_release ( NAME )
 
 =item temp_release ( FILEHANDLE )
index bd174189b98fda05f781697be26db94c9cd53364..10edb27732f439e5290a7b354efefeb10d219669 100644 (file)
@@ -315,11 +315,13 @@ sub change_file_prop {
 sub apply_textdelta {
        my ($self, $fb, $exp) = @_;
        return undef if $self->is_path_ignored($fb->{path});
-       my $fh = $::_repository->temp_acquire('svn_delta');
+       my $suffix = 0;
+       ++$suffix while $::_repository->temp_is_locked("svn_delta_${$}_$suffix");
+       my $fh = $::_repository->temp_acquire("svn_delta_${$}_$suffix");
        # $fh gets auto-closed() by SVN::TxDelta::apply(),
        # (but $base does not,) so dup() it for reading in close_file
        open my $dup, '<&', $fh or croak $!;
-       my $base = $::_repository->temp_acquire('git_blob');
+       my $base = $::_repository->temp_acquire("git_blob_${$}_$suffix");
 
        if ($fb->{blob}) {
                my ($base_is_link, $size);
index 297fc8a529217bc5bb097d967f7c7ce1eb5d2caf..107aa5917cac70b715d8bdac1d0e83a2f5da2d3d 100644 (file)
--- a/po/TEAMS
+++ b/po/TEAMS
@@ -15,6 +15,7 @@ Members:      Thomas Rast <trast@student.ethz.ch>
 Language:      fr (French)
 Repository:    https://github.com/jnavila/git
 Leader:                Jean-Noël Avila <jn.avila@free.fr>
+Members:       Sébastien Helleu <flashcode@flashtux.org>
 
 Language:      is (Icelandic)
 Leader:                Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
index 11dde114f3b682e782090bfd903953a327d9bc12..f4076fb633ca970f22281a6ff98c229587945c69 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -4694,12 +4694,12 @@ msgstr "git describe [Optionen] --dirty"
 #: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
-msgstr "annotierter Tag %s ist nicht verfügbar"
+msgstr "annotiertes Tag %s ist nicht verfügbar"
 
 #: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
-msgstr "annotierter Tag %s hat keinen eingebetteten Namen"
+msgstr "annotiertes Tag %s hat keinen eingebetteten Namen"
 
 #: builtin/describe.c:243
 #, c-format
@@ -4765,7 +4765,7 @@ msgstr ""
 
 #: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
-msgstr "findet den Tag, die nach Commit kommt"
+msgstr "findet das Tag, das nach Commit kommt"
 
 #: builtin/describe.c:410
 msgid "debug search strategy on stderr"
@@ -4777,7 +4777,7 @@ msgstr "verwendet alle Referenzen"
 
 #: builtin/describe.c:412
 msgid "use any tag, even unannotated"
-msgstr "verwendet jeden Tag, auch nicht-annotierte"
+msgstr "verwendet jedes Tag, auch nicht-annotierte"
 
 #: builtin/describe.c:413
 msgid "always use long format"
@@ -4880,7 +4880,7 @@ msgstr "Importiert Kennzeichen von dieser Datei"
 
 #: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
-msgstr "erzeugt künstlich einen Tag-Ersteller, wenn der Tag keinen hat"
+msgstr "erzeugt künstlich einen Tag-Ersteller, wenn das Tag keinen hat"
 
 #: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
@@ -5013,7 +5013,7 @@ msgstr "  (kann lokale Referenz nicht aktualisieren)"
 
 #: builtin/fetch.c:324
 msgid "[new tag]"
-msgstr "[neuer Tag]"
+msgstr "[neues Tag]"
 
 #: builtin/fetch.c:327
 msgid "[new branch]"
@@ -7831,7 +7831,7 @@ msgstr ""
 #: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil der Tag bereits\n"
+"Aktualisierungen wurden zurückgewiesen, weil das Tag bereits\n"
 "im Remote-Repository existiert."
 
 #: builtin/push.c:260
@@ -9244,7 +9244,7 @@ msgstr "Optionen für Erstellung von Tags"
 
 #: builtin/tag.c:454
 msgid "annotated tag, needs a message"
-msgstr "annotierter Tag, benötigt eine Beschreibung"
+msgstr "annotiertes Tag, benötigt eine Beschreibung"
 
 #: builtin/tag.c:456
 msgid "tag message"
@@ -9252,15 +9252,15 @@ msgstr "Tag-Beschreibung"
 
 #: builtin/tag.c:458
 msgid "annotated and GPG-signed tag"
-msgstr "annotierter und GPG-signierter Tag"
+msgstr "annotiertes und GPG-signiertes Tag"
 
 #: builtin/tag.c:462
 msgid "use another key to sign the tag"
-msgstr "verwendet einen anderen Schlüssel um den Tag zu signieren"
+msgstr "verwendet einen anderen Schlüssel um das Tag zu signieren"
 
 #: builtin/tag.c:463
 msgid "replace the tag if exists"
-msgstr "ersetzt den Tag, wenn er existiert"
+msgstr "ersetzt das Tag, wenn es existiert"
 
 #: builtin/tag.c:464
 msgid "show tag list in columns"
index fa98cfe4cff6f31fdd4618222471c6b049cc0cea..2cdb6f93a3b683ff4d25bdc711b330844e24d7d0 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,15 +1,63 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# French translations for Git.
+# Copyright (C) 2013 Jean-Noël Avila <jn.avila@free.fr>
+# This file is distributed under the same license as the Git package.
+# Jean-Noël Avila <jn.avila@free.fr>, 2013.
+# Sébastien Helleu <flashcode@flashtux.org>, 2013.
+#
+# French translations of common Git words used in this file:
+#
+#   English          |  French
+#   -----------------+---------------------------------
+#   #NN              |  n°NN
+#   a commit         |  un commit
+#   backward         |
+#     compatibility  |  rétrocompatibilité
+#   bare repository  |  dépôt nu
+#   bisect           |  bissection
+#   blob             |  blob
+#   bug              |  bogue
+#   bundle           |  colis
+#   cherry-pick      |  picorer
+#   dangling         |  en suspens
+#   debugging        |  débogage
+#   fast-forward     |  avance rapide
+#   fast-forwarded   |  mis Ã  jour en avance rapide
+#   glob             |  glob
+#   hash             |  hachage
+#   HEAD             |  HEAD (genre féminin)
+#   hook             |  hook
+#   hunk             |  section
+#   merge            |  fusion
+#   pattern          |  motif
+#   repository       |  dépôt
+#   remote           |  distante (ou serveur distant)
+#   revision         |  révision
+#   stash            |  remisage
+#   tag              |  Ã©tiquette
+#   template         |  modèle
+#   to checkout      |  extraire
+#   to commit        |  valider
+#   to fetch         |  rapatrier
+#   to prune         |  Ã©laguer
+#   to push          |  pousser
+#   to rebase        |  rebaser
+#   to stash         |  remiser
+#   to track         |  suivre
+#   to unstage       |  désindexer
+#   tree-ish         |  arbre
+#   upstream         |  amont
+#   worktree /       |
+#   work(ing) tree   |  copie de travail
 #
 msgid ""
-msgstr "Project-Id-Version: git\n"
+msgstr ""
+"Project-Id-Version: git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
 "POT-Creation-Date: 2013-04-30 08:25+0800\n"
-"PO-Revision-Date: 2013-07-02 22:28+0100\n"
-"Last-Translator: Jean-Noël Avila <jn.avila@free.fr>\n"
+"PO-Revision-Date: 2013-08-27 19:43+0200\n"
+"Last-Translator: Sébastien Helleu <flashcode@flashtux.org>\n"
 "Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n"
+"Language: fr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -19,7 +67,7 @@ msgstr "Project-Id-Version: git\n"
 #: advice.c:53
 #, c-format
 msgid "hint: %.*s\n"
-msgstr "astuce: %*s\n"
+msgstr "astuce: %.*s\n"
 
 #.
 #. * Message used both when 'git commit' fails and when
@@ -31,8 +79,9 @@ msgid ""
 "and then use 'git add/rm <file>' as\n"
 "appropriate to mark resolution and make a commit,\n"
 "or use 'git commit -a'."
-msgstr "Corrigez-les dans l'espace de travail,\n"
-"et utilisez 'git add/rm <fichier>' comme\n"
+msgstr ""
+"Corrigez-les dans la copie de travail,\n"
+"et utilisez 'git add/rm <fichier>' si\n"
 "nécessaire pour marquer la résolution et valider,\n"
 "ou utilisez 'git commit -a'."
 
@@ -47,7 +96,7 @@ msgstr "git archive --list"
 #: archive.c:12
 msgid ""
 "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"
-msgstr "git archive --remote <dépot> [--exec <commande>] [options] <arbre> [<chemin>...]"
+msgstr "git archive --remote <dépôt> [--exec <commande>] [options] <arbre> [<chemin>...]"
 
 #: archive.c:13
 msgid "git archive --remote <repo> [--exec <cmd>] --list"
@@ -67,7 +116,7 @@ msgstr "préfixe"
 
 #: archive.c:325
 msgid "prepend prefix to each pathname in the archive"
-msgstr "Préfixer chaque chemin de fichier dans l'archive"
+msgstr "préfixer chaque chemin de fichier dans l'archive"
 
 #: archive.c:326 builtin/archive.c:88 builtin/blame.c:2371
 #: builtin/blame.c:2372 builtin/config.c:55 builtin/fast-export.c:665
@@ -79,7 +128,7 @@ msgstr "fichier"
 
 #: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
-msgstr "Ã\89crire l'archive dans ce fichier"
+msgstr "écrire l'archive dans ce fichier"
 
 #: archive.c:329
 msgid "read .gitattributes in working directory"
@@ -125,8 +174,9 @@ msgstr "chemin vers la commande distante git-upload-archive"
 msgid ""
 "Negative patterns are ignored in git attributes\n"
 "Use '\\!' for literal leading exclamation."
-msgstr "Les patrons négatifs sont ignorés dans les attributs git\n"
-"Utilisez '\\!' pour un point d'exclamation littéral"
+msgstr ""
+"Les motifs de négation sont ignorés dans les attributs git\n"
+"Utilisez '\\!' pour un point d'exclamation littéral."
 
 #: branch.c:60
 #, c-format
@@ -217,30 +267,30 @@ msgid ""
 "If you are planning to push out a new local branch that\n"
 "will track its remote counterpart, you may want to use\n"
 "\"git push -u\" to set the upstream config as you push."
-msgstr "\n"
+msgstr ""
+"\n"
 "Si vous comptez baser votre travail sur une branche\n"
 "amont qui existe déjà sur le serveur distant, vous pourriez\n"
 "devoir lancer \"git fetch\" pour la récupérer.\n"
 "\n"
-"Si vous comptez pousser une nouvelle branche locale\n"
-"qui suivra sa jumelle distante, vous souhaiterez utiliser\n"
-"\"git push -u\" pour paramétrer le suivi distant en même\n"
-"temps que vous poussez."
+"Si vous comptez pousser une nouvelle branche locale qui suivra\n"
+"sa jumelle distante, vous souhaiterez utiliser \"git push -u\"\n"
+"pour paramétrer le suivi distant en même temps que vous poussez."
 
 #: branch.c:250
 #, c-format
 msgid "Not a valid object name: '%s'."
-msgstr "Nom d'objet invalide : '%s'"
+msgstr "Nom d'objet invalide : '%s'."
 
 #: branch.c:270
 #, c-format
 msgid "Ambiguous object name: '%s'."
-msgstr "Nom d'objet ambigu : '%s'"
+msgstr "Nom d'objet ambigu : '%s'."
 
 #: branch.c:275
 #, c-format
 msgid "Not a valid branch point: '%s'."
-msgstr "Point d'embranchement invalide : '%s'"
+msgstr "Point d'embranchement invalide : '%s'."
 
 #: branch.c:281
 msgid "Failed to lock ref for update"
@@ -258,7 +308,7 @@ msgstr "'%s' ne semble pas Ãªtre un fichier bundle v2"
 #: bundle.c:63
 #, c-format
 msgid "unrecognized header: %s%s (%d)"
-msgstr "entête non reconnu : %s %s (%d)"
+msgstr "en-tête non reconnu : %s%s (%d)"
 
 #: bundle.c:89 builtin/commit.c:676
 #, c-format
@@ -284,13 +334,13 @@ msgstr[1] "Le colis contient ces %d références :"
 
 #: bundle.c:193
 msgid "The bundle records a complete history."
-msgstr "Le bundle enregistre l'historique complet."
+msgstr "Le colis enregistre l'historique complet."
 
 #: bundle.c:195
 #, c-format
 msgid "The bundle requires this ref:"
 msgid_plural "The bundle requires these %d refs:"
-msgstr[0] "Le colis exige cette référence"
+msgstr[0] "Le colis exige cette référence :"
 msgstr[1] "Le colis exige ces %d références :"
 
 #: bundle.c:294
@@ -305,11 +355,11 @@ msgstr "argument non reconnu : %s"
 #: bundle.c:335
 #, c-format
 msgid "ref '%s' is excluded by the rev-list options"
-msgstr "ref '%s' est exclus par les options de rev-list"
+msgstr "la référence '%s' est exclue par les options de rev-list"
 
 #: bundle.c:380
 msgid "Refusing to create empty bundle."
-msgstr "Refus de créer un bundle vide"
+msgstr "Refus de créer un colis vide."
 
 #: bundle.c:398
 msgid "Could not spawn pack-objects"
@@ -340,16 +390,16 @@ msgstr "%s %s n'est pas un commit !"
 
 #: compat/obstack.c:406 compat/obstack.c:408
 msgid "memory exhausted"
-msgstr "Plus de mémoire"
+msgstr "plus de mémoire"
 
 #: connected.c:39
 msgid "Could not run 'git rev-list'"
-msgstr "impossible de lancer 'git rev-list'"
+msgstr "Impossible de lancer 'git rev-list'"
 
 #: connected.c:48
 #, c-format
 msgid "failed write to rev-list: %s"
-msgstr "impossible d'écrire la rev-list : %s"
+msgstr "impossible d'écrire dans la rev-list : %s"
 
 #: connected.c:56
 #, c-format
@@ -507,14 +557,16 @@ msgstr "commandes git disponibles depuis un autre endroit de votre $PATH"
 
 #: help.c:235
 msgid "The most commonly used git commands are:"
-msgstr "Les commandes git les plus usitées sont :"
+msgstr "Les commandes git les plus utilisées sont :"
 
 #: help.c:292
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
 "able to execute it. Maybe git-%s is broken?"
-msgstr "'%s' semble Ãªtre une commande git, mais elle n'a pas pu Ãªtre Ã©xecutée. Peut-être git-%s est-il cassé ?"
+msgstr ""
+"'%s' semble Ãªtre une commande git, mais elle n'a pas pu\n"
+"être exécutée. Peut-être git-%s est-elle cassée ?"
 
 #: help.c:349
 msgid "Uh oh. Your system reports no Git commands at all."
@@ -526,17 +578,17 @@ msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
 "Continuing under the assumption that you meant '%s'"
 msgstr "ATTENTION : vous avez invoqué une commande Git nommée '%s' qui n'existe pas.\n"
-"Poursuite avec l'hypothèse que vous avez voulu dire '%s'"
+"Poursuite en supposant que vous avez voulu dire '%s'"
 
 #: help.c:376
 #, c-format
 msgid "in %0.1f seconds automatically..."
-msgstr "dans %01f secondes automatiquement..."
+msgstr "dans %0.1f secondes automatiquement..."
 
 #: help.c:383
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
-msgstr "git : '%s' n'est pas une commande git. Référez-vous Ã  'git --help'"
+msgstr "git : '%s' n'est pas une commande git. Voir 'git --help'."
 
 #: help.c:387
 msgid ""
@@ -547,8 +599,7 @@ msgid_plural ""
 "Did you mean one of these?"
 msgstr[0] "\n"
 "Vouliez-vous dire cela ?"
-msgstr[1] "\n"
-"Vouliez-vous dire un de cela ?"
+msgstr[1] "\nVouliez-vous dire un de ceux-là ?"
 
 #: merge.c:56
 msgid "failed to read the cache"
@@ -567,11 +618,11 @@ msgstr "(mauvais commit)\n"
 #: merge-recursive.c:206
 #, c-format
 msgid "addinfo_cache failed for path '%s'"
-msgstr "Ã\89chec de addinfo_cache pour le chemin '%s'"
+msgstr "échec de addinfo_cache pour le chemin '%s'"
 
 #: merge-recursive.c:268
 msgid "error building trees"
-msgstr "Erreur Ã  la construction des arbres"
+msgstr "erreur de construction des arbres"
 
 #: merge-recursive.c:672
 #, c-format
@@ -607,12 +658,12 @@ msgstr "blob attendu pour %s '%s'"
 #: merge-recursive.c:773 builtin/clone.c:313
 #, c-format
 msgid "failed to open '%s'"
-msgstr "Ã\89chec Ã  l'ouverture de '%s'"
+msgstr "échec Ã  l'ouverture de '%s'"
 
 #: merge-recursive.c:781
 #, c-format
 msgid "failed to symlink '%s'"
-msgstr "Ã\89chec Ã  la création du lien symbolique '%s'"
+msgstr "échec Ã  la création du lien symbolique '%s'"
 
 #: merge-recursive.c:784
 #, c-format
@@ -644,7 +695,7 @@ msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
 "in tree at %s."
-msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans%s. Version %s de %s laissée dans l'arbre dans le fichier %s."
+msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s de %s laissée dans l'arbre dans le fichier %s."
 
 #: merge-recursive.c:1081
 msgid "rename"
@@ -664,7 +715,7 @@ msgstr "%s est un répertoire dans %s ajouté plutôt comme %s"
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
 "\"->\"%s\" in \"%s\"%s"
-msgstr "CONFLIT (renommage/renommage) : Renommage de '%s'->'%s' dans la branche '%s' et renommage '%s'->'%s' dans '%s'%s"
+msgstr "CONFLIT (renommage/renommage) : Renommage de \"%s\"->\"%s\" dans la branche \"%s\" et renommage \"%s\"->\"%s\" dans \"%s\"%s"
 
 #: merge-recursive.c:1164
 msgid " (left unresolved)"
@@ -678,12 +729,12 @@ msgstr "CONFLIT (renommage/renommage) : renommage '%s'->'%s' dans %s. Renommage
 #: merge-recursive.c:1248
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
-msgstr "Renommage préféré de %s en %s et de %s en %s"
+msgstr "Renommage de %s en %s et de %s en %s Ã  la place"
 
 #: merge-recursive.c:1447
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
-msgstr "CONFLIT (renommage/ajout) : renommage de '%s'->'%s' dans ' %s. %s ajouté dans %s"
+msgstr "CONFLIT (renommage/ajout) : Renommage de %s->%s dans %s. %s ajouté dans %s"
 
 #: merge-recursive.c:1457
 #, c-format
@@ -729,7 +780,7 @@ msgstr "%s ignoré (fusion identique Ã  l'existant)"
 #: merge-recursive.c:1629
 #, c-format
 msgid "Auto-merging %s"
-msgstr "fusion automatique de %s"
+msgstr "Fusion automatique de %s"
 
 #: merge-recursive.c:1633 git-submodule.sh:1029
 msgid "submodule"
@@ -738,7 +789,7 @@ msgstr "sous-module"
 #: merge-recursive.c:1634
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
-msgstr "CONFLIT (%s) : conflit de fusion dans %s"
+msgstr "CONFLIT (%s) : Conflit de fusion dans %s"
 
 #: merge-recursive.c:1724
 #, c-format
@@ -774,7 +825,7 @@ msgstr "Déjà Ã  jour !"
 #: merge-recursive.c:1815
 #, c-format
 msgid "merging of trees %s and %s failed"
-msgstr "Ã\89chec de fusion des arbres %s et %s"
+msgstr "échec de fusion des arbres %s et %s"
 
 #: merge-recursive.c:1845
 #, c-format
@@ -808,7 +859,7 @@ msgstr "Impossible d'écrire l'index."
 #: object.c:195
 #, c-format
 msgid "unable to parse object: %s"
-msgstr "Impossible d'analyser l'objet '%s'"
+msgstr "impossible d'analyser l'objet : %s"
 
 #: parse-options.c:489
 msgid "..."
@@ -849,24 +900,24 @@ msgstr "'%s' est au delà d'un lien symbolique"
 #, c-format
 msgid "Your branch is ahead of '%s' by %d commit.\n"
 msgid_plural "Your branch is ahead of '%s' by %d commits.\n"
-msgstr[0] "Votre branche est en avance sur '%s' par %d commit.\n"
-msgstr[1] "Votre branche est en avance sur '%s' par %d commits.\n"
+msgstr[0] "Votre branche est en avance sur '%s' de %d commit.\n"
+msgstr[1] "Votre branche est en avance sur '%s' de %d commits.\n"
 
 #: remote.c:1787
 msgid "  (use \"git push\" to publish your local commits)\n"
-msgstr "  (utilisez 'git push' pour publier vos commits locaux)\n"
+msgstr "  (utilisez \"git push\" pour publier vos commits locaux)\n"
 
 #: remote.c:1790
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
 "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
-msgstr[0] "Votre branche est en retard sur '%s' par %d commit, et peut Ãªtre mise Ã  jour en avance rapide.\n"
-msgstr[1] "Votre branche est en retard sur '%s' par %d commits, et peut Ãªtre mise Ã  jour en avance rapide.\n"
+msgstr[0] "Votre branche est en retard sur '%s' de %d commit, et peut Ãªtre mise Ã  jour en avance rapide.\n"
+msgstr[1] "Votre branche est en retard sur '%s' de %d commits, et peut Ãªtre mise Ã  jour en avance rapide.\n"
 
 #: remote.c:1798
 msgid "  (use \"git pull\" to update your local branch)\n"
-msgstr "  (  utilisez 'git pull' pour mettre Ã  jour votre branche locale)\n"
+msgstr "  (utilisez \"git pull\" pour mettre Ã  jour votre branche locale)\n"
 
 #: remote.c:1801
 #, c-format
@@ -909,7 +960,8 @@ msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'\n"
 "and commit the result with 'git commit'"
-msgstr "après résolution des conflits, marquez les chemins corrigés\n"
+msgstr ""
+"après résolution des conflits, marquez les chemins corrigés\n"
 "avec 'git add <chemins>' ou 'git rm <chemins>'\n"
 "puis validez le résultat avec 'git commit'"
 
@@ -925,7 +977,7 @@ msgstr "Erreur Ã  l'emballage de %s"
 
 #: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
-msgstr "Vos modifications locales serait Ã©crasées par cherry-pick."
+msgstr "Vos modifications locales seraient Ã©crasées par cherry-pick."
 
 #: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
@@ -939,7 +991,7 @@ msgstr "Validez vos modifications ou remisez-les pour continuer."
 #: sequencer.c:319
 #, c-format
 msgid "%s: Unable to write new index file"
-msgstr "%s: impossiblde d'écrire le nouveau fichier index"
+msgstr "%s: Impossible d'écrire le nouveau fichier index"
 
 #: sequencer.c:350
 msgid "Could not resolve HEAD commit\n"
@@ -971,7 +1023,7 @@ msgstr "Le commit %s est une fusion mais l'option -m n'a pas Ã©té spécifiée."
 #: sequencer.c:514
 #, c-format
 msgid "Commit %s does not have parent %d"
-msgstr "Le commit %s n'a pas %d pour parent"
+msgstr "Le commit %s n'a pas le parent %d"
 
 #: sequencer.c:518
 #, c-format
@@ -1060,7 +1112,7 @@ msgstr "Feuille d'options malformée : %s"
 
 #: sequencer.c:840
 msgid "a cherry-pick or revert is already in progress"
-msgstr "Un picorage ou un retour est déjà en cours"
+msgstr "un picorage ou un retour est déjà en cours"
 
 #: sequencer.c:841
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
@@ -1105,7 +1157,7 @@ msgstr "fin de fichier inattendue"
 #: sequencer.c:916
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
-msgstr "Le fichier HEAD de préparation de picorage '%s' est corrompu"
+msgstr "le fichier HEAD de préparation de picorage '%s' est corrompu"
 
 #: sequencer.c:939
 #, c-format
@@ -1120,7 +1172,7 @@ msgstr "%s : impossible de picorer un %s"
 #: sequencer.c:1085
 #, c-format
 msgid "%s: bad revision"
-msgstr "%s: mauvaise révision"
+msgstr "%s : mauvaise révision"
 
 #: sequencer.c:1119
 msgid "Can't revert as initial commit"
@@ -1128,7 +1180,7 @@ msgstr "Impossible d'annuler en tant que commit initial"
 
 #: sequencer.c:1120
 msgid "Can't cherry-pick into empty head"
-msgstr "Impossible de picorer vers un HEAD vide"
+msgstr "Impossible de picorer vers une HEAD vide"
 
 #: sha1_name.c:1036
 msgid "HEAD does not point to a branch"
@@ -1183,15 +1235,15 @@ msgstr "  (utilisez \"git rm --cached <fichier>...\" pour désindexer)"
 
 #: wt-status.c:173
 msgid "  (use \"git add <file>...\" to mark resolution)"
-msgstr "  (utilisez \"git add <fichier>...\" pour marquer résolu)"
+msgstr "  (utilisez \"git add <fichier>...\" pour marquer comme résolu)"
 
 #: wt-status.c:175 wt-status.c:179
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
-msgstr "  (utilisez \"git add/rm <fichier>...\" selon le cas pour marquer résolu)"
+msgstr "  (utilisez \"git add/rm <fichier>...\" si nécessaire pour marquer comme résolu)"
 
 #: wt-status.c:177
 msgid "  (use \"git rm <file>...\" to mark resolution)"
-msgstr "  (utilisez \"git rm <fichier>...\" pour marquer résolu)"
+msgstr "  (utilisez \"git rm <fichier>...\" pour marquer comme résolu)"
 
 #: wt-status.c:188
 msgid "Changes to be committed:"
@@ -1216,7 +1268,7 @@ msgstr "  (utilisez \"git checkout -- <fichier>...\" pour annuler les modificati
 
 #: wt-status.c:215
 msgid "  (commit or discard the untracked or modified content in submodules)"
-msgstr "  (valider out annuler le contenu non suivi our modifié dans les sous-modules)"
+msgstr "  (valider ou annuler le contenu non suivi ou modifié dans les sous-modules)"
 
 #: wt-status.c:227
 #, c-format
@@ -1229,7 +1281,7 @@ msgstr "bogue"
 
 #: wt-status.c:249
 msgid "both deleted:"
-msgstr "effacé des deux côtés :"
+msgstr "supprimé des deux côtés :"
 
 #: wt-status.c:250
 msgid "added by us:"
@@ -1237,7 +1289,7 @@ msgstr "ajouté par nous :"
 
 #: wt-status.c:251
 msgid "deleted by them:"
-msgstr "effacé par eux :"
+msgstr "supprimé par eux :"
 
 #: wt-status.c:252
 msgid "added by them:"
@@ -1245,11 +1297,11 @@ msgstr "ajouté par eux :"
 
 #: wt-status.c:253
 msgid "deleted by us:"
-msgstr "effacé par nous :"
+msgstr "supprimé par nous :"
 
 #: wt-status.c:254
 msgid "both added:"
-msgstr "Ajouté de deux côtés :"
+msgstr "ajouté de deux côtés :"
 
 #: wt-status.c:255
 msgid "both modified:"
@@ -1270,42 +1322,42 @@ msgstr "contenu non suivi, "
 #: wt-status.c:306
 #, c-format
 msgid "new file:   %s"
-msgstr "nouveau : %s"
+msgstr "nouveau :   %s"
 
 #: wt-status.c:309
 #, c-format
 msgid "copied:     %s -> %s"
-msgstr "copié :   %s->%s"
+msgstr "copié :     %s -> %s"
 
 #: wt-status.c:312
 #, c-format
 msgid "deleted:    %s"
-msgstr "effacé :  %s"
+msgstr "supprimé :  %s"
 
 #: wt-status.c:315
 #, c-format
 msgid "modified:   %s"
-msgstr "modifié : %s"
+msgstr "modifié :   %s"
 
 #: wt-status.c:318
 #, c-format
 msgid "renamed:    %s -> %s"
-msgstr "renommé : %s -> %s"
+msgstr "renommé :   %s -> %s"
 
 #: wt-status.c:321
 #, c-format
 msgid "typechange: %s"
-msgstr "nv type :  %s"
+msgstr "nv type :   %s"
 
 #: wt-status.c:324
 #, c-format
 msgid "unknown:    %s"
-msgstr "inconnu : %s"
+msgstr "inconnu :   %s"
 
 #: wt-status.c:327
 #, c-format
 msgid "unmerged:   %s"
-msgstr "non fus : %s"
+msgstr "non fus. :  %s"
 
 #: wt-status.c:330
 #, c-format
@@ -1346,7 +1398,7 @@ msgstr "  (utilisez \"git am --skip\" pour sauter ce patch)"
 
 #: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
-msgstr "  (utilisez \"git am --abort\" pour restaurer la branche originelle)"
+msgstr "  (utilisez \"git am --abort\" pour restaurer la branche d'origine)"
 
 #: wt-status.c:893 wt-status.c:910
 #, c-format
@@ -1416,7 +1468,7 @@ msgstr "  (tous les conflits sont résolus : lancez \"git commit\")"
 #: wt-status.c:970
 #, c-format
 msgid "You are currently reverting commit %s."
-msgstr "Vous Ãªtes actuellement en train de rétablir un commit %s."
+msgstr "Vous Ãªtes actuellement en train de rétablir le commit %s."
 
 #: wt-status.c:975
 msgid "  (fix conflicts and run \"git revert --continue\")"
@@ -1441,7 +1493,7 @@ msgstr "Vous Ãªtes en cours de bissection."
 
 #: wt-status.c:998
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
-msgstr "  (utilisez \"git bisect reset\" pour revenir Ã  la branche de départ)"
+msgstr "  (utilisez \"git bisect reset\" pour revenir Ã  la branche d'origine)"
 
 #: wt-status.c:1173
 msgid "On branch "
@@ -1449,11 +1501,11 @@ msgstr "Sur la branche "
 
 #: wt-status.c:1184
 msgid "HEAD detached at "
-msgstr "HEAD détaché sur "
+msgstr "HEAD détachée sur "
 
 #: wt-status.c:1186
 msgid "HEAD detached from "
-msgstr "HEAD détaché depuis "
+msgstr "HEAD détachée depuis "
 
 #: wt-status.c:1189
 msgid "Not currently on any branch."
@@ -1461,7 +1513,7 @@ msgstr "Actuellement sur aucun branche."
 
 #: wt-status.c:1206
 msgid "Initial commit"
-msgstr "Validation initiale."
+msgstr "Validation initiale"
 
 #: wt-status.c:1220
 msgid "Untracked files"
@@ -1484,7 +1536,7 @@ msgstr "L'énumération des fichiers non suivis a duré %.2f secondes. 'status -
 #: wt-status.c:1232
 #, c-format
 msgid "Untracked files not listed%s"
-msgstr "Les fichiers non suivis ne sont pas listés %s"
+msgstr "Fichiers non suivis non listés%s"
 
 #: wt-status.c:1234
 msgid " (use -u option to show untracked files)"
@@ -1497,29 +1549,29 @@ msgstr "Aucune modification"
 #: wt-status.c:1245
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
-msgstr "aucune modification n'a Ã©té ajoutée au commit (utilisez \"git add\" ou \"git commit -a\")\n"
+msgstr "aucune modification n'a Ã©té ajoutée Ã  la validation (utilisez \"git add\" ou \"git commit -a\")\n"
 
 #: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit\n"
-msgstr "aucune modification indexée\n"
+msgstr "aucune modification ajoutée Ã  la validation\n"
 
 #: wt-status.c:1251
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
-msgstr "Aucune modification indexée mais des fichiers non suivis sont présents (utilisez \"git add\" pour les suivre)\n"
+msgstr "aucune modification ajoutée Ã  la validation mais des fichiers non suivis sont présents (utilisez \"git add\" pour les suivre)\n"
 
 #: wt-status.c:1254
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
-msgstr "Aucune modification indexée mais des fichiers non-suivis sont présents\n"
+msgstr "aucune modification ajoutée Ã  la validation mais des fichiers non suivis sont présents\n"
 
 #: wt-status.c:1257
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
-msgstr "rien Ã  valider (créez/copiez des fichiers et utilisez \"git add\" pour le suivre)\n"
+msgstr "rien Ã  valider (créez/copiez des fichiers et utilisez \"git add\" pour les suivre)\n"
 
 #: wt-status.c:1260 wt-status.c:1265
 #, c-format
@@ -1593,10 +1645,10 @@ msgid ""
 "\n"
 "With the current Git version, the command is restricted to the current "
 "directory.\n"
-msgstr "Le comportement de 'git add %s (ou %s)' sans argument de chemin\n"
-"depuis un sous-répertoire du projet va changer dans Git 2.0. Cet usage\n"
-"est déconseillé.\n"
-"Pour ajouter du contenu pour tout le projet, lancez :\n"
+msgstr ""
+"Le comportement de 'git add %s (ou %s)' sans argument de chemin depuis un\n"
+"sous-répertoire du projet va changer dans Git 2.0 et ne doit plus Ãªtre utilisé.\n"
+"Pour ajouter le contenu de toute l'arborescence, lancez :\n"
 "\n"
 "  git add %s :/\n"
 "  (ou git add %s :/)\n"
@@ -1622,15 +1674,18 @@ msgid ""
 "* 'git add --all <pathspec>' will let you also record the removals.\n"
 "\n"
 "Run 'git status' to check the paths you removed from your working tree.\n"
-msgstr "Vous avez lancé 'git add' sans '-A (--all)' ni '--ignore-removal',\n"
-"Le comportement vis Ã  vis des fichiers supprimés va changer dans Git 2.0.\n"
-"Les chemins tels que '%s' qui ont Ã©té retirés de votre copie de travail sont\n"
-"actuellement ignorés.\n"
+msgstr ""
+"Vous avez lancé 'git add' sans '-A (--all)' ni '--ignore-removal',\n"
+"dont le comportement va changer dans Git 2.0 avec le respect des chemins que vous supprimez.\n"
+"Les chemins tels que '%s' qui ont Ã©té\n"
+"retirés de votre copie de travail sont ignorés avec cette version de Git.\n"
 "\n"
-" 'git add --ignore-removal <chemin>', est l'option par défaut actuelle qui\n"
-"  ignore les chemins que vous avez supprimé de votre copie de travail.\n"
+"* 'git add --ignore-removal <chemin>', qui est l'option par défaut actuelle,\n"
+"  ignore les chemins que vous avez supprimés de votre copie de travail.\n"
 "\n"
 "* 'git add --all <chemin>' permet d'enregistrer aussi les suppressions.\n"
+"\n"
+"Lancez 'git status' pour vérifier les chemins que vous avez supprimés de votre copie de travail.\n"
 
 #: builtin/add.c:144
 #, c-format
@@ -1639,16 +1694,16 @@ msgstr "status de diff inattendu %c"
 
 #: builtin/add.c:149 builtin/commit.c:233
 msgid "updating files failed"
-msgstr "echec de la mise Ã  jour des fichiers"
+msgstr "échec de la mise Ã  jour des fichiers"
 
 #: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
-msgstr "retrait de '%s'\n"
+msgstr "suppression de '%s'\n"
 
 #: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
-msgstr "Modifications non indexées après rafraîchissement de l'index"
+msgstr "Modifications non indexées après rafraîchissement de l'index :"
 
 #: builtin/add.c:256 builtin/add.c:572 builtin/rm.c:275
 #, c-format
@@ -1716,7 +1771,7 @@ msgstr "permettre l'ajout de fichiers ignorés"
 
 #: builtin/add.c:400
 msgid "update tracked files"
-msgstr "mise Ã  jour des fichiers suivis"
+msgstr "mettre Ã  jour les fichiers suivis"
 
 #: builtin/add.c:401
 msgid "record only the fact that the path will be added later"
@@ -1724,7 +1779,7 @@ msgstr "enregistrer seulement le fait que le chemin sera ajouté plus tard"
 
 #: builtin/add.c:402
 msgid "add changes from all tracked and untracked files"
-msgstr "ajouter les modifications de tous les fichiers suivis et non-suivis"
+msgstr "ajouter les modifications de tous les fichiers suivis et non suivis"
 
 #. takes no arguments
 #: builtin/add.c:405
@@ -1741,7 +1796,7 @@ msgstr "sauter seulement les fichiers qui ne peuvent pas Ãªtre ajoutés du fait
 
 #: builtin/add.c:409
 msgid "check if - even missing - files are ignored in dry run"
-msgstr "vérifier Ã  vide si des fichiers, même manquants,  sont ignorés"
+msgstr "vérifier si des fichiers - même manquants - sont ignorés, Ã  vide"
 
 #: builtin/add.c:431
 #, c-format
@@ -1754,7 +1809,7 @@ msgstr "aucun fichier ajouté"
 
 #: builtin/add.c:438
 msgid "adding files failed"
-msgstr "echec de l'ajout de fichiers"
+msgstr "échec de l'ajout de fichiers"
 
 #: builtin/add.c:477
 msgid "-A and -u are mutually incompatible"
@@ -1762,17 +1817,17 @@ msgstr "-A et -u sont mutuellement incompatibles"
 
 #: builtin/add.c:495
 msgid "Option --ignore-missing can only be used together with --dry-run"
-msgstr "L'option --ignore-missing ne peut Ãªtre utilisée qu'en complément de  --dry-run"
+msgstr "L'option --ignore-missing ne peut Ãªtre utilisée qu'en complément de --dry-run"
 
 #: builtin/add.c:525
 #, c-format
 msgid "Nothing specified, nothing added.\n"
-msgstr "Spécification vide, riien Ã  ajouter.\n"
+msgstr "Rien de spécifié, rien n'a Ã©té ajouté.\n"
 
 #: builtin/add.c:526
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
-msgstr "Vous voulez sûrement dire 'git add .' ?\n"
+msgstr "Vous vouliez sûrement dire 'git add .' ?\n"
 
 #: builtin/add.c:532 builtin/check-ignore.c:66 builtin/clean.c:204
 #: builtin/commit.c:293 builtin/mv.c:82 builtin/rm.c:235
@@ -1785,17 +1840,17 @@ msgstr "Impossible d'écrire le nouveau fichier d'index"
 
 #: builtin/apply.c:57
 msgid "git apply [options] [<patch>...]"
-msgstr "git apply [option] [<patch>...]"
+msgstr "git apply [options] [<patch>...]"
 
 #: builtin/apply.c:110
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "option d'espaces non reconnue '%s'"
+msgstr "option d'espace non reconnue '%s'"
 
 #: builtin/apply.c:125
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "option d'espaces ignorés non reconnue '%s'"
+msgstr "option d'ignorance d'espce non reconnue '%s'"
 
 #: builtin/apply.c:823
 #, c-format
@@ -1805,32 +1860,32 @@ msgstr "Impossible de préparer la regexp d'horodatage %s"
 #: builtin/apply.c:832
 #, c-format
 msgid "regexec returned %d for input: %s"
-msgstr "regexec a retourné %d pour l'entrée %s"
+msgstr "regexec a retourné %d pour l'entrée %s"
 
 #: builtin/apply.c:913
 #, c-format
 msgid "unable to find filename in patch at line %d"
-msgstr "nom de fichier du patch introuvable ligne %d"
+msgstr "nom de fichier du patch introuvable Ã  la ligne %d"
 
 #: builtin/apply.c:945
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
-msgstr "git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé ligne %d"
+msgstr "git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé Ã  la ligne %d"
 
 #: builtin/apply.c:949
 #, c-format
 msgid "git apply: bad git-diff - inconsistent new filename on line %d"
-msgstr "git apply : mauvais format de git-diff - nouveau nom de fichier inconsistant ligne %d"
+msgstr "git apply : mauvais format de git-diff - nouveau nom de fichier inconsistant Ã  la ligne %d"
 
 #: builtin/apply.c:950
 #, c-format
 msgid "git apply: bad git-diff - inconsistent old filename on line %d"
-msgstr "git apply : mauvais format de git-diff - ancien nom de fichier inconsistant ligne %d"
+msgstr "git apply : mauvais format de git-diff - ancien nom de fichier inconsistant Ã  la ligne %d"
 
 #: builtin/apply.c:957
 #, c-format
 msgid "git apply: bad git-diff - expected /dev/null on line %d"
-msgstr "git apply : mauvais format de git-diff - /dev/null attendu ligne %d"
+msgstr "git apply : mauvais format de git-diff - /dev/null attendu Ã  la ligne %d"
 
 #: builtin/apply.c:1422
 #, c-format
@@ -1840,7 +1895,7 @@ msgstr "recount : ligne inattendue : %.*s"
 #: builtin/apply.c:1479
 #, c-format
 msgid "patch fragment without header at line %d: %.*s"
-msgstr "fragment de patch sans entête ligne %d : %.*s"
+msgstr "fragment de patch sans en-tête Ã  la ligne %d : %.*s"
 
 #: builtin/apply.c:1496
 #, c-format
@@ -1850,8 +1905,8 @@ msgid ""
 msgid_plural ""
 "git diff header lacks filename information when removing %d leading pathname "
 "components (line %d)"
-msgstr[0] "information de nom de fichier manquante dans l'entête de git diff lors de la suppression de %d composant de préfix de chemin (ligne %d)"
-msgstr[1] "information de nom de fichier manquante dans l'entête de git diff lors de la suppression de %d composants de préfix de chemin (ligne %d)"
+msgstr[0] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composant de préfixe de chemin (ligne %d)"
+msgstr[1] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composants de préfixe de chemin (ligne %d)"
 
 #: builtin/apply.c:1656
 msgid "new file depends on old contents"
@@ -1879,28 +1934,28 @@ msgstr "le fichier supprimé %s a encore du contenu"
 #: builtin/apply.c:1725
 #, c-format
 msgid "** warning: file %s becomes empty but is not deleted"
-msgstr "** attention : le fichier %s devient vide mais n'est pas effacé"
+msgstr "** attention : le fichier %s devient vide mais n'est pas supprimé"
 
 #: builtin/apply.c:1871
 #, c-format
 msgid "corrupt binary patch at line %d: %.*s"
-msgstr "patch binaire corrompu ligne %d : %.*s"
+msgstr "patch binaire corrompu Ã  la ligne %d : %.*s"
 
 #. there has to be one hunk (forward hunk)
 #: builtin/apply.c:1900
 #, c-format
 msgid "unrecognized binary patch at line %d"
-msgstr "patch binaire non reconnu ligne %d"
+msgstr "patch binaire non reconnu Ã  la ligne %d"
 
 #: builtin/apply.c:1986
 #, c-format
 msgid "patch with only garbage at line %d"
-msgstr "patch totalement incompréhensible ligne %d"
+msgstr "patch totalement incompréhensible Ã  la ligne %d"
 
 #: builtin/apply.c:2076
 #, c-format
 msgid "unable to read symlink %s"
-msgstr "lecture du symlink %s impossible"
+msgstr "lecture du lien symbolique %s impossible"
 
 #: builtin/apply.c:2080
 #, c-format
@@ -1916,8 +1971,8 @@ msgstr "début de ligne invalide : '%c'"
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
-msgstr[0] "La section #%d a réussi Ã  la ligne %d (offset %d ligne)"
-msgstr[1] "La section #%d a réussi Ã  la ligne %d (offset %d lignes)"
+msgstr[0] "La section n°%d a réussi Ã  la ligne %d (offset %d ligne)."
+msgstr[1] "La section n°%d a réussi Ã  la ligne %d (offset %d lignes)."
 
 #: builtin/apply.c:2818
 #, c-format
@@ -1950,7 +2005,7 @@ msgstr "le patch binaire sur '%s' crée un résultat incorrect (%s attendu, mais
 #: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
-msgstr "le patch a Ã©choué : %s : %ld"
+msgstr "le patch a Ã©choué : %s:%ld"
 
 #: builtin/apply.c:3095
 #, c-format
@@ -1965,7 +2020,7 @@ msgstr "echec de la lecture de %s"
 #: builtin/apply.c:3173 builtin/apply.c:3395
 #, c-format
 msgid "path %s has been renamed/deleted"
-msgstr "le fichier %s a Ã©té renommé/supprimé"
+msgstr "le chemin %s a Ã©té renommé/supprimé"
 
 #: builtin/apply.c:3254 builtin/apply.c:3409
 #, c-format
@@ -2049,7 +2104,7 @@ msgstr "stat du fichier nouvellement créé '%s' impossible"
 #: builtin/apply.c:3859
 #, c-format
 msgid "unable to create backing store for newly created file %s"
-msgstr "création du magasin de sauvegard pour le fichier nouvellement créé %s impossible"
+msgstr "création du magasin de stockage pour le fichier nouvellement créé %s impossible"
 
 #: builtin/apply.c:3862 builtin/apply.c:3970
 #, c-format
@@ -2059,7 +2114,7 @@ msgstr "ajout de l'entrée de cache %s impossible"
 #: builtin/apply.c:3895
 #, c-format
 msgid "closing file '%s'"
-msgstr "fermeture du ficheir '%s'"
+msgstr "fermeture du fichier '%s'"
 
 #: builtin/apply.c:3944
 #, c-format
@@ -2091,12 +2146,12 @@ msgstr "troncature du nom de fichier .rej en %.*s.rej"
 #: builtin/apply.c:4073
 #, c-format
 msgid "Hunk #%d applied cleanly."
-msgstr "Section no %d appliqué proprement."
+msgstr "Section n°%d appliquée proprement."
 
 #: builtin/apply.c:4076
 #, c-format
 msgid "Rejected hunk #%d."
-msgstr "Section n%d rejetée."
+msgstr "Section n°%d rejetée."
 
 #: builtin/apply.c:4226
 msgid "unrecognized input"
@@ -2165,7 +2220,7 @@ msgstr "tenter une fusion Ã  3 points si le patch ne s'applique pas proprement"
 
 #: builtin/apply.c:4386
 msgid "build a temporary index based on embedded index information"
-msgstr "construire une index temporaire fondé sur l'information de l'index embarqué"
+msgstr "construire un index temporaire fondé sur l'information de l'index embarqué"
 
 #: builtin/apply.c:4388 builtin/checkout-index.c:197 builtin/ls-files.c:456
 msgid "paths are separated with NUL character"
@@ -2185,15 +2240,15 @@ msgstr "détecter des lignes nouvelles ou modifiées qui contiennent des erreurs
 
 #: builtin/apply.c:4396 builtin/apply.c:4399
 msgid "ignore changes in whitespace when finding context"
-msgstr "ignorer des modifications d'espaces lors de la recherche de contexte"
+msgstr "ignorer des modifications d'espace lors de la recherche de contexte"
 
 #: builtin/apply.c:4402
 msgid "apply the patch in reverse"
-msgstr "appliquer le patch en inverse"
+msgstr "appliquer le patch en sens inverse"
 
 #: builtin/apply.c:4404
 msgid "don't expect at least one line of context"
-msgstr "accepter moins d'une ligne de contexte"
+msgstr "ne pas s'attendre Ã  au moins une ligne de contexte"
 
 #: builtin/apply.c:4406
 msgid "leave the rejected hunks in corresponding *.rej files"
@@ -2205,11 +2260,11 @@ msgstr "accepter les recouvrements de sections"
 
 #: builtin/apply.c:4411
 msgid "tolerate incorrectly detected missing new-line at the end of file"
-msgstr "tolérer faux positifs de retours chariot manquant en fin de fichier"
+msgstr "tolérer des erreurs de détection de retours chariot manquants en fin de fichier"
 
 #: builtin/apply.c:4414
 msgid "do not trust the line counts in the hunk headers"
-msgstr "ne pas se fier au comptes de lignes dans les entêtes de section"
+msgstr "ne pas se fier au compte de lignes dans les en-têtes de section"
 
 #: builtin/apply.c:4416
 msgid "root"
@@ -2225,7 +2280,7 @@ msgstr "--3way hors d'un dépôt"
 
 #: builtin/apply.c:4447
 msgid "--index outside a repository"
-msgstr "--index hors d'une dépôt"
+msgstr "--index hors d'un dépôt"
 
 #: builtin/apply.c:4450
 msgid "--cached outside a repository"
@@ -2247,7 +2302,7 @@ msgstr[1] "%d erreurs d'espace ignorées"
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
-msgstr[0] "%d ligne ont ajouté des erreurs d'espace."
+msgstr[0] "%d ligne a ajouté des erreurs d'espace."
 msgstr[1] "%d lignes ont ajouté des erreurs d'espace."
 
 #: builtin/archive.c:17
@@ -2299,23 +2354,23 @@ msgstr "mettre Ã  jour BISECT_HEAD au lieu d'extraire le commit actuel"
 
 #: builtin/blame.c:25
 msgid "git blame [options] [rev-opts] [rev] [--] file"
-msgstr "git blame [options] [option de révision] [rev] [--] file"
+msgstr "git blame [options] [options-de-révision] [rev] [--] fichier"
 
 #: builtin/blame.c:30
 msgid "[rev-opts] are documented in git-rev-list(1)"
-msgstr "[rev-opts] sont documentés dans git-rev-list(1)"
+msgstr "[options-de-révision] sont documentés dans git-rev-list(1)"
 
 #: builtin/blame.c:2355
 msgid "Show blame entries as we find them, incrementally"
-msgstr "Montrer les entrée de blame au fur et Ã  mesure de leur découverte, incrémentalement"
+msgstr "Montrer les entrée de blâme au fur et Ã  mesure de leur découverte, de manière incrémentale"
 
 #: builtin/blame.c:2356
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
-msgstr "Montrer un SHA-1 blanc pour les commits de limite (défaut : désactivé)"
+msgstr "Montrer un SHA-1 blanc pour les commits de limite (Défaut : désactivé)"
 
 #: builtin/blame.c:2357
 msgid "Do not treat root commits as boundaries (Default: off)"
-msgstr "Ne pas traiter les commits racine comme des limites (défaut : désactivé)"
+msgstr "Ne pas traiter les commits racine comme des limites (Défaut : désactivé)"
 
 #: builtin/blame.c:2358
 msgid "Show work cost statistics"
@@ -2327,11 +2382,11 @@ msgstr "Montrer le score de sortie pour les entrées de blâme"
 
 #: builtin/blame.c:2360
 msgid "Show original filename (Default: auto)"
-msgstr "Montrer les noms de fichier originaux (défaut : auto)"
+msgstr "Montrer les noms de fichier originaux (Défaut : auto)"
 
 #: builtin/blame.c:2361
 msgid "Show original linenumber (Default: off)"
-msgstr "Montrer les numéros de lignes originaux (défaut : désactivé)"
+msgstr "Montrer les numéros de lignes originaux (Défaut : désactivé)"
 
 #: builtin/blame.c:2362
 msgid "Show in a format designed for machine consumption"
@@ -2343,23 +2398,23 @@ msgstr "Afficher en format porcelaine avec l'information de commit par ligne"
 
 #: builtin/blame.c:2364
 msgid "Use the same output mode as git-annotate (Default: off)"
-msgstr "Utiliser le même mode de sortie que git-annotate (défaut : désactivé)"
+msgstr "Utiliser le même mode de sortie que git-annotate (Défaut : désactivé)"
 
 #: builtin/blame.c:2365
 msgid "Show raw timestamp (Default: off)"
-msgstr "Afficher les horodatages bruts (défaut: désactivé)"
+msgstr "Afficher les horodatages bruts (Défaut: désactivé)"
 
 #: builtin/blame.c:2366
 msgid "Show long commit SHA1 (Default: off)"
-msgstr "Afficher les SHA1 longs (defaut : désactivé)"
+msgstr "Afficher les longs SHA1 de commits (Défaut : désactivé)"
 
 #: builtin/blame.c:2367
 msgid "Suppress author name and timestamp (Default: off)"
-msgstr "Supprimer le nom de l'auteur et l'horodatage (défaut : désactivé)"
+msgstr "Supprimer le nom de l'auteur et l'horodatage (Défaut : désactivé)"
 
 #: builtin/blame.c:2368
 msgid "Show author email instead of name (Default: off)"
-msgstr "Afficher l'e-mail de l'auteur au lieu du nom (défaut : désactivé)"
+msgstr "Afficher l'e-mail de l'auteur au lieu du nom (Défaut : désactivé)"
 
 #: builtin/blame.c:2369
 msgid "Ignore whitespace differences"
@@ -2387,7 +2442,7 @@ msgstr "Trouver les copies de ligne dans et entre les fichiers"
 
 #: builtin/blame.c:2374
 msgid "Find line movements within and across files"
-msgstr "Trouver les mouvements dans et entre les fichiers"
+msgstr "Trouver les mouvements de ligne dans et entre les fichiers"
 
 #: builtin/blame.c:2375
 msgid "n,m"
@@ -2411,23 +2466,25 @@ msgstr "git branch [options] [-r] (-d | -D) <nomdebranche>..."
 
 #: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
-msgstr "git branch [options] (-m | -M) [<anciennebranch>] <nouvellebranch>"
+msgstr "git branch [options] (-m | -M) [<anciennebranche>] <nouvellebranche>"
 
 #: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
 "         '%s', but not yet merged to HEAD."
-msgstr "suppression de la branche '%s' qui a Ã©té fusionnée dans\n"
-"         '%s', mais pas dans  HEAD."
+msgstr ""
+"suppression de la branche '%s' qui a Ã©té fusionnée dans\n"
+"         '%s', mais pas dans HEAD."
 
 #: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
 "         '%s', even though it is merged to HEAD."
-msgstr "branche '%s' non supprimée car elle n'a pas Ã©té fusionnée dans\n"
-"         '%s', mais dans HEAD."
+msgstr ""
+"branche '%s' non supprimée car elle n'a pas Ã©té fusionnée dans\n"
+"         '%s', même si elle est fusionnée dans HEAD."
 
 #: builtin/branch.c:168
 #, c-format
@@ -2439,12 +2496,13 @@ msgstr "Impossible de rechercher l'objet commit pour '%s'"
 msgid ""
 "The branch '%s' is not fully merged.\n"
 "If you are sure you want to delete it, run 'git branch -D %s'."
-msgstr "La branche '%s' n'est pas totalement fusionnée.\n"
-"Si vous Ãªtes sur que vous voulez la supprimer, lancez 'git branch -D %s'"
+msgstr ""
+"La branche '%s' n'est pas totalement fusionnée.\n"
+"Si vous Ãªtes sur que vous voulez la supprimer, lancez 'git branch -D %s'."
 
 #: builtin/branch.c:185
 msgid "Update of config-file failed"
-msgstr "Echec de la mise Ã  jour du fichier config"
+msgstr "Échec de la mise Ã  jour du fichier de config"
 
 #: builtin/branch.c:213
 msgid "cannot use -a with -d"
@@ -2457,7 +2515,7 @@ msgstr "Impossible de rechercher l'objet commit pour HEAD"
 #: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
-msgstr "Impossible d'effacer la branche '%s' qui est actuellement extraite"
+msgstr "Impossible de supprimer la branche '%s' sur laquelle vous Ãªtes."
 
 #: builtin/branch.c:240
 #, c-format
@@ -2482,12 +2540,12 @@ msgstr "Erreur lors de la suppression de la branche '%s'"
 #: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
-msgstr "Branche distante %s supprimée (précédemment %s)\n"
+msgstr "Branche distante %s supprimée (précédemment %s).\n"
 
 #: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
-msgstr "Branche %s supprimée (précédemment %s)\n"
+msgstr "Branche %s supprimée (précédemment %s).\n"
 
 #: builtin/branch.c:366
 #, c-format
@@ -2577,11 +2635,11 @@ msgstr "Renommage d'un branche mal nommée '%s'"
 #: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "La branche a Ã©té renommée '%s', mais HEAD n'est pas mis Ã  jour !"
+msgstr "La branche a Ã©té renommée en %s, mais HEAD n'est pas mise Ã  jour !"
 
 #: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
-msgstr "La branche est renommée, mais echec de la mise Ã  jour du fichier de config"
+msgstr "La branche est renommée, mais la mise Ã  jour du fichier de config a Ã©choué"
 
 #: builtin/branch.c:737
 #, c-format
@@ -2599,7 +2657,7 @@ msgstr "Options génériques"
 
 #: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
-msgstr "afficher le condensé et le sujet, doublé pour la branche amont"
+msgstr "afficher le hachage et le sujet, doublé pour la branche amont"
 
 #: builtin/branch.c:794
 msgid "suppress informational messages"
@@ -2619,7 +2677,7 @@ msgstr "utiliser la coloration dans la sortie"
 
 #: builtin/branch.c:802
 msgid "act on remote-tracking branches"
-msgstr "agir sur les branches de suivi distant"
+msgstr "agir sur les branches de suivi distantes"
 
 #: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
 #: builtin/branch.c:838 builtin/commit.c:1368 builtin/commit.c:1369
@@ -2685,11 +2743,11 @@ msgstr "afficher les branches en colonnes"
 
 #: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Echec de résolution de HEAD comme référence valide."
+msgstr "Échec de résolution de HEAD comme référence valide."
 
 #: builtin/branch.c:860 builtin/clone.c:619
 msgid "HEAD not found below refs/heads!"
-msgstr "HEAD non trouvé sous refs/heads !"
+msgstr "HEAD non trouvée sous refs/heads !"
 
 #: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
@@ -2697,11 +2755,11 @@ msgstr "--column et --verbose sont incompatibles"
 
 #: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
-msgstr "nom de branche exigé"
+msgstr "le nom de branche est requis"
 
 #: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
-msgstr "Impossible de décrire  HEAD détaché"
+msgstr "Impossible de décrire une HEAD détachée"
 
 #: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
@@ -2710,7 +2768,7 @@ msgstr "impossible d'éditer la description de plus d'une branche"
 #: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
-msgstr "Aucun commit sur la branche '%s'"
+msgstr "Aucun commit sur la branche '%s'."
 
 #: builtin/branch.c:919
 #, c-format
@@ -2734,12 +2792,12 @@ msgstr "impossible de spécifier une branche amont de HEAD par %s qui ne pointe
 #: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
 #, c-format
 msgid "no such branch '%s'"
-msgstr "branche inexistante '%s'"
+msgstr "pas de branche '%s'"
 
 #: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "branche inexistante '%s'"
+msgstr "la branche '%s' n'existe pas"
 
 #: builtin/branch.c:962
 msgid "too many branches to unset upstream"
@@ -2747,7 +2805,7 @@ msgstr "trop de branches pour désactiver un amont"
 
 #: builtin/branch.c:966
 msgid "could not unset upstream of HEAD when it does not point to any branch."
-msgstr "impossible de désactiver une branche amont de HEAD qu'elle ne pointe sur aucune branche."
+msgstr "impossible de désactiver une branche amont de HEAD quand elle ne pointe sur aucune branche."
 
 #: builtin/branch.c:972
 #, c-format
@@ -2792,15 +2850,15 @@ msgstr "    git branch -set-upstream-to %s\n"
 #: builtin/bundle.c:47
 #, c-format
 msgid "%s is okay\n"
-msgstr "%s convient\n"
+msgstr "%s est correct\n"
 
 #: builtin/bundle.c:56
 msgid "Need a repository to create a bundle."
-msgstr "La création d'un bundle requiert un dépôt."
+msgstr "La création d'un colis requiert un dépôt."
 
 #: builtin/bundle.c:60
 msgid "Need a repository to unbundle."
-msgstr "Le dépaquetage d'un bundle requiert un dépôt."
+msgstr "Le dépaquetage d'un colis requiert un dépôt."
 
 #: builtin/cat-file.c:176
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
@@ -2820,7 +2878,7 @@ msgstr "afficher le type de l'objet"
 
 #: builtin/cat-file.c:197
 msgid "show object size"
-msgstr "affichier la taille de l'objet"
+msgstr "afficher la taille de l'objet"
 
 #: builtin/cat-file.c:199
 msgid "exit with zero when there's no error"
@@ -2836,7 +2894,7 @@ msgstr "pour les objets blob, lancer textconv sur le contenu de l'objet"
 
 #: builtin/cat-file.c:204
 msgid "show info and content of objects fed from the standard input"
-msgstr "afficher l'information et le contenu des objets passé en entrée standard"
+msgstr "afficher l'information et le contenu des objets passés en entrée standard"
 
 #: builtin/cat-file.c:207
 msgid "show info about objects fed from the standard input"
@@ -2868,11 +2926,11 @@ msgstr "les chemins en entrée sont terminés par le caractère nul"
 
 #: builtin/check-ignore.c:18 builtin/checkout.c:1044 builtin/gc.c:177
 msgid "suppress progress reporting"
-msgstr "supprimer le rapport de progrès"
+msgstr "supprimer l'état d'avancement"
 
 #: builtin/check-ignore.c:146
 msgid "cannot specify pathnames with --stdin"
-msgstr "l'option --stdin et la spécification de chemin sont incompatibles"
+msgstr "impossible de spécifier les chemins avec --stdin"
 
 #: builtin/check-ignore.c:149
 msgid "-z only makes sense with --stdin"
@@ -2888,7 +2946,7 @@ msgstr "--quiet n'est valide qu'avec un seul chemin"
 
 #: builtin/check-ignore.c:157
 msgid "cannot have both --quiet and --verbose"
-msgstr "les options --quiet et --verbose sont incompatibles"
+msgstr "impossible d'avoir --quiet et --verbose"
 
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
@@ -2900,11 +2958,11 @@ msgstr "extraire tous les fichiers présents dans l'index"
 
 #: builtin/checkout-index.c:188
 msgid "force overwrite of existing files"
-msgstr "forcer l'écrasement des fichiers existant"
+msgstr "forcer l'écrasement des fichiers existants"
 
 #: builtin/checkout-index.c:190
 msgid "no warning for existing files and files not in index"
-msgstr "éliminer les alertes pour les fichiers existant et les fichiers absents de l'index"
+msgstr "pas d'avertissement pour les fichiers existants et les fichiers absents de l'index"
 
 #: builtin/checkout-index.c:192
 msgid "don't checkout new files"
@@ -2928,7 +2986,7 @@ msgstr "chaîne"
 
 #: builtin/checkout-index.c:204
 msgid "when creating files, prepend <string>"
-msgstr "lors de la création de fichiers, préfixer <chaîne>"
+msgstr "lors de la création de fichiers, préfixer par <chaîne>"
 
 #: builtin/checkout-index.c:207
 msgid "copy out the files from named stage"
@@ -2965,7 +3023,7 @@ msgstr "le chemin '%s' n'a pas les versions nécessaires"
 #: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
-msgstr "impossible de fusionner le chemin '%s'"
+msgstr "chemin '%s' : impossible de fusionner"
 
 #: builtin/checkout.c:213
 #, c-format
@@ -3008,7 +3066,7 @@ msgstr "Impossible de faire un reflog pour '%s'\n"
 
 #: builtin/checkout.c:634
 msgid "HEAD is now at"
-msgstr "HEAD est maintenant sur "
+msgstr "HEAD est maintenant sur"
 
 #: builtin/checkout.c:641
 #, c-format
@@ -3053,12 +3111,14 @@ msgid_plural ""
 "any of your branches:\n"
 "\n"
 "%s\n"
-msgstr[0] "Attention : vous laissez %d commit en retard, connectés Ã  aucune de vos\n"
-"branches :\n"
+msgstr[0] ""
+"Attention : vous laissez %d commit en retard, non connectés Ã \n"
+"une branche :\n"
 "\n"
 "%s\n"
-msgstr[1] "Attention : vous laissez %d commits en retard, connectés Ã  aucune de vos\n"
-"branches :\n"
+msgstr[1] ""
+"Attention : vous laissez %d commits en retard, non connectés Ã \n"
+"une branche :\n"
 "\n"
 "%s\n"
 
@@ -3070,7 +3130,9 @@ msgid ""
 "\n"
 " git branch new_branch_name %s\n"
 "\n"
-msgstr "Si vous souhaitez les garder en créant une nouvelle branches, c'est le bon moment avec :\n"
+msgstr ""
+"Si vous souhaitez les garder en créant une nouvelle branche, c'est le bon moment\n"
+"de le faire avec :\n"
 "\n"
 "git branche nouvelle_branche %s\n"
 "\n"
@@ -3081,7 +3143,7 @@ msgstr "erreur interne lors du parcours des révisions"
 
 #: builtin/checkout.c:766
 msgid "Previous HEAD position was"
-msgstr "Lo position précédente de HEAD Ã©tait "
+msgstr "La position précédente de HEAD Ã©tait"
 
 #: builtin/checkout.c:793 builtin/checkout.c:982
 msgid "You are on a branch yet to be born"
@@ -3106,13 +3168,13 @@ msgstr "impossible d'utiliser des chemins avec un basculement de branches"
 #: builtin/checkout.c:999 builtin/checkout.c:1003
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "impossible d'utiliser '%s' avec un basculement de branches "
+msgstr "'%s' ne peut pas Ãªtre utilisé avec un basculement de branches"
 
 #: builtin/checkout.c:1007 builtin/checkout.c:1010 builtin/checkout.c:1015
 #: builtin/checkout.c:1018
 #, c-format
 msgid "'%s' cannot be used with '%s'"
-msgstr "impossible d'utiliser '%s' avec '%s'"
+msgstr "'%s' ne peut pas Ãªtre utilisé avec '%s'"
 
 #: builtin/checkout.c:1023
 #, c-format
@@ -3126,11 +3188,11 @@ msgstr "branche"
 
 #: builtin/checkout.c:1046
 msgid "create and checkout a new branch"
-msgstr "créer et extrait une nouvelle branche"
+msgstr "créer et extraire une nouvelle branche"
 
 #: builtin/checkout.c:1048
 msgid "create/reset and checkout a branch"
-msgstr "créer/reinitialise et extrait une branche"
+msgstr "créer/réinitialiser et extraire une branche"
 
 #: builtin/checkout.c:1049
 msgid "create reflog for new branch"
@@ -3138,11 +3200,11 @@ msgstr "créer un refog pour une nouvelle branche"
 
 #: builtin/checkout.c:1050
 msgid "detach the HEAD at named commit"
-msgstr "détacher le HEAD au commit nommé"
+msgstr "détacher la HEAD Ã  la validation nommée"
 
 #: builtin/checkout.c:1051
 msgid "set upstream info for new branch"
-msgstr "paramètrer l'information de branche amont pour une nouvelle branche"
+msgstr "paramétrer l'information de branche amont pour une nouvelle branche"
 
 #: builtin/checkout.c:1053
 msgid "new branch"
@@ -3162,7 +3224,7 @@ msgstr "extraire leur version pour les fichiers non fusionnés"
 
 #: builtin/checkout.c:1058
 msgid "force checkout (throw away local modifications)"
-msgstr "forcer l'extraction (écraser les modifications locales)"
+msgstr "forcer l'extraction (laisser tomber les modifications locales)"
 
 #: builtin/checkout.c:1059
 msgid "perform a 3-way merge with the new branch"
@@ -3194,11 +3256,11 @@ msgstr "-b, -B et --orphan sont mutuellement exclusifs"
 
 #: builtin/checkout.c:1108
 msgid "--track needs a branch name"
-msgstr "un nom de branche est nécessaire pour --track"
+msgstr "--track requiert un nom de branche"
 
 #: builtin/checkout.c:1115
 msgid "Missing branch name; try -b"
-msgstr "Nom de branche manquant ; essayez avec -b"
+msgstr "Nom de branche manquant ; essayez -b"
 
 #: builtin/checkout.c:1150
 msgid "invalid path specification"
@@ -3209,24 +3271,26 @@ msgstr "spécification de chemin invalide"
 msgid ""
 "Cannot update paths and switch to branch '%s' at the same time.\n"
 "Did you intend to checkout '%s' which can not be resolved as commit?"
-msgstr "Impossible e mettre Ã  jour les chemins et de basculer sur la branche '%s' en même temps.\n"
+msgstr ""
+"Impossible de mettre Ã  jour les chemins et de basculer sur la branche '%s' en même temps.\n"
 "Souhaitiez-vous extraire '%s' qui ne peut Ãªtre résolu comme commit ?"
 
 #: builtin/checkout.c:1162
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
-msgstr "git checkout: --detach d'accepte pas un argument de chemin '%s'"
+msgstr "git checkout: --detach n'accepte pas un argument de chemin '%s'"
 
 #: builtin/checkout.c:1166
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
-msgstr "git checkout: --ours/--theirs, --force et --merge sont incompatible lors\n"
+msgstr ""
+"git checkout: --ours/--theirs, --force et --merge sont incompatibles lors\n"
 "de l'extraction de l'index."
 
 #: builtin/clean.c:20
 msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
-msgstr "git clean [-d] [-f] [-n] [-q] [-e <expression>] [-x | -X] [--] <chemins>..."
+msgstr "git clean [-d] [-f] [-n] [-q] [-e <motif>] [-x | -X] [--] <chemins>..."
 
 #: builtin/clean.c:24
 #, c-format
@@ -3241,7 +3305,7 @@ msgstr "Supprimerait %s\n"
 #: builtin/clean.c:26
 #, c-format
 msgid "Skipping repository %s\n"
-msgstr "Dépôt %s ignoré\n"
+msgstr "Ignore le dépôt %s\n"
 
 #: builtin/clean.c:27
 #, c-format
@@ -3251,7 +3315,7 @@ msgstr "Ignorerait le dépôt %s\n"
 #: builtin/clean.c:28
 #, c-format
 msgid "failed to remove %s"
-msgstr "echec de la suppression de %s"
+msgstr "échec de la suppression de %s"
 
 #: builtin/clean.c:160
 msgid "do not print names of files removed"
@@ -3263,16 +3327,16 @@ msgstr "forcer"
 
 #: builtin/clean.c:164
 msgid "remove whole directories"
-msgstr "supprimer des répertoires entiers"
+msgstr "supprimer les répertoires entiers"
 
 #: builtin/clean.c:165 builtin/describe.c:412 builtin/grep.c:717
 #: builtin/ls-files.c:487 builtin/name-rev.c:231 builtin/show-ref.c:182
 msgid "pattern"
-msgstr "expression"
+msgstr "motif"
 
 #: builtin/clean.c:166
 msgid "add <pattern> to ignore rules"
-msgstr "ajouter <expression> aux règles ignore"
+msgstr "ajouter <motif> aux règles ignore"
 
 #: builtin/clean.c:167
 msgid "remove ignored files, too"
@@ -3284,7 +3348,7 @@ msgstr "supprimer seulement les fichiers ignorés"
 
 #: builtin/clean.c:187
 msgid "-x and -X cannot be used together"
-msgstr "-x et -X sont mutuellement exclusifs"
+msgstr "-x et -X ne peuvent pas Ãªtre utilisés ensemble"
 
 #: builtin/clean.c:191
 msgid ""
@@ -3304,7 +3368,7 @@ msgstr "git clone [options] [--] <dépôt> [<répertoire>]"
 #: builtin/clone.c:65 builtin/fetch.c:82 builtin/merge.c:214
 #: builtin/push.c:436
 msgid "force progress reporting"
-msgstr "forcer l'affichage du progrès"
+msgstr "forcer l'état d'avancement"
 
 #: builtin/clone.c:67
 msgid "don't create a checkout"
@@ -3324,11 +3388,11 @@ msgstr "pour cloner depuis un dépôt local"
 
 #: builtin/clone.c:77
 msgid "don't use local hardlinks, always copy"
-msgstr "ne pas utiliser de liens durs locaux, mais toujours copier"
+msgstr "ne pas utiliser de liens durs locaux, toujours copier"
 
 #: builtin/clone.c:79
 msgid "setup as shared repository"
-msgstr "régler comme dépôt partager"
+msgstr "régler comme dépôt partagé"
 
 #: builtin/clone.c:81 builtin/clone.c:83
 msgid "initialize submodules in the clone"
@@ -3352,11 +3416,11 @@ msgstr "nom"
 
 #: builtin/clone.c:89
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "utiliser <nom> au lieu de 'origin' pour traquer la branche amont"
+msgstr "utiliser <nom> au lieu de 'origin' pour suivre la branche amont"
 
 #: builtin/clone.c:91
 msgid "checkout <branch> instead of the remote's HEAD"
-msgstr "extraire <branche> au lieu du HEAD du répertoire distant"
+msgstr "extraire <branche> au lieu de la HEAD du répertoire distant"
 
 #: builtin/clone.c:93
 msgid "path to git-upload-pack on the remote"
@@ -3372,7 +3436,7 @@ msgstr "créer un clone superficiel de cette profondeur"
 
 #: builtin/clone.c:97
 msgid "clone only one branch, HEAD or --branch"
-msgstr "clonez seulement une branche, HEAD ou --branch"
+msgstr "cloner seulement une branche, HEAD ou --branch"
 
 #: builtin/clone.c:98 builtin/init-db.c:494
 msgid "gitdir"
@@ -3393,37 +3457,37 @@ msgstr "régler la configuration dans le nouveau dépôt"
 #: builtin/clone.c:254
 #, c-format
 msgid "reference repository '%s' is not a local repository."
-msgstr "le dépôt de référence '%s' n'est pas un dépôt local"
+msgstr "le dépôt de référence '%s' n'est pas un dépôt local."
 
 #: builtin/clone.c:317
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr "echec de la création du répertoire '%s'"
+msgstr "échec de la création du répertoire '%s'"
 
 #: builtin/clone.c:319 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
-msgstr "echec du stat de '%s'"
+msgstr "échec du stat de '%s'"
 
 #: builtin/clone.c:321
 #, c-format
 msgid "%s exists and is not a directory"
-msgstr "%s existe  et n'est pas un répertoire"
+msgstr "%s existe et n'est pas un répertoire"
 
 #: builtin/clone.c:335
 #, c-format
 msgid "failed to stat %s\n"
-msgstr "echec du stat de %s\n"
+msgstr "échec du stat de %s\n"
 
 #: builtin/clone.c:357
 #, c-format
 msgid "failed to create link '%s'"
-msgstr "echec de la création du lien '%s'"
+msgstr "échec de la création du lien '%s'"
 
 #: builtin/clone.c:361
 #, c-format
 msgid "failed to copy file to '%s'"
-msgstr "echec de la copie vers '%s'"
+msgstr "échec de la copie vers '%s'"
 
 #: builtin/clone.c:384
 #, c-format
@@ -3450,15 +3514,15 @@ msgstr "le serveur distant n'a pas envoyé tous les objets nécessaires"
 
 #: builtin/clone.c:610
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
-msgstr "le HEAD distant réfère Ã  une référence non existante, impossible de l'extraire.\n"
+msgstr "la HEAD distante réfère Ã  une référence non existante, impossible de l'extraire.\n"
 
 #: builtin/clone.c:641
 msgid "unable to checkout working tree"
-msgstr "incapable d'extraire la copie de travail"
+msgstr "inpossible d'extraire la copie de travail"
 
 #: builtin/clone.c:749
 msgid "Too many arguments."
-msgstr "Trop d'arguments"
+msgstr "Trop d'arguments."
 
 #: builtin/clone.c:753
 msgid "You must specify a repository to clone."
@@ -3467,11 +3531,11 @@ msgstr "Vous devez spécifier un dépôt Ã  cloner."
 #: builtin/clone.c:764
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
-msgstr "les options --bare et --origin %s sont incompatibles"
+msgstr "les options --bare et --origin %s sont incompatibles."
 
 #: builtin/clone.c:767
 msgid "--bare and --separate-git-dir are incompatible."
-msgstr "les option --bare --separate-git-dir sont incompatibles."
+msgstr "--bare et --separate-git-dir sont incompatibles."
 
 #: builtin/clone.c:780
 #, c-format
@@ -3480,12 +3544,12 @@ msgstr "le dépôt '%s' n'existe pas"
 
 #: builtin/clone.c:785
 msgid "--depth is ignored in local clones; use file:// instead."
-msgstr "--depth est ignoré dans les clones locaux : utilisez plutôt file://"
+msgstr "--depth est ignoré dans les clones locaux : utilisez plutôt \"file://\"."
 
 #: builtin/clone.c:795
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
-msgstr "le chemin de destination '%s' existe déjà et n'est  pas un répertoire vide."
+msgstr "le chemin de destination '%s' existe déjà et n'est pas un répertoire vide."
 
 #: builtin/clone.c:805
 #, c-format
@@ -3500,7 +3564,7 @@ msgstr "impossible de créer les répertoires de premier niveau dans '%s'"
 #: builtin/clone.c:821
 #, c-format
 msgid "could not create work tree dir '%s'."
-msgstr "impossible de créer le répertoire de la copie de travail '%s'"
+msgstr "impossible de créer le répertoire de la copie de travail '%s'."
 
 #: builtin/clone.c:840
 #, c-format
@@ -3510,12 +3574,12 @@ msgstr "Clonage dans le dépôt nu '%s'\n"
 #: builtin/clone.c:842
 #, c-format
 msgid "Cloning into '%s'...\n"
-msgstr "Clonage dans '%s'\n"
+msgstr "Clonage dans '%s'...\n"
 
 #: builtin/clone.c:877
 #, c-format
 msgid "Don't know how to clone %s"
-msgstr "Je ne sais pas cloner '%s'"
+msgstr "Je ne sais pas cloner %s"
 
 #: builtin/clone.c:926
 #, c-format
@@ -3544,15 +3608,15 @@ msgstr "Largeur maximale"
 
 #: builtin/column.c:30
 msgid "Padding space on left border"
-msgstr "Tabulation sur le côté gauche"
+msgstr "Remplissage d'espace sur la bordure gauche"
 
 #: builtin/column.c:31
 msgid "Padding space on right border"
-msgstr "Tabulation sur le côté droit"
+msgstr "Remplissage d'espace sur le côté droit"
 
 #: builtin/column.c:32
 msgid "Padding space between columns"
-msgstr "Tabulation entre colonnes"
+msgstr "Remplissage d'espace entre les colonnes"
 
 #: builtin/column.c:51
 msgid "--command must be the first argument"
@@ -3560,11 +3624,11 @@ msgstr "--command doit Ãªtre le premier argument"
 
 #: builtin/commit.c:34
 msgid "git commit [options] [--] <pathspec>..."
-msgstr "git commit [options] [--] <spécification de chemin>..."
+msgstr "git commit [options] [--] <spécification-de-chemin>..."
 
 #: builtin/commit.c:39
 msgid "git status [options] [--] <pathspec>..."
-msgstr "git status [options] [--] <spécification de chemin>..."
+msgstr "git status [options] [--] <spécification-de-chemin>..."
 
 #: builtin/commit.c:44
 msgid ""
@@ -3578,12 +3642,13 @@ msgid ""
 "After doing this, you may fix the identity used for this commit with:\n"
 "\n"
 "    git commit --amend --reset-author\n"
-msgstr "Votre nom et votre adresse e-mail ont Ã©té configurés automatiquement en se fondant\n"
+msgstr ""
+"Votre nom et votre adresse e-mail ont Ã©té configurés automatiquement en se fondant\n"
 "sur votre nom d'utilisateur et votre nom d'ordinateur. Veuillez vérifier qu'ils sont corrects.\n"
 "Vous pouvez supprimer ce message en les paramétrant explicitement :\n"
 "\n"
 "    git config --global user.name \"Votre Nom\"\n"
-"    git config --global user.email \"vous@exemple.com\n"
+"    git config --global user.email vous@exemple.com\n"
 "\n"
 "Après ceci, vous pouvez corriger l'identité utilisée pour ce commit avec :\n"
 "\n"
@@ -3606,7 +3671,8 @@ msgid ""
 "    git commit --allow-empty\n"
 "\n"
 "Otherwise, please use 'git reset'\n"
-msgstr "Le picorage précédent est Ã  présent vide, vraisemblablement du Ã  une résolution de conflit.\n"
+msgstr ""
+"Le picorage précédent est Ã  présent vide, vraisemblablement dû Ã  une résolution de conflit.\n"
 "Si vous souhaitez tout de même le valider, utilisez :\n"
 "\n"
 "    git commit --allow-empty\n"
@@ -3615,7 +3681,7 @@ msgstr "Le picorage précédent est Ã  présent vide, vraisemblablement du Ã  un
 
 #: builtin/commit.c:260
 msgid "failed to unpack HEAD tree object"
-msgstr "echec du dépaquetage de l'objet arbre HEAD"
+msgstr "échec du dépaquetage de l'objet arbre HEAD"
 
 #: builtin/commit.c:302
 msgid "unable to create temporary index"
@@ -3623,7 +3689,7 @@ msgstr "impossible de créer l'index temporaire"
 
 #: builtin/commit.c:308
 msgid "interactive add failed"
-msgstr "echec de l'ajout interactif"
+msgstr "échec de l'ajout interactif"
 
 #: builtin/commit.c:341 builtin/commit.c:362 builtin/commit.c:412
 msgid "unable to write new_index file"
@@ -3657,7 +3723,7 @@ msgstr "paramètre --author mal formé"
 #: builtin/commit.c:562
 #, c-format
 msgid "Malformed ident string: '%s'"
-msgstr "chaîne ident mal formée : '%s'"
+msgstr "Chaîne ident mal formée : '%s'"
 
 #: builtin/commit.c:600 builtin/commit.c:633 builtin/commit.c:956
 #, c-format
@@ -3707,7 +3773,8 @@ msgid ""
 "If this is not correct, please remove the file\n"
 "\t%s\n"
 "and try again.\n"
-msgstr "\n"
+msgstr ""
+"\n"
 "Il semble que vous validiez une fusion.\n"
 "Si ce n'est pas le cas, veuillez supprimer le fichier\n"
 "\t%s\n"
@@ -3721,7 +3788,8 @@ msgid ""
 "If this is not correct, please remove the file\n"
 "\t%s\n"
 "and try again.\n"
-msgstr "\n"
+msgstr ""
+"\n"
 "Il semble que vous validiez un picorage.\n"
 "Si ce n'est pas le cas, veuillez supprimer le fichier\n"
 "\t%s\n"
@@ -3732,9 +3800,9 @@ msgstr "\n"
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
 "with '%c' will be ignored, and an empty message aborts the commit.\n"
-msgstr "Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
-"commençant par '%c' seront ignorées, et un message vide abandonne la\n"
-"validation.\n"
+msgstr ""
+"Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
+"commençant par '%c' seront ignorées, et un message vide abandonne la validation.\n"
 
 #: builtin/commit.c:742
 #, c-format
@@ -3742,19 +3810,20 @@ msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
 "with '%c' will be kept; you may remove them yourself if you want to.\n"
 "An empty message aborts the commit.\n"
-msgstr "Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
-"commençant par '%c' seront conservées ; vous pouvez les supprimer vous-\n"
-"même si vous le souhaitez. Un message vide abandonne la validation.\n"
+msgstr ""
+"Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
+"commençant par '%c' seront conservées ; vous pouvez les supprimer vous-même\n"
+"si vous le souhaitez. Un message vide abandonne la validation.\n"
 
 #: builtin/commit.c:755
 #, c-format
 msgid "%sAuthor:    %s"
-msgstr "%sAuteur : %s"
+msgstr "%sAuteur :     %s"
 
 #: builtin/commit.c:762
 #, c-format
 msgid "%sCommitter: %s"
-msgstr "%s Validateur: %s"
+msgstr "%sValidateur : %s"
 
 #: builtin/commit.c:782
 msgid "Cannot read index"
@@ -3781,7 +3850,7 @@ msgstr "Mode de fichier non suivi invalide '%s'"
 
 #: builtin/commit.c:976
 msgid "Using both --reset-author and --author does not make sense"
-msgstr "L'utilisation concurrente de --reset-author et --author n'a pas de sens"
+msgstr "L'utilisation simultanée de --reset-author et --author n'a pas de sens"
 
 #: builtin/commit.c:987
 msgid "You have nothing to amend."
@@ -3797,7 +3866,7 @@ msgstr "Vous Ãªtes en plein picorage -- impossible de corriger (amend)."
 
 #: builtin/commit.c:995
 msgid "Options --squash and --fixup cannot be used together"
-msgstr "Les options --squash et --fixup sont incompatibles"
+msgstr "Les options --squash et --fixup ne peuvent pas Ãªtre utilisées ensemble"
 
 #: builtin/commit.c:1005
 msgid "Only one of -c/-C/-F/--fixup can be used."
@@ -3821,7 +3890,7 @@ msgstr "Aucun chemin avec les options --include/--only n'a pas de sens."
 
 #: builtin/commit.c:1036
 msgid "Clever... amending the last one with dirty index."
-msgstr "Malin... correction du dernier commit avec un index sale."
+msgstr "Malin... correction du dernier avec un index sale."
 
 #: builtin/commit.c:1038
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
@@ -7376,150 +7445,152 @@ msgstr ""
 
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
-msgstr ""
+msgstr "git remote [-v | --verbose]"
 
 #: builtin/remote.c:12
 msgid ""
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--"
 "mirror=<fetch|push>] <name> <url>"
-msgstr ""
+msgstr "git remote add [-t <branche>] [-m <maîtresse>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <nom> <URL>"
 
 #: builtin/remote.c:13 builtin/remote.c:32
 msgid "git remote rename <old> <new>"
-msgstr ""
+msgstr "git remote rename <ancienne> <nouvelle>"
 
 #: builtin/remote.c:14 builtin/remote.c:37
 msgid "git remote remove <name>"
-msgstr ""
+msgstr "git remote remove <nom>"
 
 #: builtin/remote.c:15 builtin/remote.c:42
 msgid "git remote set-head <name> (-a | -d | <branch>)"
-msgstr ""
+msgstr "git remote set-head <nom> (-a | -d | <branche>)"
 
 #: builtin/remote.c:16
 msgid "git remote [-v | --verbose] show [-n] <name>"
-msgstr ""
+msgstr "git remote [-v | --verbose] show [-n] <nom>"
 
 #: builtin/remote.c:17
 msgid "git remote prune [-n | --dry-run] <name>"
-msgstr ""
+msgstr "git remote prune [-n | --dry-run] <nom>"
 
 #: builtin/remote.c:18
 msgid ""
 "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
-msgstr ""
+msgstr "git remote [-v | --verbose] update [-p | --prune] [(<groupe> | <distante>)...]"
 
 #: builtin/remote.c:19
 msgid "git remote set-branches [--add] <name> <branch>..."
-msgstr ""
+msgstr "git remote set-branches [--add] <nom> <branche>..."
 
 #: builtin/remote.c:20 builtin/remote.c:68
 msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
-msgstr ""
+msgstr "git remote set-url [--push] <nom> <nouvelle-URL> [<ancienne-URL>]"
 
 #: builtin/remote.c:21 builtin/remote.c:69
 msgid "git remote set-url --add <name> <newurl>"
-msgstr ""
+msgstr "git remote set-url --add <nom> <nouvelle-URL>"
 
 #: builtin/remote.c:22 builtin/remote.c:70
 msgid "git remote set-url --delete <name> <url>"
-msgstr ""
+msgstr "git remote set-url --delete <nom> <URL>"
 
 #: builtin/remote.c:27
 msgid "git remote add [<options>] <name> <url>"
-msgstr ""
+msgstr "git remote add [<options>] <nom> <URL>"
 
 #: builtin/remote.c:47
 msgid "git remote set-branches <name> <branch>..."
-msgstr ""
+msgstr "git remote set-branches <nom> <branche>..."
 
 #: builtin/remote.c:48
 msgid "git remote set-branches --add <name> <branch>..."
-msgstr ""
+msgstr "git remote set-branches --add <nom> <branche>..."
 
 #: builtin/remote.c:53
 msgid "git remote show [<options>] <name>"
-msgstr ""
+msgstr "git remote show [<options>] <nom>"
 
 #: builtin/remote.c:58
 msgid "git remote prune [<options>] <name>"
-msgstr ""
+msgstr "git remote prune [<options>] <nom>"
 
 #: builtin/remote.c:63
 msgid "git remote update [<options>] [<group> | <remote>]..."
-msgstr ""
+msgstr "git remote update [<options>] [<groupe> | <distante>]..."
 
 #: builtin/remote.c:98
 #, c-format
 msgid "Updating %s"
-msgstr ""
+msgstr "Mise Ã  jour de %s"
 
 #: builtin/remote.c:130
 msgid ""
 "--mirror is dangerous and deprecated; please\n"
 "\t use --mirror=fetch or --mirror=push instead"
 msgstr ""
+"--mirror est dangereux et obsolète ; merci\n"
+"\t d'utiliser --mirror=fetch ou --mirror=push Ã  la place"
 
 #: builtin/remote.c:147
 #, c-format
 msgid "unknown mirror argument: %s"
-msgstr ""
+msgstr "argument miroir inconnu : %s"
 
 #: builtin/remote.c:163
 msgid "fetch the remote branches"
-msgstr ""
+msgstr "rapatrier les branches distantes"
 
 #: builtin/remote.c:165
 msgid "import all tags and associated objects when fetching"
-msgstr ""
+msgstr "importer toutes les Ã©tiquettes et les objets associés lors du rapatriement"
 
 #: builtin/remote.c:168
 msgid "or do not fetch any tag at all (--no-tags)"
-msgstr ""
+msgstr "ou ne rapatrier aucune Ã©tiquette (--no-tags)"
 
 #: builtin/remote.c:170
 msgid "branch(es) to track"
-msgstr ""
+msgstr "branche(s) Ã  suivre"
 
 #: builtin/remote.c:171
 msgid "master branch"
-msgstr ""
+msgstr "branche maîtresse"
 
 #: builtin/remote.c:172
 msgid "push|fetch"
-msgstr ""
+msgstr "push|fetch"
 
 #: builtin/remote.c:173
 msgid "set up remote as a mirror to push to or fetch from"
-msgstr ""
+msgstr "paramétrer la distante comme miroir pour pousser ou pour rapatrier depuis"
 
 #: builtin/remote.c:185
 msgid "specifying a master branch makes no sense with --mirror"
-msgstr ""
+msgstr "spécifier une branche maîtresse n'a pas de sens avec --mirror"
 
 #: builtin/remote.c:187
 msgid "specifying branches to track makes sense only with fetch mirrors"
-msgstr ""
+msgstr "spécifier les branches Ã  suivre n'a de sens qu'avec des miroirs de rapatriement"
 
 #: builtin/remote.c:195 builtin/remote.c:646
 #, c-format
 msgid "remote %s already exists."
-msgstr ""
+msgstr "la distante %s existe déjà."
 
 #: builtin/remote.c:199 builtin/remote.c:650
 #, c-format
 msgid "'%s' is not a valid remote name"
-msgstr ""
+msgstr "'%s' n'est pas un nom valide de distante"
 
 #: builtin/remote.c:243
 #, c-format
 msgid "Could not setup master '%s'"
-msgstr ""
+msgstr "Impossible de paramétrer la maîtresse '%s'"
 
 #: builtin/remote.c:299
 #, c-format
 msgid "more than one %s"
-msgstr ""
+msgstr "plus d'un %s"
 
 #: builtin/remote.c:339
 #, c-format
@@ -7528,31 +7599,31 @@ msgstr ""
 
 #: builtin/remote.c:440 builtin/remote.c:448
 msgid "(matching)"
-msgstr ""
+msgstr "(correspond)"
 
 #: builtin/remote.c:452
 msgid "(delete)"
-msgstr ""
+msgstr "(supprimer)"
 
 #: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
 #, c-format
 msgid "Could not append '%s' to '%s'"
-msgstr ""
+msgstr "Impossible d'ajouter '%s' Ã  '%s'"
 
 #: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
 #, c-format
 msgid "No such remote: %s"
-msgstr ""
+msgstr "Distante inconnue : %s"
 
 #: builtin/remote.c:656
 #, c-format
 msgid "Could not rename config section '%s' to '%s'"
-msgstr ""
+msgstr "Impossible de renommer la section de configuration '%s' en '%s'"
 
 #: builtin/remote.c:662 builtin/remote.c:799
 #, c-format
 msgid "Could not remove config section '%s'"
-msgstr ""
+msgstr "Impossible de supprimer la section de configuration '%s'"
 
 #: builtin/remote.c:677
 #, c-format
@@ -7561,31 +7632,34 @@ msgid ""
 "\t%s\n"
 "\tPlease update the configuration manually if necessary."
 msgstr ""
+"Pas de mise Ã  jour du refspec de rapatriement qui n'est pas par défaut\n"
+"\t%s\n"
+"\tMerci de mettre Ã  jour la configuration si nécessaire."
 
 #: builtin/remote.c:683
 #, c-format
 msgid "Could not append '%s'"
-msgstr ""
+msgstr "Impossible d'ajouter '%s'"
 
 #: builtin/remote.c:694
 #, c-format
 msgid "Could not set '%s'"
-msgstr ""
+msgstr "Impossible de définir '%s'"
 
 #: builtin/remote.c:716
 #, c-format
 msgid "deleting '%s' failed"
-msgstr ""
+msgstr "échec de suppression de '%s'"
 
 #: builtin/remote.c:750
 #, c-format
 msgid "creating '%s' failed"
-msgstr ""
+msgstr "échec de création de '%s'"
 
 #: builtin/remote.c:764
 #, c-format
 msgid "Could not remove branch %s"
-msgstr ""
+msgstr "Impossible de supprimer la branche %s"
 
 #: builtin/remote.c:834
 msgid ""
@@ -7595,258 +7669,262 @@ msgid_plural ""
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
 msgstr[0] ""
+"Note : Une branche en dehors de refs/remotes/ n'a pas Ã©té supprimée ;\n"
+"pour la supprimer, utilisez :"
 msgstr[1] ""
+"Note : Plusieurs branches en dehors de refs/remotes/ n'ont pas Ã©té supprimées ;\n"
+"pour les supprimer, utilisez :"
 
 #: builtin/remote.c:943
 #, c-format
 msgid " new (next fetch will store in remotes/%s)"
-msgstr ""
+msgstr " nouveau (le prochain rapatriement (fetch) stockera dans remotes/%s)"
 
 #: builtin/remote.c:946
 msgid " tracked"
-msgstr ""
+msgstr " suivi"
 
 #: builtin/remote.c:948
 msgid " stale (use 'git remote prune' to remove)"
-msgstr ""
+msgstr " dépassé (utilisez 'git remote prune' pour supprimer)"
 
 #: builtin/remote.c:950
 msgid " ???"
-msgstr ""
+msgstr " ???"
 
 #: builtin/remote.c:991
 #, c-format
 msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
-msgstr ""
+msgstr "branch.%s.merge invalide ; ne peut pas rebaser sur plus d'une branche"
 
 #: builtin/remote.c:998
 #, c-format
 msgid "rebases onto remote %s"
-msgstr ""
+msgstr "rebase sur la distante %s"
 
 #: builtin/remote.c:1001
 #, c-format
 msgid " merges with remote %s"
-msgstr ""
+msgstr " fusionne avec la distante %s"
 
 #: builtin/remote.c:1002
 msgid "    and with remote"
-msgstr ""
+msgstr "    et avec la distante"
 
 #: builtin/remote.c:1004
 #, c-format
 msgid "merges with remote %s"
-msgstr ""
+msgstr "fusionne avec la distante %s"
 
 #: builtin/remote.c:1005
 msgid "   and with remote"
-msgstr ""
+msgstr "   et avec la distante"
 
 #: builtin/remote.c:1051
 msgid "create"
-msgstr ""
+msgstr "créer"
 
 #: builtin/remote.c:1054
 msgid "delete"
-msgstr ""
+msgstr "supprimer"
 
 #: builtin/remote.c:1058
 msgid "up to date"
-msgstr ""
+msgstr "à jour"
 
 #: builtin/remote.c:1061
 msgid "fast-forwardable"
-msgstr ""
+msgstr "peut Ãªtre mis Ã  jour en avance rapide"
 
 #: builtin/remote.c:1064
 msgid "local out of date"
-msgstr ""
+msgstr "le local n'est pas Ã  jour"
 
 #: builtin/remote.c:1071
 #, c-format
 msgid "    %-*s forces to %-*s (%s)"
-msgstr ""
+msgstr "    %-*s force vers %-*s (%s)"
 
 #: builtin/remote.c:1074
 #, c-format
 msgid "    %-*s pushes to %-*s (%s)"
-msgstr ""
+msgstr "    %-*s pousse vers %-*s (%s)"
 
 #: builtin/remote.c:1078
 #, c-format
 msgid "    %-*s forces to %s"
-msgstr ""
+msgstr "    %-*s force vers %s"
 
 #: builtin/remote.c:1081
 #, c-format
 msgid "    %-*s pushes to %s"
-msgstr ""
+msgstr "    %-*s pousse vers %s"
 
 #: builtin/remote.c:1091
 msgid "do not query remotes"
-msgstr ""
+msgstr "ne pas interroger les distantes"
 
 #: builtin/remote.c:1118
 #, c-format
 msgid "* remote %s"
-msgstr ""
+msgstr "* distante %s"
 
 #: builtin/remote.c:1119
 #, c-format
 msgid "  Fetch URL: %s"
-msgstr ""
+msgstr "  URL de rapatriement : %s"
 
 #: builtin/remote.c:1120 builtin/remote.c:1285
 msgid "(no URL)"
-msgstr ""
+msgstr "(pas d'URL)"
 
 #: builtin/remote.c:1129 builtin/remote.c:1131
 #, c-format
 msgid "  Push  URL: %s"
-msgstr ""
+msgstr "  URL push : %s"
 
 #: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
 #, c-format
 msgid "  HEAD branch: %s"
-msgstr ""
+msgstr "  Branche HEAD : %s"
 
 #: builtin/remote.c:1139
 #, c-format
 msgid ""
 "  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
-msgstr ""
+msgstr "  Branche HEAD (la HEAD distante est ambiguë, peut Ãªtre l'une des suivantes) :\n"
 
 #: builtin/remote.c:1151
 #, c-format
 msgid "  Remote branch:%s"
 msgid_plural "  Remote branches:%s"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "  Branche distante :%s"
+msgstr[1] "  Branches distantes :%s"
 
 #: builtin/remote.c:1154 builtin/remote.c:1181
 msgid " (status not queried)"
-msgstr ""
+msgstr " (statut non demandé)"
 
 #: builtin/remote.c:1163
 msgid "  Local branch configured for 'git pull':"
 msgid_plural "  Local branches configured for 'git pull':"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "  Branche locale configurée pour 'git pull' :"
+msgstr[1] "  Branches locales configurées pour 'git pull' :"
 
 #: builtin/remote.c:1171
 msgid "  Local refs will be mirrored by 'git push'"
-msgstr ""
+msgstr "  Les références locales seront reflétées par 'git push'"
 
 #: builtin/remote.c:1178
 #, c-format
 msgid "  Local ref configured for 'git push'%s:"
 msgid_plural "  Local refs configured for 'git push'%s:"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "  Référence locale configurée pour 'git push'%s :"
+msgstr[1] "  Références locales configurées pour 'git push'%s :"
 
 #: builtin/remote.c:1199
 msgid "set refs/remotes/<name>/HEAD according to remote"
-msgstr ""
+msgstr "définir refs/remotes/<nom>/HEAD selon la distante"
 
 #: builtin/remote.c:1201
 msgid "delete refs/remotes/<name>/HEAD"
-msgstr ""
+msgstr "supprimer refs/remotes/<nom>/HEAD"
 
 #: builtin/remote.c:1216
 msgid "Cannot determine remote HEAD"
-msgstr ""
+msgstr "Impossible de déterminer la HEAD distante"
 
 #: builtin/remote.c:1218
 msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
-msgstr ""
+msgstr "Il y a de multiples branches HEAD distantes. Merci d'en choisir une explicitement avec :"
 
 #: builtin/remote.c:1228
 #, c-format
 msgid "Could not delete %s"
-msgstr ""
+msgstr "Impossible de supprimer %s"
 
 #: builtin/remote.c:1236
 #, c-format
 msgid "Not a valid ref: %s"
-msgstr ""
+msgstr "Référence non valide : %s"
 
 #: builtin/remote.c:1238
 #, c-format
 msgid "Could not setup %s"
-msgstr ""
+msgstr "Impossible de paramétrer %s"
 
 #: builtin/remote.c:1274
 #, c-format
 msgid " %s will become dangling!"
-msgstr ""
+msgstr " %s deviendra en suspens !"
 
 #: builtin/remote.c:1275
 #, c-format
 msgid " %s has become dangling!"
-msgstr ""
+msgstr " %s est devenu en suspens !"
 
 #: builtin/remote.c:1281
 #, c-format
 msgid "Pruning %s"
-msgstr ""
+msgstr "Élagage de %s"
 
 #: builtin/remote.c:1282
 #, c-format
 msgid "URL: %s"
-msgstr ""
+msgstr "URL : %s"
 
 #: builtin/remote.c:1295
 #, c-format
 msgid " * [would prune] %s"
-msgstr ""
+msgstr " * [serait Ã©lagué] %s"
 
 #: builtin/remote.c:1298
 #, c-format
 msgid " * [pruned] %s"
-msgstr ""
+msgstr " * [élagué] %s"
 
 #: builtin/remote.c:1321
 msgid "prune remotes after fetching"
-msgstr ""
+msgstr "élaguer les distants après le rapatriement"
 
 #: builtin/remote.c:1387 builtin/remote.c:1461
 #, c-format
 msgid "No such remote '%s'"
-msgstr ""
+msgstr "Pas de serveur remote '%s'"
 
 #: builtin/remote.c:1407
 msgid "add branch"
-msgstr ""
+msgstr "ajouter une branche"
 
 #: builtin/remote.c:1414
 msgid "no remote specified"
-msgstr ""
+msgstr "pas de serveur distant spécifié"
 
 #: builtin/remote.c:1436
 msgid "manipulate push URLs"
-msgstr ""
+msgstr "manipuler les URLs push"
 
 #: builtin/remote.c:1438
 msgid "add URL"
-msgstr ""
+msgstr "ajouter une URL"
 
 #: builtin/remote.c:1440
 msgid "delete URLs"
-msgstr ""
+msgstr "supprimer des URLs"
 
 #: builtin/remote.c:1447
 msgid "--add --delete doesn't make sense"
-msgstr ""
+msgstr "--add --delete n'a aucun sens"
 
 #: builtin/remote.c:1487
 #, c-format
 msgid "Invalid old URL pattern: %s"
-msgstr ""
+msgstr "Motif d'URL ancien invalide : %s"
 
 #: builtin/remote.c:1495
 #, c-format
 msgid "No such URL found: %s"
-msgstr ""
+msgstr "Pas d'URL trouvée : %s"
 
 #: builtin/remote.c:1497
 msgid "Will not delete all non-push URLs"
@@ -7854,173 +7932,173 @@ msgstr ""
 
 #: builtin/remote.c:1569
 msgid "be verbose; must be placed before a subcommand"
-msgstr ""
+msgstr "être verbeux : doit Ãªtre placé avant une sous-commande"
 
 #: builtin/replace.c:17
 msgid "git replace [-f] <object> <replacement>"
-msgstr ""
+msgstr "git replace [-f] <objet> <remplacement>"
 
 #: builtin/replace.c:18
 msgid "git replace -d <object>..."
-msgstr ""
+msgstr "git replace -d <objet>..."
 
 #: builtin/replace.c:19
 msgid "git replace -l [<pattern>]"
-msgstr ""
+msgstr "git replace -l [<motif>]"
 
 #: builtin/replace.c:121
 msgid "list replace refs"
-msgstr ""
+msgstr "afficher les références de remplacement"
 
 #: builtin/replace.c:122
 msgid "delete replace refs"
-msgstr ""
+msgstr "supprimer les références de remplacement"
 
 #: builtin/replace.c:123
 msgid "replace the ref if it exists"
-msgstr ""
+msgstr "remplacer la référence si elle existe"
 
 #: builtin/rerere.c:11
 msgid "git rerere [clear | forget path... | status | remaining | diff | gc]"
-msgstr ""
+msgstr "git rerere [clear | forget chemin... | status | remaining | diff | gc]"
 
 #: builtin/rerere.c:56
 msgid "register clean resolutions in index"
-msgstr ""
+msgstr "enregistrer des résolutions propres dans l'index"
 
 #: builtin/reset.c:25
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
-msgstr ""
+msgstr "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
 
 #: builtin/reset.c:26
 msgid "git reset [-q] <tree-ish> [--] <paths>..."
-msgstr ""
+msgstr "git reset [-q] <arbre> [--] <chemins>..."
 
 #: builtin/reset.c:27
 msgid "git reset --patch [<tree-ish>] [--] [<paths>...]"
-msgstr ""
+msgstr "git reset --patch [<arbre>] [--] [<chemins>...]"
 
 #: builtin/reset.c:33
 msgid "mixed"
-msgstr ""
+msgstr "mixed"
 
 #: builtin/reset.c:33
 msgid "soft"
-msgstr ""
+msgstr "soft"
 
 #: builtin/reset.c:33
 msgid "hard"
-msgstr ""
+msgstr "hard"
 
 #: builtin/reset.c:33
 msgid "merge"
-msgstr ""
+msgstr "merge"
 
 #: builtin/reset.c:33
 msgid "keep"
-msgstr ""
+msgstr "keep"
 
 #: builtin/reset.c:73
 msgid "You do not have a valid HEAD."
-msgstr ""
+msgstr "Vous n'avez pas une HEAD valide."
 
 #: builtin/reset.c:75
 msgid "Failed to find tree of HEAD."
-msgstr ""
+msgstr "Impossible de trouver l'arbre pour HEAD."
 
 #: builtin/reset.c:81
 #, c-format
 msgid "Failed to find tree of %s."
-msgstr ""
+msgstr "Impossible de trouver l'arbre pour %s."
 
 #: builtin/reset.c:98
 #, c-format
 msgid "HEAD is now at %s"
-msgstr ""
+msgstr "HEAD est maintenant Ã  %s"
 
 #: builtin/reset.c:169
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
-msgstr ""
+msgstr "Impossible de faire un \"reset %s\" au milieu d'une fusion."
 
 #: builtin/reset.c:248
 msgid "be quiet, only report errors"
-msgstr ""
+msgstr "être silencieux, afficher seulement les erreurs"
 
 #: builtin/reset.c:250
 msgid "reset HEAD and index"
-msgstr ""
+msgstr "réinitialiser HEAD et l'index"
 
 #: builtin/reset.c:251
 msgid "reset only HEAD"
-msgstr ""
+msgstr "réinitialiser seulement HEAD"
 
 #: builtin/reset.c:253 builtin/reset.c:255
 msgid "reset HEAD, index and working tree"
-msgstr ""
+msgstr "réinitialiser HEAD, l'index et la copie de travail"
 
 #: builtin/reset.c:257
 msgid "reset HEAD but keep local changes"
-msgstr ""
+msgstr "réinitialiser HEAD mais garder les changements locaux"
 
 #: builtin/reset.c:275
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
-msgstr ""
+msgstr "Échec de résolution de '%s' comme une révision valide."
 
 #: builtin/reset.c:278 builtin/reset.c:286
 #, c-format
 msgid "Could not parse object '%s'."
-msgstr ""
+msgstr "Impossible d'analyser l'objet '%s'."
 
 #: builtin/reset.c:283
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
-msgstr ""
+msgstr "Échec de résolution de '%s' comme un arbre valide."
 
 #: builtin/reset.c:292
 msgid "--patch is incompatible with --{hard,mixed,soft}"
-msgstr ""
+msgstr "--patch est incompatible avec --{hard,mixed,soft}"
 
 #: builtin/reset.c:301
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
-msgstr ""
+msgstr "--mixed avec des chemins est obsolète ; utilisez 'git reset -- <paths>' Ã  la place."
 
 #: builtin/reset.c:303
 #, c-format
 msgid "Cannot do %s reset with paths."
-msgstr ""
+msgstr "Impossible de faire un \"%s reset\" avec des chemins."
 
 #: builtin/reset.c:313
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
-msgstr "RAZ de %s n'est pas permis dans un dépôt nu"
+msgstr "Le \"%s reset\" n'est pas permis dans un dépôt nu"
 
 #: builtin/reset.c:333
 #, c-format
 msgid "Could not reset index file to revision '%s'."
-msgstr ""
+msgstr "Impossible de réinitialiser le fichier d'index Ã  la révision '%s'."
 
 #: builtin/reset.c:339
 msgid "Unstaged changes after reset:"
-msgstr ""
+msgstr "Modifications non indexées après reset :"
 
 #: builtin/reset.c:344
 msgid "Could not write new index file."
-msgstr ""
+msgstr "Impossible d'écrire le nouveau fichier d'index."
 
 #: builtin/rev-parse.c:339
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
-msgstr ""
+msgstr "git rev-parse --parseopt [options] -- [<arguments>...]"
 
 #: builtin/rev-parse.c:344
 msgid "keep the `--` passed as an arg"
-msgstr ""
+msgstr "garder le `--` passé en argument"
 
 #: builtin/rev-parse.c:346
 msgid "stop parsing after the first non-option argument"
-msgstr ""
+msgstr "arrêt de l'analyse après le premier argument qui n'est pas une option"
 
 #: builtin/rev-parse.c:464
 msgid ""
@@ -8030,22 +8108,27 @@ msgid ""
 "\n"
 "Run \"git rev-parse --parseopt -h\" for more information on the first usage."
 msgstr ""
+"git rev-parse --parseopt [options] -- [<arguments>...]\n"
+"   ou : git rev-parse --sq-quote [<argument>...]\n"
+"   ou : git rev-parse [options] [<argument>...]\n"
+"\n"
+"Lancez \"git rev-parse --parseopt -h\" pour plus d'information sur l'utilisation principale."
 
 #: builtin/revert.c:22
 msgid "git revert [options] <commit-ish>..."
-msgstr ""
+msgstr "git revert [options] <commit>..."
 
 #: builtin/revert.c:23
 msgid "git revert <subcommand>"
-msgstr ""
+msgstr "git revert <sous-commande>"
 
 #: builtin/revert.c:28
 msgid "git cherry-pick [options] <commit-ish>..."
-msgstr ""
+msgstr "git cherry-pick [options] <commit>..."
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
-msgstr ""
+msgstr "git cherry-pick <sous-commande>"
 
 #: builtin/revert.c:70 builtin/revert.c:92
 #, c-format
@@ -8054,75 +8137,75 @@ msgstr "%s : %s ne peut pas Ãªtre utilisé avec %s"
 
 #: builtin/revert.c:103
 msgid "end revert or cherry-pick sequence"
-msgstr ""
+msgstr "mettre fin au retour ou picorage"
 
 #: builtin/revert.c:104
 msgid "resume revert or cherry-pick sequence"
-msgstr ""
+msgstr "reprendre le retour ou picorage"
 
 #: builtin/revert.c:105
 msgid "cancel revert or cherry-pick sequence"
-msgstr ""
+msgstr "annuler le retour ou picorage"
 
 #: builtin/revert.c:106
 msgid "don't automatically commit"
-msgstr ""
+msgstr "ne pas valider automatiquement"
 
 #: builtin/revert.c:107
 msgid "edit the commit message"
-msgstr ""
+msgstr "éditer le message de validation"
 
 #: builtin/revert.c:110
 msgid "parent number"
-msgstr ""
+msgstr "numéro de parent"
 
 #: builtin/revert.c:112
 msgid "merge strategy"
-msgstr ""
+msgstr "stratégie de fusion"
 
 #: builtin/revert.c:113
 msgid "option"
-msgstr ""
+msgstr "option"
 
 #: builtin/revert.c:114
 msgid "option for merge strategy"
-msgstr ""
+msgstr "option pour la stratégie de fusion"
 
 #: builtin/revert.c:125
 msgid "append commit name"
-msgstr ""
+msgstr "ajouter le nom de validation"
 
 #: builtin/revert.c:126
 msgid "allow fast-forward"
-msgstr ""
+msgstr "autoriser l'avance rapide"
 
 #: builtin/revert.c:127
 msgid "preserve initially empty commits"
-msgstr ""
+msgstr "préserver les validations vides initialement"
 
 #: builtin/revert.c:128
 msgid "allow commits with empty messages"
-msgstr ""
+msgstr "autoriser les validations avec des messages vides"
 
 #: builtin/revert.c:129
 msgid "keep redundant, empty commits"
-msgstr ""
+msgstr "garder les validations redondantes, vides"
 
 #: builtin/revert.c:133
 msgid "program error"
-msgstr ""
+msgstr "erreur du programme"
 
 #: builtin/revert.c:223
 msgid "revert failed"
-msgstr ""
+msgstr "revert a Ã©choué"
 
 #: builtin/revert.c:238
 msgid "cherry-pick failed"
-msgstr ""
+msgstr "le picorage a Ã©choué"
 
 #: builtin/rm.c:15
 msgid "git rm [options] [--] <file>..."
-msgstr ""
+msgstr "git rm [options] [--] <fichier>..."
 
 #: builtin/rm.c:64 builtin/rm.c:186
 #, c-format
@@ -8130,6 +8213,8 @@ msgid ""
 "submodule '%s' (or one of its nested submodules) uses a .git directory\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
+"le sous-module '%s' (ou un des sous-modules imbriqués) utilise un répertoire .git\n"
+"(utilisez 'rm -rf' si vous souhaitez vraiment le supprimer avec tout son historique)"
 
 #: builtin/rm.c:174
 #, c-format
@@ -8137,6 +8222,8 @@ msgid ""
 "'%s' has staged content different from both the file and the HEAD\n"
 "(use -f to force removal)"
 msgstr ""
+"'%s' a du contenu indexé différent du fichier et de HEAD\n"
+"(utilisez -f pour forcer la suppression)"
 
 #: builtin/rm.c:180
 #, c-format
@@ -8144,6 +8231,8 @@ msgid ""
 "'%s' has changes staged in the index\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
+"'%s' a des changements dans l'index\n"
+"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)"
 
 #: builtin/rm.c:191
 #, c-format
@@ -8151,65 +8240,67 @@ msgid ""
 "'%s' has local modifications\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
+"'%s' a des modifications locales\n"
+"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)"
 
 #: builtin/rm.c:207
 msgid "do not list removed files"
-msgstr ""
+msgstr "ne pas afficher les fichiers supprimés"
 
 #: builtin/rm.c:208
 msgid "only remove from the index"
-msgstr ""
+msgstr "supprimer seulement de l'index"
 
 #: builtin/rm.c:209
 msgid "override the up-to-date check"
-msgstr ""
+msgstr "outrepasser la vérification des fichiers Ã  jour"
 
 #: builtin/rm.c:210
 msgid "allow recursive removal"
-msgstr ""
+msgstr "autoriser la suppression récursive"
 
 #: builtin/rm.c:212
 msgid "exit with a zero status even if nothing matched"
-msgstr ""
+msgstr "sortir avec un statut zéro même si rien ne correspondait"
 
 #: builtin/rm.c:283
 #, c-format
 msgid "not removing '%s' recursively without -r"
-msgstr ""
+msgstr "pas de suppression récursive de '%s' sans -r"
 
 #: builtin/rm.c:322
 #, c-format
 msgid "git rm: unable to remove %s"
-msgstr ""
+msgstr "git rm : impossible de supprimer %s"
 
 #: builtin/shortlog.c:13
 msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
-msgstr ""
+msgstr "git shortlog [<options>] [<intervalle révisions>] [[--] [<chemin>...]]"
 
 #: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
-msgstr ""
+msgstr "Auteur manquant : %s"
 
 #: builtin/shortlog.c:227
 msgid "sort output according to the number of commits per author"
-msgstr ""
+msgstr "trier la sortie sur le nombre de validations par auteur"
 
 #: builtin/shortlog.c:229
 msgid "Suppress commit descriptions, only provides commit count"
-msgstr ""
+msgstr "Supprimer les descriptions de validation, fournit seulement le nombre de validations"
 
 #: builtin/shortlog.c:231
 msgid "Show the email address of each author"
-msgstr ""
+msgstr "Afficher l'adresse e-mail de chaque auteur"
 
 #: builtin/shortlog.c:232
 msgid "w[,i1[,i2]]"
-msgstr ""
+msgstr "w[,i1[,i2]]"
 
 #: builtin/shortlog.c:233
 msgid "Linewrap output"
-msgstr ""
+msgstr "Couper les lignes"
 
 #: builtin/show-branch.c:9
 msgid ""
@@ -8217,31 +8308,31 @@ msgid ""
 "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --"
 "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | "
 "<glob>)...]"
-msgstr ""
+msgstr "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<quand>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<révision> | <glob>)...]"
 
 #: builtin/show-branch.c:10
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
-msgstr ""
+msgstr "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<référence>]"
 
 #: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
-msgstr ""
+msgstr "afficher les branches de suivi distantes et les branches locales"
 
 #: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
-msgstr ""
+msgstr "afficher les branches de suivi distantes"
 
 #: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
-msgstr ""
+msgstr "couleur '*!+-' correspondant Ã  la branche"
 
 #: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
-msgstr ""
+msgstr "afficher <n> validations de plus après l'ancêtre commun"
 
 #: builtin/show-branch.c:658
 msgid "synonym to more=-1"
-msgstr ""
+msgstr "synonyme de more=-1"
 
 #: builtin/show-branch.c:659
 msgid "suppress naming strings"
@@ -8249,157 +8340,159 @@ msgstr ""
 
 #: builtin/show-branch.c:661
 msgid "include the current branch"
-msgstr ""
+msgstr "inclure la branche courante"
 
 #: builtin/show-branch.c:663
 msgid "name commits with their object names"
-msgstr ""
+msgstr "nommer les validations avec leurs noms d'objet"
 
 #: builtin/show-branch.c:665
 msgid "show possible merge bases"
-msgstr ""
+msgstr "afficher les bases possibles de fusion"
 
 #: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
-msgstr ""
+msgstr "afficher les références non accessibles depuis toute autre référence"
 
 #: builtin/show-branch.c:669
 msgid "show commits in topological order"
-msgstr ""
+msgstr "afficher les validations dans l'ordre topologique"
 
 #: builtin/show-branch.c:671
 msgid "show only commits not on the first branch"
-msgstr ""
+msgstr "afficher seulement les validations qui ne sont pas sur la première branche"
 
 #: builtin/show-branch.c:673
 msgid "show merges reachable from only one tip"
-msgstr ""
+msgstr "afficher les fusions accessibles depuis une seule pointe"
 
 #: builtin/show-branch.c:675
 msgid "show commits where no parent comes before its children"
-msgstr ""
+msgstr "afficher les validations où aucun parent ne vient avant ses enfants"
 
 #: builtin/show-branch.c:677
 msgid "<n>[,<base>]"
-msgstr ""
+msgstr "<n>[,<base>]"
 
 #: builtin/show-branch.c:678
 msgid "show <n> most recent ref-log entries starting at base"
-msgstr ""
+msgstr "afficher les <n> plus récentes entrées de ref-log en commençant Ã  la base"
 
 #: builtin/show-ref.c:10
 msgid ""
 "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--"
 "hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] "
-msgstr ""
+msgstr "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [motif*] "
 
 #: builtin/show-ref.c:11
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
-msgstr ""
+msgstr "git show-ref --exclude-existing[=motif] < liste-références"
 
 #: builtin/show-ref.c:165
 msgid "only show tags (can be combined with heads)"
-msgstr ""
+msgstr "afficher seulement les Ã©tiquettes (peut Ãªtre combiné avec des têtes)"
 
 #: builtin/show-ref.c:166
 msgid "only show heads (can be combined with tags)"
-msgstr ""
+msgstr "afficher seulement les têtes (peut Ãªtre combiné avec des Ã©tiquettes)"
 
 #: builtin/show-ref.c:167
 msgid "stricter reference checking, requires exact ref path"
-msgstr ""
+msgstr "vérification de référence plus stricte, requiert un chemin de référence exact"
 
 #: builtin/show-ref.c:170 builtin/show-ref.c:172
 msgid "show the HEAD reference"
-msgstr ""
+msgstr "afficher la référence HEAD"
 
 #: builtin/show-ref.c:174
 msgid "dereference tags into object IDs"
-msgstr ""
+msgstr "déréférencer les Ã©tiquettes en IDs d'objet"
 
 #: builtin/show-ref.c:176
 msgid "only show SHA1 hash using <n> digits"
-msgstr ""
+msgstr "afficher seulement le hachage SHA1 en utilisant <n> chiffres"
 
 #: builtin/show-ref.c:180
 msgid "do not print results to stdout (useful with --verify)"
-msgstr ""
+msgstr "ne pas afficher les résultats sur la sortie standard (pratique avec --verify)"
 
 #: builtin/show-ref.c:182
 msgid "show refs from stdin that aren't in local repository"
-msgstr ""
+msgstr "afficher les références de l'entrée standard qui ne sont pas dans le dépôt local"
 
 #: builtin/symbolic-ref.c:7
 msgid "git symbolic-ref [options] name [ref]"
-msgstr ""
+msgstr "git symbolic-ref [options] nom [référence]"
 
 #: builtin/symbolic-ref.c:8
 msgid "git symbolic-ref -d [-q] name"
-msgstr ""
+msgstr "git symbolic-ref -d [-q] nom"
 
 #: builtin/symbolic-ref.c:40
 msgid "suppress error message for non-symbolic (detached) refs"
-msgstr ""
+msgstr "supprimer le message d'erreur pour une référence non symbolique (détachée)"
 
 #: builtin/symbolic-ref.c:41
 msgid "delete symbolic ref"
-msgstr ""
+msgstr "supprimer la référence symbolique"
 
 #: builtin/symbolic-ref.c:42
 msgid "shorten ref output"
-msgstr ""
+msgstr "raccourcir l'affichage de la référence"
 
 #: builtin/symbolic-ref.c:43 builtin/update-ref.c:18
 msgid "reason"
-msgstr ""
+msgstr "raison"
 
 #: builtin/symbolic-ref.c:43 builtin/update-ref.c:18
 msgid "reason of the update"
-msgstr ""
+msgstr "raison de la mise Ã  jour"
 
 #: builtin/tag.c:22
 msgid ""
 "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"
-msgstr ""
+msgstr "git tag [-a|-s|-u <id-clé>] [-f] [-m <msg>|-F <file>] <nométiquette> [<head>]"
 
 #: builtin/tag.c:23
 msgid "git tag -d <tagname>..."
-msgstr ""
+msgstr "git tag -d <nométiquette>..."
 
 #: builtin/tag.c:24
 msgid ""
 "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] \n"
 "\t\t[<pattern>...]"
 msgstr ""
+"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <objet>] \n"
+"\t\t[<motif>...]"
 
 #: builtin/tag.c:26
 msgid "git tag -v <tagname>..."
-msgstr ""
+msgstr "git tag -v <nométiquette>..."
 
 #: builtin/tag.c:60
 #, c-format
 msgid "malformed object at '%s'"
-msgstr ""
+msgstr "objet malformé Ã  '%s'"
 
 #: builtin/tag.c:207
 #, c-format
 msgid "tag name too long: %.*s..."
-msgstr ""
+msgstr "nom d'étiquette trop long : %.*s..."
 
 #: builtin/tag.c:212
 #, c-format
 msgid "tag '%s' not found."
-msgstr ""
+msgstr "étiquette '%s' non trouvée."
 
 #: builtin/tag.c:227
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
-msgstr ""
+msgstr "Étiquette '%s' supprimée (elle Ã©tait %s)\n"
 
 #: builtin/tag.c:239
 #, c-format
 msgid "could not verify the tag '%s'"
-msgstr ""
+msgstr "impossible de vérifier l'étiquette '%s'"
 
 #: builtin/tag.c:249
 #, c-format
@@ -8408,6 +8501,9 @@ msgid ""
 "Write a tag message\n"
 "Lines starting with '%c' will be ignored.\n"
 msgstr ""
+"\n"
+"Écrire un message pour l'étiquette\n"
+"Les lignes commençant par '%c' seront ignorées.\n"
 
 #: builtin/tag.c:253
 #, c-format
@@ -8417,130 +8513,133 @@ msgid ""
 "Lines starting with '%c' will be kept; you may remove them yourself if you "
 "want to.\n"
 msgstr ""
+"\n"
+"Écrire un message pour l'étiquette\n"
+"Les lignes commençant par '%c' seront gardées ; vous pouvez les retirer vous-même si vous le souhaitez.\n"
 
 #: builtin/tag.c:292
 msgid "unable to sign the tag"
-msgstr ""
+msgstr "impossible de signer l'étiquette"
 
 #: builtin/tag.c:294
 msgid "unable to write tag file"
-msgstr ""
+msgstr "impossible d'écrire le fichier d'étiquettes"
 
 #: builtin/tag.c:319
 msgid "bad object type."
-msgstr ""
+msgstr "mauvais type d'objet."
 
 #: builtin/tag.c:332
 msgid "tag header too big."
-msgstr ""
+msgstr "en-tête d'étiquette trop gros."
 
 #: builtin/tag.c:368
 msgid "no tag message?"
-msgstr ""
+msgstr "pas de message pour l'étiquette ?"
 
 #: builtin/tag.c:374
 #, c-format
 msgid "The tag message has been left in %s\n"
-msgstr ""
+msgstr "Le message pour l'étiquette a Ã©té laissé dans %s\n"
 
 #: builtin/tag.c:423
 msgid "switch 'points-at' requires an object"
-msgstr ""
+msgstr "le commutateur 'points-at' requiert un objet"
 
 #: builtin/tag.c:425
 #, c-format
 msgid "malformed object name '%s'"
-msgstr ""
+msgstr "nom d'objet malformé '%s'"
 
 #: builtin/tag.c:445
 msgid "list tag names"
-msgstr ""
+msgstr "afficher les noms des Ã©tiquettes"
 
 #: builtin/tag.c:447
 msgid "print <n> lines of each tag message"
-msgstr ""
+msgstr "affiche <n> lignes de chaque message d'étiquette"
 
 #: builtin/tag.c:449
 msgid "delete tags"
-msgstr ""
+msgstr "supprimer des Ã©tiquettes"
 
 #: builtin/tag.c:450
 msgid "verify tags"
-msgstr ""
+msgstr "vérifier des Ã©tiquettes"
 
 #: builtin/tag.c:452
 msgid "Tag creation options"
-msgstr ""
+msgstr "Options de création de l'étiquette"
 
 #: builtin/tag.c:454
 msgid "annotated tag, needs a message"
-msgstr ""
+msgstr "étiquette annotée, nécessite un message"
 
 #: builtin/tag.c:456
 msgid "tag message"
-msgstr ""
+msgstr "message pour l'étiquette"
 
 #: builtin/tag.c:458
 msgid "annotated and GPG-signed tag"
-msgstr ""
+msgstr "étiquette annotée et signée avec GPG"
 
 #: builtin/tag.c:462
 msgid "use another key to sign the tag"
-msgstr ""
+msgstr "utiliser une autre clé pour signer l'étiquette"
 
 #: builtin/tag.c:463
 msgid "replace the tag if exists"
-msgstr ""
+msgstr "remplacer l'étiquette si elle existe"
 
 #: builtin/tag.c:464
 msgid "show tag list in columns"
-msgstr ""
+msgstr "afficher la liste des Ã©tiquettes sous forme de colonnes"
 
 #: builtin/tag.c:466
 msgid "Tag listing options"
-msgstr ""
+msgstr "Options d'affichage des Ã©tiquettes"
 
 #: builtin/tag.c:469
 msgid "print only tags that contain the commit"
-msgstr ""
+msgstr "afficher seulement les Ã©tiquettes qui contiennent la validation"
 
 #: builtin/tag.c:475
 msgid "print only tags of the object"
-msgstr ""
+msgstr "afficher seulement les Ã©tiquettes de l'objet"
 
 #: builtin/tag.c:504
 msgid "--column and -n are incompatible"
-msgstr ""
+msgstr "--column et -n sont incompatibles"
 
 #: builtin/tag.c:521
 msgid "-n option is only allowed with -l."
-msgstr ""
+msgstr "l'option -n est autorisée seulement avec -l."
 
 #: builtin/tag.c:523
 msgid "--contains option is only allowed with -l."
-msgstr ""
+msgstr "l'option --contains est autorisée seulement avec -l."
 
 #: builtin/tag.c:525
 msgid "--points-at option is only allowed with -l."
-msgstr ""
+msgstr "l'option --points-at est autorisée seulement avec -l."
 
 #: builtin/tag.c:533
 msgid "only one -F or -m option is allowed."
-msgstr ""
+msgstr "une seule option -F ou -m est autorisée."
 
 #: builtin/tag.c:553
 msgid "too many params"
-msgstr ""
+msgstr "trop de paramètres"
 
 #: builtin/tag.c:559
 #, c-format
 msgid "'%s' is not a valid tag name."
-msgstr ""
+msgstr "'%s' n'est pas un nom d'étiquette valide."
 
 #: builtin/tag.c:564
 #, c-format
 msgid "tag '%s' already exists"
-msgstr ""
+msgstr "l'étiquette '%s' existe déjà"
 
 #: builtin/tag.c:582
 #, c-format
@@ -8555,63 +8654,63 @@ msgstr "%s : impossible de mettre Ã  jour la référence"
 #: builtin/tag.c:586
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
-msgstr ""
+msgstr "Étiquette '%s' mise Ã  jour (elle Ã©tait %s)\n"
 
 #: builtin/update-index.c:401
 msgid "git update-index [options] [--] [<file>...]"
-msgstr ""
+msgstr "git update-index [options] [--] [<fichier>...]"
 
 #: builtin/update-index.c:718
 msgid "continue refresh even when index needs update"
-msgstr ""
+msgstr "continuer de rafraîchir même si l'index a besoin d'une mise Ã  jour"
 
 #: builtin/update-index.c:721
 msgid "refresh: ignore submodules"
-msgstr ""
+msgstr "rafraîchir : ignorer les sous-modules"
 
 #: builtin/update-index.c:724
 msgid "do not ignore new files"
-msgstr ""
+msgstr "ne pas ignorer les nouveaux fichiers"
 
 #: builtin/update-index.c:726
 msgid "let files replace directories and vice-versa"
-msgstr ""
+msgstr "laisser les fichiers remplacer des répertoires et vice-versa"
 
 #: builtin/update-index.c:728
 msgid "notice files missing from worktree"
-msgstr ""
+msgstr "aviser des fichiers manquants dans la copie de travail"
 
 #: builtin/update-index.c:730
 msgid "refresh even if index contains unmerged entries"
-msgstr ""
+msgstr "rafraîchir même si l'index contient des entrées non fusionnées"
 
 #: builtin/update-index.c:733
 msgid "refresh stat information"
-msgstr ""
+msgstr "rafraîchir l'information de stat"
 
 #: builtin/update-index.c:737
 msgid "like --refresh, but ignore assume-unchanged setting"
-msgstr ""
+msgstr "comme --refresh, mais en ignorant l'option assume-unchanged"
 
 #: builtin/update-index.c:741
 msgid "<mode> <object> <path>"
-msgstr ""
+msgstr "<mode> <objet> <chemin>"
 
 #: builtin/update-index.c:742
 msgid "add the specified entry to the index"
-msgstr ""
+msgstr "ajouter l'entrée spécifiée dans l'index"
 
 #: builtin/update-index.c:746
 msgid "(+/-)x"
-msgstr ""
+msgstr "(+/-)x"
 
 #: builtin/update-index.c:747
 msgid "override the executable bit of the listed files"
-msgstr ""
+msgstr "outrepasser le bit exécutable pour les fichiers listés"
 
 #: builtin/update-index.c:751
 msgid "mark files as \"not changing\""
-msgstr ""
+msgstr "marquer les fichiers comme \"non changeants\""
 
 #: builtin/update-index.c:754
 msgid "clear assumed-unchanged bit"
@@ -8619,7 +8718,7 @@ msgstr ""
 
 #: builtin/update-index.c:757
 msgid "mark files as \"index-only\""
-msgstr ""
+msgstr "marquer les fichiers comme \"index seulement\""
 
 #: builtin/update-index.c:760
 msgid "clear skip-worktree bit"
@@ -8627,59 +8726,59 @@ msgstr ""
 
 #: builtin/update-index.c:763
 msgid "add to index only; do not add content to object database"
-msgstr ""
+msgstr "ajouter seulement Ã  l'index ; ne pas ajouter le contenu dans la base de données des objets"
 
 #: builtin/update-index.c:765
 msgid "remove named paths even if present in worktree"
-msgstr ""
+msgstr "supprimer les chemins nommés même s'ils sont présents dans la copie de travail"
 
 #: builtin/update-index.c:767
 msgid "with --stdin: input lines are terminated by null bytes"
-msgstr ""
+msgstr "avec --stdin : les lignes en entrée sont terminées par des octets nuls"
 
 #: builtin/update-index.c:769
 msgid "read list of paths to be updated from standard input"
-msgstr ""
+msgstr "lire la liste des chemins Ã  mettre Ã  jour depuis l'entrée standard"
 
 #: builtin/update-index.c:773
 msgid "add entries from standard input to the index"
-msgstr ""
+msgstr "ajouter les entrées depuis l'entrée standard Ã  l'index"
 
 #: builtin/update-index.c:777
 msgid "repopulate stages #2 and #3 for the listed paths"
-msgstr ""
+msgstr "repeupler les Ã©tapes n°2 et n°3 pour les chemins listés"
 
 #: builtin/update-index.c:781
 msgid "only update entries that differ from HEAD"
-msgstr ""
+msgstr "mettre Ã  jour seulement les entrées qui diffèrent de HEAD"
 
 #: builtin/update-index.c:785
 msgid "ignore files missing from worktree"
-msgstr ""
+msgstr "ignorer les fichiers manquants dans la copie de travail"
 
 #: builtin/update-index.c:788
 msgid "report actions to standard output"
-msgstr ""
+msgstr "afficher les actions sur la sortie standard"
 
 #: builtin/update-index.c:790
 msgid "(for porcelains) forget saved unresolved conflicts"
-msgstr ""
+msgstr "(pour porcelaines) oublier les conflits sauvés et non résolus"
 
 #: builtin/update-index.c:794
 msgid "write index in this format"
-msgstr ""
+msgstr "écrire l'index dans ce format"
 
 #: builtin/update-ref.c:7
 msgid "git update-ref [options] -d <refname> [<oldval>]"
-msgstr ""
+msgstr "git update-ref [options] -d <nomréférence> [<anciennevaleur>]"
 
 #: builtin/update-ref.c:8
 msgid "git update-ref [options]    <refname> <newval> [<oldval>]"
-msgstr ""
+msgstr "git update-ref [options]    <nomréférence> <nouvellevaleur> [<anciennevaleur>]"
 
 #: builtin/update-ref.c:19
 msgid "delete the reference"
-msgstr ""
+msgstr "supprimer la référence"
 
 #: builtin/update-ref.c:21
 msgid "update <refname> not the one it points to"
@@ -8687,47 +8786,47 @@ msgstr ""
 
 #: builtin/update-server-info.c:6
 msgid "git update-server-info [--force]"
-msgstr ""
+msgstr "git update-server-info [--force]"
 
 #: builtin/update-server-info.c:14
 msgid "update the info files from scratch"
-msgstr ""
+msgstr "mettre Ã  jour les fichiers d'information Ã  partir de zéro"
 
 #: builtin/verify-pack.c:56
 msgid "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."
-msgstr ""
+msgstr "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."
 
 #: builtin/verify-pack.c:66
 msgid "verbose"
-msgstr ""
+msgstr "verbeux"
 
 #: builtin/verify-pack.c:68
 msgid "show statistics only"
-msgstr ""
+msgstr "afficher seulement les statistiques"
 
 #: builtin/verify-tag.c:17
 msgid "git verify-tag [-v|--verbose] <tag>..."
-msgstr ""
+msgstr "git verify-tag [-v|--verbose] <étiquette>..."
 
 #: builtin/verify-tag.c:73
 msgid "print tag contents"
-msgstr ""
+msgstr "afficher le contenu de l'étiquette"
 
 #: builtin/write-tree.c:13
 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
-msgstr ""
+msgstr "git write-tree [--missing-ok] [--prefix=<préfixe>/]"
 
 #: builtin/write-tree.c:26
 msgid "<prefix>/"
-msgstr ""
+msgstr "<préfixe>/"
 
 #: builtin/write-tree.c:27
 msgid "write tree object for a subdirectory <prefix>"
-msgstr ""
+msgstr "écrire l'objet arbre pour un sous-répertoire <préfixe>"
 
 #: builtin/write-tree.c:30
 msgid "only useful for debugging"
-msgstr ""
+msgstr "seulement utile pour le débogage"
 
 #: git.c:16
 msgid ""
@@ -8735,116 +8834,121 @@ msgid ""
 "concept guides. See 'git help <command>' or 'git help <concept>'\n"
 "to read about a specific subcommand or concept."
 msgstr ""
+"'git help -a' et 'git help -g' listent les sous-commandes disponibles et\n"
+"quelques concepts. Voir 'git help <command>' ou 'git help <concept>'\n"
+"pour en lire plus Ã  propos d'une commande spécifique ou d'un concept."
 
 #: parse-options.h:156
 msgid "no-op (backward compatibility)"
-msgstr ""
+msgstr "sans action (rétrocompatibilité)"
 
 #: parse-options.h:232
 msgid "be more verbose"
-msgstr ""
+msgstr "être plus verbeux"
 
 #: parse-options.h:234
 msgid "be more quiet"
-msgstr ""
+msgstr "être plus silencieux"
 
 #: parse-options.h:240
 msgid "use <n> digits to display SHA-1s"
-msgstr ""
+msgstr "utiliser <n> chiffres pour afficher les SHA-1s"
 
 #: common-cmds.h:8
 msgid "Add file contents to the index"
-msgstr ""
+msgstr "Ajouter le contenu du fichier dans l'index"
 
 #: common-cmds.h:9
 msgid "Find by binary search the change that introduced a bug"
-msgstr ""
+msgstr "Rechercher de manière binaire le changement qui a introduit un bogue"
 
 #: common-cmds.h:10
 msgid "List, create, or delete branches"
-msgstr ""
+msgstr "Lister, créer ou supprimer des branches"
 
 #: common-cmds.h:11
 msgid "Checkout a branch or paths to the working tree"
-msgstr ""
+msgstr "Extraire une branche ou des chemins dans la copie de travail"
 
 #: common-cmds.h:12
 msgid "Clone a repository into a new directory"
-msgstr ""
+msgstr "Cloner un dépôt dans un nouveau répertoire"
 
 #: common-cmds.h:13
 msgid "Record changes to the repository"
-msgstr ""
+msgstr "Enregistrer les changements dans le dépôt"
 
 #: common-cmds.h:14
 msgid "Show changes between commits, commit and working tree, etc"
-msgstr ""
+msgstr "Afficher les changements entre les validations, entre validation et copie de travail, etc"
 
 #: common-cmds.h:15
 msgid "Download objects and refs from another repository"
-msgstr ""
+msgstr "Télécharger les objets et références depuis un autre dépôt"
 
 #: common-cmds.h:16
 msgid "Print lines matching a pattern"
-msgstr ""
+msgstr "Afficher les lignes correspondant Ã  un motif"
 
 #: common-cmds.h:17
 msgid "Create an empty Git repository or reinitialize an existing one"
-msgstr ""
+msgstr "Créer un dépôt Git vide ou réinitialiser un existant"
 
 #: common-cmds.h:18
 msgid "Show commit logs"
-msgstr ""
+msgstr "Afficher l'historique des validations"
 
 #: common-cmds.h:19
 msgid "Join two or more development histories together"
-msgstr ""
+msgstr "Joindre deux ou plusieurs historiques de développement ensemble"
 
 #: common-cmds.h:20
 msgid "Move or rename a file, a directory, or a symlink"
-msgstr ""
+msgstr "Déplacer ou renommer un fichier, un répertoire, ou un lien symbolique"
 
 #: common-cmds.h:21
 msgid "Fetch from and merge with another repository or a local branch"
-msgstr ""
+msgstr "Rapatrier et fusionner avec un autre dépôt ou une branche locale"
 
 #: common-cmds.h:22
 msgid "Update remote refs along with associated objects"
-msgstr ""
+msgstr "Mettre Ã  jour les références distantes ainsi que les objets associés"
 
 #: common-cmds.h:23
 msgid "Forward-port local commits to the updated upstream head"
-msgstr ""
+msgstr "Reporter en avant les validations locales dans la tête en amont mise Ã  jour"
 
 #: common-cmds.h:24
 msgid "Reset current HEAD to the specified state"
-msgstr ""
+msgstr "Réinitialiser la HEAD courante Ã  l'état spécifié"
 
 #: common-cmds.h:25
 msgid "Remove files from the working tree and from the index"
-msgstr ""
+msgstr "Supprimer les fichiers de la copie de travail et de l'index"
 
 #: common-cmds.h:26
 msgid "Show various types of objects"
-msgstr ""
+msgstr "Afficher différents types d'objects"
 
 #: common-cmds.h:27
 msgid "Show the working tree status"
-msgstr ""
+msgstr "Afficher le statut de la copie de travail"
 
 #: common-cmds.h:28
 msgid "Create, list, delete or verify a tag object signed with GPG"
-msgstr ""
+msgstr "Créer, lister, supprimer ou vérifier un objet d'étiquette signé avec GPG"
 
 #: git-am.sh:50
 msgid "You need to set your committer info first"
-msgstr ""
+msgstr "Vous devez d'abord définir vos informations de validateur"
 
 #: git-am.sh:95
 msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
 "Not rewinding to ORIG_HEAD"
 msgstr ""
+"Vous semblez avoir déplacé la HEAD depuis le dernier Ã©chec de 'am'.\n"
+"Pas de retour Ã  ORIG_HEAD"
 
 #: git-am.sh:105
 #, sh-format
@@ -8853,69 +8957,76 @@ msgid ""
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
+"Lorsque vous aurez résolu ce problème, lancez \"$cmdline --resolved\".\n"
+"Si vous préférez sauter ce patch, lancez \"$cmdline --skip\" Ã  la place.\n"
+"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"."
 
 #: git-am.sh:121
 msgid "Cannot fall back to three-way merge."
-msgstr ""
+msgstr "Impossible de retourner Ã  une fusion 3-way."
 
 #: git-am.sh:137
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
-msgstr ""
+msgstr "Le dépôt n'a pas les blobs nécessaires pour un retour Ã  une fusion 3-way."
 
 #: git-am.sh:139
 msgid "Using index info to reconstruct a base tree..."
-msgstr ""
+msgstr "Utilisation de l'information de l'index pour reconstruire un arbre de base..."
 
 #: git-am.sh:154
 msgid ""
 "Did you hand edit your patch?\n"
 "It does not apply to blobs recorded in its index."
 msgstr ""
+"Avez-vous Ã©dité le patch Ã  la main ?\n"
+"Il ne s'applique pas aux blobs enregistrés dans son index."
 
 #: git-am.sh:163
 msgid "Falling back to patching base and 3-way merge..."
-msgstr ""
+msgstr "Retour Ã  un patch de la base et fusion 3-way..."
 
 #: git-am.sh:179
 msgid "Failed to merge in the changes."
-msgstr ""
+msgstr "Échec de fusion dans les changements."
 
 #: git-am.sh:274
 msgid "Only one StGIT patch series can be applied at once"
-msgstr ""
+msgstr "Seulement une série de patches StGIT peut Ãªtre appliquée Ã  la fois"
 
 #: git-am.sh:361
 #, sh-format
 msgid "Patch format $patch_format is not supported."
-msgstr ""
+msgstr "Le format de patch $patch_format n'est pas supporté."
 
 #: git-am.sh:363
 msgid "Patch format detection failed."
-msgstr ""
+msgstr "Échec de détection du format du patch."
 
 #: git-am.sh:389
 msgid ""
 "The -b/--binary option has been a no-op for long time, and\n"
 "it will be removed. Please do not use it anymore."
 msgstr ""
+"L'option -b/--binary ne fait plus rien depuis longtemps,\n"
+"et elle sera supprimée. Merci de ne plus l'utiliser."
 
 #: git-am.sh:477
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
-msgstr ""
+msgstr "le répertoire précédent de rebasage $dotest existe toujours mais mbox donnée."
 
 #: git-am.sh:482
 msgid "Please make up your mind. --skip or --abort?"
-msgstr ""
+msgstr "Décidez-vous. --skip ou --abort ?"
 
 #: git-am.sh:509
 msgid "Resolve operation not in progress, we are not resuming."
-msgstr ""
+msgstr "Pas de résolution de l'opération en cours, nous ne sommes pas dans une reprise."
 
 #: git-am.sh:575
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
-msgstr ""
+msgstr "Index sale : impossible d'appliquer des patches (sales : $files)"
 
 #: git-am.sh:679
 #, sh-format
@@ -8924,30 +9035,33 @@ msgid ""
 "If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
 "To restore the original branch and stop patching run \"$cmdline --abort\"."
 msgstr ""
+"Le patch est vide.  Ã‰tait-il mal découpé ?\n"
+"Si vous préférez sauter ce patch, lancez plutôt \"$cmdline --skip\".\n"
+"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"."
 
 #: git-am.sh:706
 msgid "Patch does not have a valid e-mail address."
-msgstr ""
+msgstr "Le patch n'a pas d'adresse e-mail valide."
 
 #: git-am.sh:753
 msgid "cannot be interactive without stdin connected to a terminal."
-msgstr ""
+msgstr "impossible d'être interactif sans entrée standard connectée Ã  un terminal."
 
 #: git-am.sh:757
 msgid "Commit Body is:"
-msgstr ""
+msgstr "Le corps de la validation est :"
 
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
 #: git-am.sh:764
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
-msgstr ""
+msgstr "Appliquer ? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 
 #: git-am.sh:800
 #, sh-format
 msgid "Applying: $FIRSTLINE"
-msgstr ""
+msgstr "Application : $FIRSTLINE"
 
 #: git-am.sh:821
 msgid ""
@@ -8961,15 +9075,17 @@ msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
 msgstr ""
+"Vous avez toujours des chemins non fusionnés dans votre index\n"
+"auriez-vous oublié de faire 'git add' ?"
 
 #: git-am.sh:845
 msgid "No changes -- Patch already applied."
-msgstr ""
+msgstr "Pas de changement -- Patch déjà appliqué."
 
 #: git-am.sh:855
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
-msgstr ""
+msgstr "Le patch a Ã©choué Ã  $msgnum $FIRSTLINE"
 
 #: git-am.sh:858
 #, sh-format
@@ -8977,26 +9093,28 @@ msgid ""
 "The copy of the patch that failed is found in:\n"
 "   $dotest/patch"
 msgstr ""
+"La copie du patch qui a Ã©choué se trouve dans :\n"
+"   $dotest/patch"
 
 #: git-am.sh:876
 msgid "applying to an empty history"
-msgstr ""
+msgstr "application Ã  un historique vide"
 
 #: git-bisect.sh:48
 msgid "You need to start by \"git bisect start\""
-msgstr ""
+msgstr "Vous devez démarrer avec \"git bisect start\""
 
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
 #: git-bisect.sh:54
 msgid "Do you want me to do it for you [Y/n]? "
-msgstr ""
+msgstr "Souhaitez-vous que je le fasse pour vous [Y/n] ? "
 
 #: git-bisect.sh:95
 #, sh-format
 msgid "unrecognised option: '$arg'"
-msgstr ""
+msgstr "option inconnue : '$arg'"
 
 #: git-bisect.sh:99
 #, sh-format
@@ -9005,13 +9123,13 @@ msgstr "'$arg' ne semble Ãªtre une révision valide"
 
 #: git-bisect.sh:117
 msgid "Bad HEAD - I need a HEAD"
-msgstr ""
+msgstr "Mauvaise HEAD - j'ai besoin d'une HEAD"
 
 #: git-bisect.sh:130
 #, sh-format
 msgid ""
 "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
-msgstr ""
+msgstr "L'extraction de '$start_head' a Ã©choué. Essayez 'git bisect reset <branchevalide>'."
 
 #: git-bisect.sh:140
 msgid "won't bisect on seeked tree"
@@ -9019,49 +9137,51 @@ msgstr ""
 
 #: git-bisect.sh:144
 msgid "Bad HEAD - strange symbolic ref"
-msgstr ""
+msgstr "Mauvaise HEAD - référence symbolique douteuse"
 
 #: git-bisect.sh:189
 #, sh-format
 msgid "Bad bisect_write argument: $state"
-msgstr ""
+msgstr "Mauvais argument pour bisect_write : $state"
 
 #: git-bisect.sh:218
 #, sh-format
 msgid "Bad rev input: $arg"
-msgstr ""
+msgstr "Mauvaise révision en entrée : $arg"
 
 #: git-bisect.sh:232
 msgid "Please call 'bisect_state' with at least one argument."
-msgstr ""
+msgstr "Merci d'appeler 'bisect_state' avec au moins un argument."
 
 #: git-bisect.sh:244
 #, sh-format
 msgid "Bad rev input: $rev"
-msgstr ""
+msgstr "Mauvaise révision en entrée : $rev"
 
 #: git-bisect.sh:250
 msgid "'git bisect bad' can take only one argument."
-msgstr ""
+msgstr "'git bisect bad' n'accepte qu'un seul argument."
 
 #. have bad but not good.  we could bisect although
 #. this is less optimum.
 #: git-bisect.sh:273
 msgid "Warning: bisecting only with a bad commit."
-msgstr ""
+msgstr "Attention : bissection avec seulement une mauvaise validation."
 
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
 #. at this point.
 #: git-bisect.sh:279
 msgid "Are you sure [Y/n]? "
-msgstr ""
+msgstr "Êtes-vous sûr [Y/n] ? "
 
 #: git-bisect.sh:289
 msgid ""
 "You need to give me at least one good and one bad revisions.\n"
 "(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
 msgstr ""
+"Vous devez me donner au moins une bonne et une mauvaise révision.\n"
+"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)"
 
 #: git-bisect.sh:292
 msgid ""
@@ -9069,15 +9189,18 @@ msgid ""
 "You then need to give me at least one good and one bad revisions.\n"
 "(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
 msgstr ""
+"Vous devez démarrer avec \"git bisect start\".\n"
+"Puis vous devez me donner au moins une bonne et une mauvaise révision.\n"
+"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)"
 
 #: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
-msgstr ""
+msgstr "Pas de bissection en cours."
 
 #: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
-msgstr "'$invalid' n'est pas un commit valide"
+msgstr "'$invalid' n'est pas une validation valide"
 
 #: git-bisect.sh:379
 #, sh-format
@@ -9085,24 +9208,26 @@ msgid ""
 "Could not check out original HEAD '$branch'.\n"
 "Try 'git bisect reset <commit>'."
 msgstr ""
+"Échec d'extraction de la HEAD d'origine '$branch'.\n"
+"Essayez 'git bisect reset <commit>'."
 
 #: git-bisect.sh:406
 msgid "No logfile given"
-msgstr ""
+msgstr "Pas de fichier de log donné"
 
 #: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
-msgstr ""
+msgstr "impossible de lire $file pour rejouer"
 
 #: git-bisect.sh:424
 msgid "?? what are you talking about?"
-msgstr ""
+msgstr "?? de quoi parlez-vous ?"
 
 #: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
-msgstr ""
+msgstr "lancement de $command"
 
 #: git-bisect.sh:443
 #, sh-format
@@ -9110,10 +9235,12 @@ msgid ""
 "bisect run failed:\n"
 "exit code $res from '$command' is < 0 or >= 128"
 msgstr ""
+"la bissection a Ã©choué :\n"
+"le code retour $res de '$command' est < 0 ou >= 128"
 
 #: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
-msgstr ""
+msgstr "la bissection ne peut plus continuer"
 
 #: git-bisect.sh:475
 #, sh-format
@@ -9121,10 +9248,12 @@ msgid ""
 "bisect run failed:\n"
 "'bisect_state $state' exited with error code $res"
 msgstr ""
+"la bissection a Ã©choué :\n"
+"'bisect_state $state' a retourné le code erreur $res"
 
 #: git-bisect.sh:482
 msgid "bisect run success"
-msgstr ""
+msgstr "succès de la bissection"
 
 #: git-pull.sh:21
 msgid ""
@@ -9132,14 +9261,17 @@ msgid ""
 "Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
 "as appropriate to mark resolution, or use 'git commit -a'."
 msgstr ""
+"Le pull n'est pas possible car vous avez des fichiers non fusionnés.\n"
+"Merci de corriger dans votre copie de travail, et utilisez alors 'git add/rm <file>'\n"
+"si nécessaire pour marquer comme résolu, ou utilisez 'git commit -a'."
 
 #: git-pull.sh:25
 msgid "Pull is not possible because you have unmerged files."
-msgstr ""
+msgstr "Le pull n'est pas possible car vous avez des fichiers non fusionnés."
 
 #: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
-msgstr ""
+msgstr "mise Ã  jour d'une branche non encore créée avec les changements ajoutés dans l'index"
 
 #. The fetch involved updating the current branch.
 #. The working tree and the index file is still based on the
@@ -9152,14 +9284,17 @@ msgid ""
 "Warning: fast-forwarding your working tree from\n"
 "Warning: commit $orig_head."
 msgstr ""
+"Attention : fetch a mis Ã  jour la tête de la branche courante.\n"
+"Attention : mise Ã  jour en avance rapide de votre copie de travail\n"
+"Attention : depuis la validation $orig_head."
 
 #: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
-msgstr ""
+msgstr "Impossible de fusionner de multiples branches sur une tête vide"
 
 #: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
-msgstr ""
+msgstr "Impossible de rebaser sur de multiples branches"
 
 #: git-rebase.sh:53
 msgid ""
@@ -9168,41 +9303,46 @@ msgid ""
 "To check out the original branch and stop rebasing, run \"git rebase --abort"
 "\"."
 msgstr ""
+"Lorsque vous aurez résolu ce problème, lancez \"git rebase --continue\".\n"
+"Si vous préférez sauter ce patch, lancez \"git rebase --skip\" Ã  la place.\n"
+"Pour extraire la branche d'origine et stopper le rebasage, lancez \"git rebase --abort\"."
 
 #: git-rebase.sh:160
 msgid "The pre-rebase hook refused to rebase."
-msgstr ""
+msgstr "Le hook pre-rebase a refusé de rebaser."
 
 #: git-rebase.sh:165
 msgid "It looks like git-am is in progress. Cannot rebase."
-msgstr ""
+msgstr "Il semble que git-am soit en cours. Impossible de rebaser."
 
 #: git-rebase.sh:296
 msgid "The --exec option must be used with the --interactive option"
-msgstr ""
+msgstr "L'option --exec doit Ãªtre utilisée avec l'option --interactive"
 
 #: git-rebase.sh:301
 msgid "No rebase in progress?"
-msgstr ""
+msgstr "Pas de rebasage en cours ?"
 
 #: git-rebase.sh:312
 msgid "The --edit-todo action can only be used during interactive rebase."
-msgstr ""
+msgstr "L'action --edit-todo peut seulement Ãªtre utilisée lors d'un rebasage interactif."
 
 #: git-rebase.sh:319
 msgid "Cannot read HEAD"
-msgstr ""
+msgstr "Impossible de lire HEAD"
 
 #: git-rebase.sh:322
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
 msgstr ""
+"Vous devez Ã©diter tous les conflits de fusion et\n"
+"les marquer comme résolus avec git add"
 
 #: git-rebase.sh:340
 #, sh-format
 msgid "Could not move back to $head_name"
-msgstr ""
+msgstr "Impossible de revenir Ã  $head_name"
 
 #: git-rebase.sh:359
 #, sh-format
@@ -9216,88 +9356,96 @@ msgid ""
 "and run me again.  I am stopping in case you still have something\n"
 "valuable there."
 msgstr ""
+"Il semble qu'il y ait déjà un répertoire $state_dir_base, et je me demande\n"
+"si vous n'êtes pas au milieu d'un autre rebasage.  Si c'est le cas,\n"
+"merci d'essayer\n"
+"\t$cmd_live_rebase\n"
+"Si Ã§a n'est pas le cas, merci de\n"
+"\t$cmd_clear_stale_rebase\n"
+"et relancer Ã  nouveau.  Je m'arrête au cas où vous auriez quelque chose\n"
+"d'important ici."
 
 #: git-rebase.sh:404
 #, sh-format
 msgid "invalid upstream $upstream_name"
-msgstr ""
+msgstr "invalide $upstream_name en amont"
 
 #: git-rebase.sh:428
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
-msgstr ""
+msgstr "$onto_name : il y a plus d'une base de fusion"
 
 #: git-rebase.sh:431 git-rebase.sh:435
 #, sh-format
 msgid "$onto_name: there is no merge base"
-msgstr ""
+msgstr "$onto_name : il n'y a pas de base de fusion"
 
 #: git-rebase.sh:440
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
-msgstr ""
+msgstr "Ne pointe pas sur une validation valide : $onto_name"
 
 #: git-rebase.sh:463
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
-msgstr ""
+msgstr "fatal : pas de branche : $branch_name"
 
 #: git-rebase.sh:483
 msgid "Please commit or stash them."
-msgstr ""
+msgstr "Merci de les valider ou de les remiser."
 
 #: git-rebase.sh:501
 #, sh-format
 msgid "Current branch $branch_name is up to date."
-msgstr ""
+msgstr "La branche courante $branch_name est Ã  jour."
 
 #: git-rebase.sh:504
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
-msgstr ""
+msgstr "La branche courante $branch_name est Ã  jour, rebasage forcé."
 
 #: git-rebase.sh:515
 #, sh-format
 msgid "Changes from $mb to $onto:"
-msgstr ""
+msgstr "Changements de $mb sur $onto :"
 
 #. Detach HEAD and reset the tree
 #: git-rebase.sh:524
 msgid "First, rewinding head to replay your work on top of it..."
-msgstr ""
+msgstr "Premièrement, retour de head pour rejouer votre travail par-dessus..."
 
 #: git-rebase.sh:532
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
-msgstr ""
+msgstr "$branch_name mise Ã  jour en avance rapide sur $onto_name."
 
 #: git-stash.sh:51
 msgid "git stash clear with parameters is unimplemented"
-msgstr ""
+msgstr "git stash clear avec des paramètres n'est pas implémenté"
 
 #: git-stash.sh:74
 msgid "You do not have the initial commit yet"
-msgstr ""
+msgstr "Vous n'avez pas encore la validation initiale"
 
 #: git-stash.sh:89
 msgid "Cannot save the current index state"
-msgstr ""
+msgstr "Impossible de sauver l'état courant de l'index"
 
 #: git-stash.sh:123 git-stash.sh:136
 msgid "Cannot save the current worktree state"
-msgstr ""
+msgstr "Impossible de sauver l'état courant de la copie de travail"
 
 #: git-stash.sh:140
 msgid "No changes selected"
-msgstr ""
+msgstr "Aucun changement sélectionné"
 
 #: git-stash.sh:143
 msgid "Cannot remove temporary index (can't happen)"
-msgstr ""
+msgstr "Impossible de supprimer l'index temporaire (ne peut pas se produire)"
 
 #: git-stash.sh:156
 msgid "Cannot record working tree state"
-msgstr ""
+msgstr "Impossible d'enregistrer l'état de la copie de travail"
 
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
@@ -9315,41 +9463,43 @@ msgid ""
 "error: unknown option for 'stash save': $option\n"
 "       To provide a message, use git stash save -- '$option'"
 msgstr ""
+"erreur: option inconnue pour 'stash save': $option\n"
+"        Pour fournir un message, utilisez git stash save -- '$option'"
 
 #: git-stash.sh:223
 msgid "No local changes to save"
-msgstr ""
+msgstr "Pas de changements en local Ã  sauver"
 
 #: git-stash.sh:227
 msgid "Cannot initialize stash"
-msgstr ""
+msgstr "Impossible d'initialiser le remisage"
 
 #: git-stash.sh:235
 msgid "Cannot save the current status"
-msgstr ""
+msgstr "Impossible de sauver le statut courant"
 
 #: git-stash.sh:253
 msgid "Cannot remove worktree changes"
-msgstr ""
+msgstr "Impossible de supprimer les changements de la copie de travail"
 
 #: git-stash.sh:352
 msgid "No stash found."
-msgstr ""
+msgstr "Pas de remisage trouvé."
 
 #: git-stash.sh:359
 #, sh-format
 msgid "Too many revisions specified: $REV"
-msgstr ""
+msgstr "Trop de révisions spécifiées : $REV"
 
 #: git-stash.sh:365
 #, sh-format
 msgid "$reference is not valid reference"
-msgstr ""
+msgstr "$reference n'est pas une référence valide"
 
 #: git-stash.sh:393
 #, sh-format
 msgid "'$args' is not a stash-like commit"
-msgstr "'$args' n'est pas un commit de type remisage"
+msgstr "'$args' n'est pas une validation de type remisage"
 
 #: git-stash.sh:404
 #, sh-format
@@ -9358,23 +9508,23 @@ msgstr "'$args' n'est pas une référence de remisage"
 
 #: git-stash.sh:412
 msgid "unable to refresh index"
-msgstr ""
+msgstr "impossible de rafraîchir l'index"
 
 #: git-stash.sh:416
 msgid "Cannot apply a stash in the middle of a merge"
-msgstr ""
+msgstr "Impossible d'appliquer un remisage au milieu d'une fusion"
 
 #: git-stash.sh:424
 msgid "Conflicts in index. Try without --index."
-msgstr ""
+msgstr "Conflits dans l'index. Essayez sans --index."
 
 #: git-stash.sh:426
 msgid "Could not save index tree"
-msgstr ""
+msgstr "Impossible de sauver l'arbre d'index"
 
 #: git-stash.sh:460
 msgid "Cannot unstage modified files"
-msgstr ""
+msgstr "Impossible de désindexer les fichiers modifiés"
 
 #: git-stash.sh:475
 msgid "Index was not unstashed."
@@ -9383,50 +9533,50 @@ msgstr ""
 #: git-stash.sh:492
 #, sh-format
 msgid "Dropped ${REV} ($s)"
-msgstr ""
+msgstr "${REV} supprimé ($s)"
 
 #: git-stash.sh:493
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
-msgstr ""
+msgstr "${REV}: Impossible de supprimer l'entrée de stash"
 
 #: git-stash.sh:500
 msgid "No branch name specified"
-msgstr ""
+msgstr "Aucune branche spécifiée"
 
 #: git-stash.sh:571
 msgid "(To restore them type \"git stash apply\")"
-msgstr ""
+msgstr "(Pour les restaurer tapez \"git stash apply\")"
 
 #: git-submodule.sh:91
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
-msgstr ""
+msgstr "impossible de supprimer un composant de l'URL '$remoteurl'"
 
 #: git-submodule.sh:196
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
-msgstr ""
+msgstr "Pas de mappage du sous-module trouvé dans .gitmodules pour le chemin '$sm_path'"
 
 #: git-submodule.sh:239
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
-msgstr ""
+msgstr "Le clonage de '$url' dans le chemin de sous-module '$sm_path' a Ã©choué"
 
 #: git-submodule.sh:251
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
-msgstr ""
+msgstr "Le répertoire Git '$a' fait partie du chemin de sous-module '$b' ou vice-versa"
 
 #: git-submodule.sh:349
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
-msgstr ""
+msgstr "L'URL de dépôt '$repo' doit Ãªtre absolu ou commencer par ./|../"
 
 #: git-submodule.sh:366
 #, sh-format
 msgid "'$sm_path' already exists in the index"
-msgstr ""
+msgstr "'$sm_path' existe déjà dans l'index"
 
 #: git-submodule.sh:370
 #, sh-format
@@ -9435,11 +9585,14 @@ msgid ""
 "$sm_path\n"
 "Use -f if you really want to add it."
 msgstr ""
+"Le chemin suivant est ignoré par un de vos fichiers .gitignore :\n"
+"$sm_path\n"
+"Utilisez -f si vous voulez vraiment l'ajouter."
 
 #: git-submodule.sh:388
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
-msgstr ""
+msgstr "Ajout du dépôt existant Ã  '$sm_path' dans l'index"
 
 #: git-submodule.sh:390
 #, sh-format
@@ -9449,119 +9602,119 @@ msgstr "'$sm_path' existe déjà et n'est pas un dépôt git valide"
 #: git-submodule.sh:398
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
-msgstr ""
+msgstr "Un répertoire git pour '$sm_name' est trouvé en local avec le(s) serveur(s) distant(s) :"
 
 #: git-submodule.sh:400
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
-msgstr ""
+msgstr "Si vous voulez réutiliser ce répertoire git local au lieu de cloner Ã  nouveau depuis"
 
 #: git-submodule.sh:402
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
-msgstr ""
+msgstr "utilisez l'option '--force'. Si le répertoire local git n'est pas le dépôt correct"
 
 #: git-submodule.sh:403
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
 "option."
-msgstr ""
+msgstr "ou vous ne savez pas ce que cela signifie de choisir un autre nom avec l'option '--name'."
 
 #: git-submodule.sh:405
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
-msgstr ""
+msgstr "Réactivation du répertoire git local pour le sous-module '$sm_name'."
 
 #: git-submodule.sh:417
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
-msgstr ""
+msgstr "Impossible d'extraire le sous-module '$sm_path'"
 
 #: git-submodule.sh:422
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
-msgstr ""
+msgstr "Échec d'ajout du sous-module '$sm_path'"
 
 #: git-submodule.sh:431
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
-msgstr ""
+msgstr "Échec d'enregistrement du sous-module '$sm_path'"
 
 #: git-submodule.sh:474
 #, sh-format
 msgid "Entering '$prefix$sm_path'"
-msgstr ""
+msgstr "Entrée dans '$prefix$sm_path'"
 
 #: git-submodule.sh:488
 #, sh-format
 msgid "Stopping at '$sm_path'; script returned non-zero status."
-msgstr ""
+msgstr "Arrêt sur '$sm_path' ; le script a retourné un statut non nul."
 
 #: git-submodule.sh:532
 #, sh-format
 msgid "No url found for submodule path '$sm_path' in .gitmodules"
-msgstr ""
+msgstr "URL non trouvé pour le chemin de sous-module '$sm_path' dans .gitmodules"
 
 #: git-submodule.sh:541
 #, sh-format
 msgid "Failed to register url for submodule path '$sm_path'"
-msgstr ""
+msgstr "Échec d'enregistrement de l'URL pour le chemin de sous-module '$sm_path'"
 
 #: git-submodule.sh:543
 #, sh-format
 msgid "Submodule '$name' ($url) registered for path '$sm_path'"
-msgstr ""
+msgstr "Sous-module '$name' ($url) enregistré pour le chemin '$sm_path'"
 
 #: git-submodule.sh:551
 #, sh-format
 msgid "Failed to register update mode for submodule path '$sm_path'"
-msgstr ""
+msgstr "Échec d'enregistrement du mode de mise Ã  jour pour le chemin de sous-module '$sm_path'"
 
 #: git-submodule.sh:588
 #, sh-format
 msgid "Use '.' if you really want to deinitialize all submodules"
-msgstr ""
+msgstr "Utilisez '.' si vous voulez vraiment réinitialiser tous les sous-modules"
 
 #: git-submodule.sh:603
 #, sh-format
 msgid "Submodule work tree '$sm_path' contains a .git directory"
-msgstr ""
+msgstr "La copie de travail du sous-module '$sm_path' contient un répertoire .git"
 
 #: git-submodule.sh:604
 #, sh-format
 msgid ""
 "(use 'rm -rf' if you really want to remove it including all of its history)"
-msgstr ""
+msgstr "(utilisez 'rm -rf' si vous voulez vraiment le supprimer en incluant tout son historique)"
 
 #: git-submodule.sh:610
 #, sh-format
 msgid ""
 "Submodule work tree '$sm_path' contains local modifications; use '-f' to "
 "discard them"
-msgstr ""
+msgstr "La copie de travail du sous-module '$sm_path' contient des modifications locales; utilisez '-f' pour les annuler"
 
 #: git-submodule.sh:613
 #, sh-format
 msgid "Cleared directory '$sm_path'"
-msgstr ""
+msgstr "Répertoire '$sm_path' nettoyé"
 
 #: git-submodule.sh:614
 #, sh-format
 msgid "Could not remove submodule work tree '$sm_path'"
-msgstr ""
+msgstr "Impossible de supprimer la copie de travail du sous-module '$sm_path'"
 
 #: git-submodule.sh:617
 #, sh-format
 msgid "Could not create empty submodule directory '$sm_path'"
-msgstr ""
+msgstr "Impossible de créer le répertoire vide du sous-module '$sm_path'"
 
 #: git-submodule.sh:626
 #, sh-format
 msgid "Submodule '$name' ($url) unregistered for path '$sm_path'"
-msgstr ""
+msgstr "Le sous-module '$name' ($url) n'est pas enregistré pour le chemin '$sm_path'"
 
 #: git-submodule.sh:731
 #, sh-format
@@ -9569,100 +9722,102 @@ msgid ""
 "Submodule path '$prefix$sm_path' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
+"Chemin de sous-module '$prefix$sm_path' non initialisé\n"
+"Peut-être souhaitez-vous utiliser 'update --init' ?"
 
 #: git-submodule.sh:744
 #, sh-format
 msgid "Unable to find current revision in submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Impossible de trouver la révision courante dans le chemin de sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:753
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
-msgstr ""
+msgstr "Impossible de rapatrier dans le chemin de sous-module '$sm_path'"
 
 #: git-submodule.sh:777
 #, sh-format
 msgid "Unable to fetch in submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Impossible de rapatrier dans le chemin de sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:791
 #, sh-format
 msgid "Unable to rebase '$sha1' in submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Impossible de rebaser '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:792
 #, sh-format
 msgid "Submodule path '$prefix$sm_path': rebased into '$sha1'"
-msgstr ""
+msgstr "Chemin de sous-module '$prefix$sm_path' : rebasé dans '$sha1'"
 
 #: git-submodule.sh:797
 #, sh-format
 msgid "Unable to merge '$sha1' in submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Impossible de fusionner '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:798
 #, sh-format
 msgid "Submodule path '$prefix$sm_path': merged in '$sha1'"
-msgstr ""
+msgstr "Chemin de sous-module '$prefix$sm_path' : fusionné dans '$sha1'"
 
 #: git-submodule.sh:803
 #, sh-format
 msgid "Unable to checkout '$sha1' in submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Impossible d'extraire '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:804
 #, sh-format
 msgid "Submodule path '$prefix$sm_path': checked out '$sha1'"
-msgstr ""
+msgstr "Chemin de sous-module '$prefix$sm_path' : '$sha1' extrait"
 
 #: git-submodule.sh:831
 #, sh-format
 msgid "Failed to recurse into submodule path '$prefix$sm_path'"
-msgstr ""
+msgstr "Échec de parcours dans le chemin du sous-module '$prefix$sm_path'"
 
 #: git-submodule.sh:939
 msgid "The --cached option cannot be used with the --files option"
-msgstr ""
+msgstr "L'option --cached ne peut pas Ãªtre utilisée avec l'option --files"
 
 #. unexpected type
 #: git-submodule.sh:979
 #, sh-format
 msgid "unexpected mode $mod_dst"
-msgstr ""
+msgstr "mode $mod_dst inattendu"
 
 #: git-submodule.sh:997
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_src"
-msgstr ""
+msgstr "  Attention : $name ne contient pas la validation $sha1_src"
 
 #: git-submodule.sh:1000
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_dst"
-msgstr ""
+msgstr "  Attention : $name ne contient pas la validation $sha1_dst"
 
 #: git-submodule.sh:1003
 #, sh-format
 msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
-msgstr ""
+msgstr "  Attention : $name ne contient pas les validations $sha1_src et $sha1_dst"
 
 #: git-submodule.sh:1028
 msgid "blob"
-msgstr ""
+msgstr "blob"
 
 #: git-submodule.sh:1066
 msgid "Submodules changed but not updated:"
-msgstr ""
+msgstr "Sous-modules modifiés mais non mis Ã  jour :"
 
 #: git-submodule.sh:1068
 msgid "Submodule changes to be committed:"
-msgstr ""
+msgstr "Changements du sous-module Ã  valider :"
 
 #: git-submodule.sh:1153
 #, sh-format
 msgid "Failed to recurse into submodule path '$sm_path'"
-msgstr ""
+msgstr "Échec de parcours dans le chemin du sous-module '$sm_path'"
 
 #: git-submodule.sh:1216
 #, sh-format
 msgid "Synchronizing submodule url for '$prefix$sm_path'"
-msgstr ""
+msgstr "Synchronisation de l'URL sous-module pour '$prefix$sm_path'"
index 49cb08df96faa90101bb25d8f96acaffc066f19b..8c44ceb2c715936b314fc10a7aae3dfb44172327 100644 (file)
@@ -2,9 +2,11 @@
  * Copyright (C) 2008 Linus Torvalds
  */
 #include "cache.h"
+#include "pathspec.h"
 
 #ifdef NO_PTHREADS
-static void preload_index(struct index_state *index, const char **pathspec)
+static void preload_index(struct index_state *index,
+                         const struct pathspec *pathspec)
 {
        ; /* nothing */
 }
@@ -24,7 +26,7 @@ static void preload_index(struct index_state *index, const char **pathspec)
 struct thread_data {
        pthread_t pthread;
        struct index_state *index;
-       const char **pathspec;
+       struct pathspec pathspec;
        int offset, nr;
 };
 
@@ -35,9 +37,7 @@ static void *preload_thread(void *_data)
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
        struct cache_def cache;
-       struct pathspec pathspec;
 
-       init_pathspec(&pathspec, p->pathspec);
        memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
@@ -53,7 +53,7 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce_uptodate(ce))
                        continue;
-               if (!ce_path_match(ce, &pathspec))
+               if (!ce_path_match(ce, &p->pathspec))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
                        continue;
@@ -63,11 +63,11 @@ static void *preload_thread(void *_data)
                        continue;
                ce_mark_uptodate(ce);
        } while (--nr > 0);
-       free_pathspec(&pathspec);
        return NULL;
 }
 
-static void preload_index(struct index_state *index, const char **pathspec)
+static void preload_index(struct index_state *index,
+                         const struct pathspec *pathspec)
 {
        int threads, i, work, offset;
        struct thread_data data[MAX_PARALLEL];
@@ -82,10 +82,12 @@ static void preload_index(struct index_state *index, const char **pathspec)
                threads = MAX_PARALLEL;
        offset = 0;
        work = DIV_ROUND_UP(index->cache_nr, threads);
+       memset(&data, 0, sizeof(data));
        for (i = 0; i < threads; i++) {
                struct thread_data *p = data+i;
                p->index = index;
-               p->pathspec = pathspec;
+               if (pathspec)
+                       copy_pathspec(&p->pathspec, pathspec);
                p->offset = offset;
                p->nr = work;
                offset += work;
@@ -100,7 +102,8 @@ static void preload_index(struct index_state *index, const char **pathspec)
 }
 #endif
 
-int read_index_preload(struct index_state *index, const char **pathspec)
+int read_index_preload(struct index_state *index,
+                      const struct pathspec *pathspec)
 {
        int retval = read_index(index);
 
diff --git a/quote.c b/quote.c
index 5c8808160e700af0c055a84f8527eb3ac76962d9..45e3db12d514dd7393539ca0aff09f5aa8467b8d 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -42,23 +42,6 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
        free(to_free);
 }
 
-void sq_quote_print(FILE *stream, const char *src)
-{
-       char c;
-
-       fputc('\'', stream);
-       while ((c = *src++)) {
-               if (need_bs_quote(c)) {
-                       fputs("'\\", stream);
-                       fputc(c, stream);
-                       fputc('\'', stream);
-               } else {
-                       fputc(c, stream);
-               }
-       }
-       fputc('\'', stream);
-}
-
 void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
 {
        int i;
@@ -408,72 +391,72 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
 
 /* quoting as a string literal for other languages */
 
-void perl_quote_print(FILE *stream, const char *src)
+void perl_quote_buf(struct strbuf *sb, const char *src)
 {
        const char sq = '\'';
        const char bq = '\\';
        char c;
 
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
        while ((c = *src++)) {
                if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
+                       strbuf_addch(sb, bq);
+               strbuf_addch(sb, c);
        }
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
 }
 
-void python_quote_print(FILE *stream, const char *src)
+void python_quote_buf(struct strbuf *sb, const char *src)
 {
        const char sq = '\'';
        const char bq = '\\';
        const char nl = '\n';
        char c;
 
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
        while ((c = *src++)) {
                if (c == nl) {
-                       fputc(bq, stream);
-                       fputc('n', stream);
+                       strbuf_addch(sb, bq);
+                       strbuf_addch(sb, 'n');
                        continue;
                }
                if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
+                       strbuf_addch(sb, bq);
+               strbuf_addch(sb, c);
        }
-       fputc(sq, stream);
+       strbuf_addch(sb, sq);
 }
 
-void tcl_quote_print(FILE *stream, const char *src)
+void tcl_quote_buf(struct strbuf *sb, const char *src)
 {
        char c;
 
-       fputc('"', stream);
+       strbuf_addch(sb, '"');
        while ((c = *src++)) {
                switch (c) {
                case '[': case ']':
                case '{': case '}':
                case '$': case '\\': case '"':
-                       fputc('\\', stream);
+                       strbuf_addch(sb, '\\');
                default:
-                       fputc(c, stream);
+                       strbuf_addch(sb, c);
                        break;
                case '\f':
-                       fputs("\\f", stream);
+                       strbuf_addstr(sb, "\\f");
                        break;
                case '\r':
-                       fputs("\\r", stream);
+                       strbuf_addstr(sb, "\\r");
                        break;
                case '\n':
-                       fputs("\\n", stream);
+                       strbuf_addstr(sb, "\\n");
                        break;
                case '\t':
-                       fputs("\\t", stream);
+                       strbuf_addstr(sb, "\\t");
                        break;
                case '\v':
-                       fputs("\\v", stream);
+                       strbuf_addstr(sb, "\\v");
                        break;
                }
        }
-       fputc('"', stream);
+       strbuf_addch(sb, '"');
 }
diff --git a/quote.h b/quote.h
index ed110a5d8d9e98a680c67379c7c39e4eda5b25f5..71dcc3aa0279af6611f7471d1a4c9949b1dc49fb 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -27,8 +27,6 @@ struct strbuf;
  * excluding the final null regardless of the buffer size.
  */
 
-extern void sq_quote_print(FILE *stream, const char *src);
-
 extern void sq_quote_buf(struct strbuf *, const char *src);
 extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
 
@@ -68,8 +66,8 @@ extern char *quote_path_relative(const char *in, const char *prefix,
                          struct strbuf *out);
 
 /* quoting as a string literal for other languages */
-extern void perl_quote_print(FILE *stream, const char *src);
-extern void python_quote_print(FILE *stream, const char *src);
-extern void tcl_quote_print(FILE *stream, const char *src);
+extern void perl_quote_buf(struct strbuf *sb, const char *src);
+extern void python_quote_buf(struct strbuf *sb, const char *src);
+extern void tcl_quote_buf(struct strbuf *sb, const char *src);
 
 #endif
index e7e6a1e342200bbf4c37bd561e6a68222349418e..654a8c58d689daf43f2ba4c40e4fe54d31c7826a 100644 (file)
@@ -80,8 +80,7 @@ static void process_tree(struct tree *tree,
                else
                        process_blob(lookup_blob(entry.sha1), p, &me, entry.path, cp);
        }
-       free(tree->buffer);
-       tree->buffer = NULL;
+       free_tree_buffer(tree);
 }
 
 static void process_tag(struct tag *tag, struct object_array *p,
index c3d5e3543fae75c81a7a0a00c48bb0076675a2f1..6bbe1b1fb3435247e1114cbf496cd9d7640f3def 100644 (file)
@@ -1114,7 +1114,8 @@ static void show_file(const char * fmt, const char * name, int in_porcelain,
        printf(fmt, name);
 }
 
-int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
+int refresh_index(struct index_state *istate, unsigned int flags,
+                 const struct pathspec *pathspec,
                  char *seen, const char *header_msg)
 {
        int i;
@@ -1149,7 +1150,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
                        continue;
 
                if (pathspec &&
-                   !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+                   !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        filtered = 1;
 
                if (ce_stage(ce)) {
@@ -1229,14 +1230,14 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall
 struct ondisk_cache_entry {
        struct cache_time ctime;
        struct cache_time mtime;
-       unsigned int dev;
-       unsigned int ino;
-       unsigned int mode;
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int size;
+       uint32_t dev;
+       uint32_t ino;
+       uint32_t mode;
+       uint32_t uid;
+       uint32_t gid;
+       uint32_t size;
        unsigned char sha1[20];
-       unsigned short flags;
+       uint16_t flags;
        char name[FLEX_ARRAY]; /* more */
 };
 
@@ -1248,15 +1249,15 @@ struct ondisk_cache_entry {
 struct ondisk_cache_entry_extended {
        struct cache_time ctime;
        struct cache_time mtime;
-       unsigned int dev;
-       unsigned int ino;
-       unsigned int mode;
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int size;
+       uint32_t dev;
+       uint32_t ino;
+       uint32_t mode;
+       uint32_t uid;
+       uint32_t gid;
+       uint32_t size;
        unsigned char sha1[20];
-       unsigned short flags;
-       unsigned short flags2;
+       uint16_t flags;
+       uint16_t flags2;
        char name[FLEX_ARRAY]; /* more */
 };
 
@@ -1817,8 +1818,17 @@ int write_index(struct index_state *istate, int newfd)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
-               if (is_null_sha1(ce->sha1))
-                       return error("cache entry has null sha1: %s", ce->name);
+               if (is_null_sha1(ce->sha1)) {
+                       static const char msg[] = "cache entry has null sha1: %s";
+                       static int allow = -1;
+
+                       if (allow < 0)
+                               allow = git_env_bool("GIT_ALLOW_NULL_SHA1", 0);
+                       if (allow)
+                               warning(msg, ce->name);
+                       else
+                               return error(msg, ce->name);
+               }
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
diff --git a/refs.c b/refs.c
index 7922261580515859bc89005c4c69e8623c50c728..d78860c46d95ea9785c83c08824355275393ed99 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3196,14 +3196,6 @@ int update_ref(const char *action, const char *refname,
        return 0;
 }
 
-struct ref *find_ref_by_name(const struct ref *list, const char *name)
-{
-       for ( ; list; list = list->next)
-               if (!strcmp(list->name, name))
-                       return (struct ref *)list;
-       return NULL;
-}
-
 /*
  * generate a format suitable for scanf from a ref_rev_parse_rules
  * rule, that is replace the "%.*s" spec with a "%s" spec
index 5b3ce9eed299e312c132b590ea4926cfb019a271..b5ebe0180069c1c1f96b769a37c5f3eb9459dfe9 100644 (file)
@@ -6,6 +6,7 @@
 #include "exec_cmd.h"
 #include "run-command.h"
 #include "pkt-line.h"
+#include "string-list.h"
 #include "sideband.h"
 #include "argv-array.h"
 
@@ -16,11 +17,13 @@ struct options {
        int verbosity;
        unsigned long depth;
        unsigned progress : 1,
+               check_self_contained_and_connected : 1,
                followtags : 1,
                dry_run : 1,
                thin : 1;
 };
 static struct options options;
+static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
 static int set_option(const char *name, const char *value)
 {
@@ -67,6 +70,22 @@ static int set_option(const char *name, const char *value)
                        return -1;
                return 0;
        }
+       else if (!strcmp(name, "check-connectivity")) {
+               if (!strcmp(value, "true"))
+                       options.check_self_contained_and_connected = 1;
+               else if (!strcmp(value, "false"))
+                       options.check_self_contained_and_connected = 0;
+               else
+                       return -1;
+               return 0;
+       }
+       else if (!strcmp(name, "cas")) {
+               struct strbuf val = STRBUF_INIT;
+               strbuf_addf(&val, "--" CAS_OPT_NAME "=%s", value);
+               string_list_append(&cas_options, val.buf);
+               strbuf_release(&val);
+               return 0;
+       }
        else {
                return 1 /* unsupported */;
        }
@@ -654,7 +673,7 @@ static int fetch_git(struct discovery *heads,
        struct strbuf preamble = STRBUF_INIT;
        char *depth_arg = NULL;
        int argc = 0, i, err;
-       const char *argv[15];
+       const char *argv[16];
 
        argv[argc++] = "fetch-pack";
        argv[argc++] = "--stateless-rpc";
@@ -668,6 +687,8 @@ static int fetch_git(struct discovery *heads,
                argv[argc++] = "-v";
                argv[argc++] = "-v";
        }
+       if (options.check_self_contained_and_connected)
+               argv[argc++] = "--check-self-contained-and-connected";
        if (!options.progress)
                argv[argc++] = "--no-progress";
        if (options.depth) {
@@ -790,6 +811,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
        struct rpc_state rpc;
        int i, err;
        struct argv_array args;
+       struct string_list_item *cas_option;
 
        argv_array_init(&args);
        argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
@@ -804,6 +826,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
        else if (options.verbosity > 1)
                argv_array_push(&args, "--verbose");
        argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+       for_each_string_list_item(cas_option, &cas_options)
+               argv_array_push(&args, cas_option->string);
        argv_array_push(&args, url);
        for (i = 0; i < nr_spec; i++)
                argv_array_push(&args, specs[i]);
@@ -939,6 +963,7 @@ int main(int argc, const char **argv)
                        printf("fetch\n");
                        printf("option\n");
                        printf("push\n");
+                       printf("check-connectivity\n");
                        printf("\n");
                        fflush(stdout);
                } else {
index efcba931eca963bd6a5fd13f01a4859e0ae9e14d..24334679e0971e3af5e168d1cf7259da55d21d50 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -148,6 +148,7 @@ static struct remote *make_remote(const char *name, int len)
        }
 
        ret = xcalloc(1, sizeof(struct remote));
+       ret->prune = -1;  /* unspecified */
        ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
        remotes[remotes_nr++] = ret;
        if (len)
@@ -404,6 +405,8 @@ static int handle_config(const char *key, const char *value, void *cb)
                remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, ".skipfetchall"))
                remote->skip_default_update = git_config_bool(key, value);
+       else if (!strcmp(subkey, ".prune"))
+               remote->prune = git_config_bool(key, value);
        else if (!strcmp(subkey, ".url")) {
                const char *v;
                if (git_config_string(&v, key, value))
@@ -1302,6 +1305,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        free(sent_tips.tip);
 }
 
+struct ref *find_ref_by_name(const struct ref *list, const char *name)
+{
+       for ( ; list; list = list->next)
+               if (!strcmp(list->name, name))
+                       return (struct ref *)list;
+       return NULL;
+}
+
 static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
 {
        for ( ; ref; ref = ref->next)
@@ -1411,12 +1422,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
 }
 
 void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
-       int force_update)
+                            int force_update)
 {
        struct ref *ref;
 
        for (ref = remote_refs; ref; ref = ref->next) {
                int force_ref_update = ref->force || force_update;
+               int reject_reason = 0;
 
                if (ref->peer_ref)
                        hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
@@ -1431,6 +1443,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                }
 
                /*
+                * Bypass the usual "must fast-forward" check but
+                * replace it with a weaker "the old value must be
+                * this value we observed".  If the remote ref has
+                * moved and is now different from what we expect,
+                * reject any push.
+                *
+                * It also is an error if the user told us to check
+                * with the remote-tracking branch to find the value
+                * to expect, but we did not have such a tracking
+                * branch.
+                */
+               if (ref->expect_old_sha1) {
+                       if (ref->expect_old_no_trackback ||
+                           hashcmp(ref->old_sha1, ref->old_sha1_expect))
+                               reject_reason = REF_STATUS_REJECT_STALE;
+               }
+
+               /*
+                * The usual "must fast-forward" rules.
+                *
                 * Decide whether an individual refspec A:B can be
                 * pushed.  The push will succeed if any of the
                 * following are true:
@@ -1448,24 +1480,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 *     passing the --force argument
                 */
 
-               if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
-                       int why = 0; /* why would this push require --force? */
-
+               else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
                        if (!prefixcmp(ref->name, "refs/tags/"))
-                               why = REF_STATUS_REJECT_ALREADY_EXISTS;
+                               reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_sha1_file(ref->old_sha1))
-                               why = REF_STATUS_REJECT_FETCH_FIRST;
+                               reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
                        else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
                                 !lookup_commit_reference_gently(ref->new_sha1, 1))
-                               why = REF_STATUS_REJECT_NEEDS_FORCE;
+                               reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
                        else if (!ref_newer(ref->new_sha1, ref->old_sha1))
-                               why = REF_STATUS_REJECT_NONFASTFORWARD;
-
-                       if (!force_ref_update)
-                               ref->status = why;
-                       else if (why)
-                               ref->forced_update = 1;
+                               reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
                }
+
+               /*
+                * "--force" will defeat any rejection implemented
+                * by the rules above.
+                */
+               if (!force_ref_update)
+                       ref->status = reject_reason;
+               else if (reject_reason)
+                       ref->forced_update = 1;
        }
 }
 
@@ -1936,3 +1970,121 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
        string_list_clear(&ref_names, 0);
        return stale_refs;
 }
+
+/*
+ * Compare-and-swap
+ */
+void clear_cas_option(struct push_cas_option *cas)
+{
+       int i;
+
+       for (i = 0; i < cas->nr; i++)
+               free(cas->entry[i].refname);
+       free(cas->entry);
+       memset(cas, 0, sizeof(*cas));
+}
+
+static struct push_cas *add_cas_entry(struct push_cas_option *cas,
+                                     const char *refname,
+                                     size_t refnamelen)
+{
+       struct push_cas *entry;
+       ALLOC_GROW(cas->entry, cas->nr + 1, cas->alloc);
+       entry = &cas->entry[cas->nr++];
+       memset(entry, 0, sizeof(*entry));
+       entry->refname = xmemdupz(refname, refnamelen);
+       return entry;
+}
+
+int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
+{
+       const char *colon;
+       struct push_cas *entry;
+
+       if (unset) {
+               /* "--no-<option>" */
+               clear_cas_option(cas);
+               return 0;
+       }
+
+       if (!arg) {
+               /* just "--<option>" */
+               cas->use_tracking_for_rest = 1;
+               return 0;
+       }
+
+       /* "--<option>=refname" or "--<option>=refname:value" */
+       colon = strchrnul(arg, ':');
+       entry = add_cas_entry(cas, arg, colon - arg);
+       if (!*colon)
+               entry->use_tracking = 1;
+       else if (get_sha1(colon + 1, entry->expect))
+               return error("cannot parse expected object name '%s'", colon + 1);
+       return 0;
+}
+
+int parseopt_push_cas_option(const struct option *opt, const char *arg, int unset)
+{
+       return parse_push_cas_option(opt->value, arg, unset);
+}
+
+int is_empty_cas(const struct push_cas_option *cas)
+{
+       return !cas->use_tracking_for_rest && !cas->nr;
+}
+
+/*
+ * Look at remote.fetch refspec and see if we have a remote
+ * tracking branch for the refname there.  Fill its current
+ * value in sha1[].
+ * If we cannot do so, return negative to signal an error.
+ */
+static int remote_tracking(struct remote *remote, const char *refname,
+                          unsigned char sha1[20])
+{
+       char *dst;
+
+       dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+       if (!dst)
+               return -1; /* no tracking ref for refname at remote */
+       if (read_ref(dst, sha1))
+               return -1; /* we know what the tracking ref is but we cannot read it */
+       return 0;
+}
+
+static void apply_cas(struct push_cas_option *cas,
+                     struct remote *remote,
+                     struct ref *ref)
+{
+       int i;
+
+       /* Find an explicit --<option>=<name>[:<value>] entry */
+       for (i = 0; i < cas->nr; i++) {
+               struct push_cas *entry = &cas->entry[i];
+               if (!refname_match(entry->refname, ref->name, ref_rev_parse_rules))
+                       continue;
+               ref->expect_old_sha1 = 1;
+               if (!entry->use_tracking)
+                       hashcpy(ref->old_sha1_expect, cas->entry[i].expect);
+               else if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
+                       ref->expect_old_no_trackback = 1;
+               return;
+       }
+
+       /* Are we using "--<option>" to cover all? */
+       if (!cas->use_tracking_for_rest)
+               return;
+
+       ref->expect_old_sha1 = 1;
+       if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
+               ref->expect_old_no_trackback = 1;
+}
+
+void apply_push_cas(struct push_cas_option *cas,
+                   struct remote *remote,
+                   struct ref *remote_refs)
+{
+       struct ref *ref;
+       for (ref = remote_refs; ref; ref = ref->next)
+               apply_cas(cas, remote, ref);
+}
index cf5672466151254b92582ba4729a2a95e670beff..131130a611b55c9f79649037e6dde916b621f578 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -1,6 +1,8 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
+#include "parse-options.h"
+
 enum {
        REMOTE_CONFIG,
        REMOTE_REMOTES,
@@ -40,6 +42,7 @@ struct remote {
        int fetch_tags;
        int skip_default_update;
        int mirror;
+       int prune;
 
        const char *receivepack;
        const char *uploadpack;
@@ -71,6 +74,56 @@ struct refspec {
 
 extern const struct refspec *tag_refspec;
 
+struct ref {
+       struct ref *next;
+       unsigned char old_sha1[20];
+       unsigned char new_sha1[20];
+       unsigned char old_sha1_expect[20]; /* used by expect-old */
+       char *symref;
+       unsigned int
+               force:1,
+               forced_update:1,
+               expect_old_sha1:1,
+               expect_old_no_trackback:1,
+               deletion:1,
+               matched:1;
+
+       /*
+        * Order is important here, as we write to FETCH_HEAD
+        * in numeric order. And the default NOT_FOR_MERGE
+        * should be 0, so that xcalloc'd structures get it
+        * by default.
+        */
+       enum {
+               FETCH_HEAD_MERGE = -1,
+               FETCH_HEAD_NOT_FOR_MERGE = 0,
+               FETCH_HEAD_IGNORE = 1
+       } fetch_head_status;
+
+       enum {
+               REF_STATUS_NONE = 0,
+               REF_STATUS_OK,
+               REF_STATUS_REJECT_NONFASTFORWARD,
+               REF_STATUS_REJECT_ALREADY_EXISTS,
+               REF_STATUS_REJECT_NODELETE,
+               REF_STATUS_REJECT_FETCH_FIRST,
+               REF_STATUS_REJECT_NEEDS_FORCE,
+               REF_STATUS_REJECT_STALE,
+               REF_STATUS_UPTODATE,
+               REF_STATUS_REMOTE_REJECT,
+               REF_STATUS_EXPECTING_REPORT
+       } status;
+       char *remote_status;
+       struct ref *peer_ref; /* when renaming */
+       char name[FLEX_ARRAY]; /* more */
+};
+
+#define REF_NORMAL     (1u << 0)
+#define REF_HEADS      (1u << 1)
+#define REF_TAGS       (1u << 2)
+
+extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
+
 struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
@@ -84,6 +137,14 @@ int check_ref_type(const struct ref *ref, int flags);
  */
 void free_refs(struct ref *ref);
 
+struct extra_have_objects {
+       int nr, alloc;
+       unsigned char (*array)[20];
+};
+extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
+                                    struct ref **list, unsigned int flags,
+                                    struct extra_have_objects *);
+
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
 
@@ -172,4 +233,27 @@ struct ref *guess_remote_head(const struct ref *head,
 /* Return refs which no longer exist on remote */
 struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);
 
+/*
+ * Compare-and-swap
+ */
+#define CAS_OPT_NAME "force-with-lease"
+
+struct push_cas_option {
+       unsigned use_tracking_for_rest:1;
+       struct push_cas {
+               unsigned char expect[20];
+               unsigned use_tracking:1;
+               char *refname;
+       } *entry;
+       int nr;
+       int alloc;
+};
+
+extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
+extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset);
+extern void clear_cas_option(struct push_cas_option *);
+
+extern int is_empty_cas(const struct push_cas_option *);
+void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
+
 #endif
index 6fde8f94c27d5a3a196098af0f4716e27566dae5..1f2d21a72f56a68a7f9307bfd9092087687cffae 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -6,6 +6,7 @@
 #include "resolve-undo.h"
 #include "ll-merge.h"
 #include "attr.h"
+#include "pathspec.h"
 
 #define RESOLVED 0
 #define PUNTED 1
@@ -656,7 +657,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
        return 0;
 }
 
-int rerere_forget(const char **pathspec)
+int rerere_forget(struct pathspec *pathspec)
 {
        int i, fd;
        struct string_list conflict = STRING_LIST_INIT_DUP;
@@ -671,8 +672,8 @@ int rerere_forget(const char **pathspec)
        find_conflict(&conflict);
        for (i = 0; i < conflict.nr; i++) {
                struct string_list_item *it = &conflict.items[i];
-               if (!match_pathspec(pathspec, it->string, strlen(it->string),
-                                   0, NULL))
+               if (!match_pathspec_depth(pathspec, it->string, strlen(it->string),
+                                         0, NULL))
                        continue;
                rerere_forget_one_path(it->string, &merge_rr);
        }
index 156d2aad804527d5ae766b5381f3b5e0d2176580..4aa06c9a519aa289f177487b41e3f6c5b1213ef4 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -3,6 +3,8 @@
 
 #include "string-list.h"
 
+struct pathspec;
+
 #define RERERE_AUTOUPDATE   01
 #define RERERE_NOAUTOUPDATE 02
 
@@ -16,7 +18,7 @@ extern void *RERERE_RESOLVED;
 extern int setup_rerere(struct string_list *, int);
 extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
-extern int rerere_forget(const char **);
+extern int rerere_forget(struct pathspec *);
 extern int rerere_remaining(struct string_list *);
 extern void rerere_clear(struct string_list *);
 extern void rerere_gc(struct string_list *);
index 77101f51c153ab014ecb80a02ca52daaa16cb10d..c09b00664e6892c84424bb4984152883460d3df6 100644 (file)
@@ -173,7 +173,7 @@ void unmerge_marked_index(struct index_state *istate)
        }
 }
 
-void unmerge_index(struct index_state *istate, const char **pathspec)
+void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
 {
        int i;
 
@@ -182,7 +182,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec)
 
        for (i = 0; i < istate->cache_nr; i++) {
                const struct cache_entry *ce = istate->cache[i];
-               if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+               if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
                        continue;
                i = unmerge_index_entry_at(istate, i);
        }
index 7a30206aad1fdee74f7e7b6e5967f9f8d9048dbf..46306455edddb94a554a7a2fcadf49a30861f599 100644 (file)
@@ -11,7 +11,7 @@ extern void resolve_undo_write(struct strbuf *, struct string_list *);
 extern struct string_list *resolve_undo_read(const char *, unsigned long);
 extern void resolve_undo_clear_index(struct index_state *);
 extern int unmerge_index_entry_at(struct index_state *, int);
-extern void unmerge_index(struct index_state *, const char **);
+extern void unmerge_index(struct index_state *, const struct pathspec *);
 extern void unmerge_marked_index(struct index_state *);
 
 #endif
index 84ccc0529b75508be5d1603a9e73790ca2b102fe..172b0d3b2c090c4b0bcce40b5cb19d5e544a0aed 100644 (file)
@@ -15,6 +15,7 @@
 #include "string-list.h"
 #include "line-log.h"
 #include "mailmap.h"
+#include "commit-slab.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -138,8 +139,7 @@ void mark_tree_uninteresting(struct tree *tree)
         * We don't care about the tree any more
         * after it has been marked uninteresting.
         */
-       free(tree->buffer);
-       tree->buffer = NULL;
+       free_tree_buffer(tree);
 }
 
 void mark_parents_uninteresting(struct commit *commit)
@@ -1372,7 +1372,7 @@ static void prepare_show_merge(struct rev_info *revs)
                        i++;
        }
        free_pathspec(&revs->prune_data);
-       init_pathspec(&revs->prune_data, prune);
+       parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune);
        revs->limited = 1;
 }
 
@@ -2120,8 +2120,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                 */
                ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
                prune_data.path[prune_data.nr++] = NULL;
-               init_pathspec(&revs->prune_data,
-                             get_pathspec(revs->prefix, prune_data.path));
+               parse_pathspec(&revs->prune_data, 0, 0,
+                              revs->prefix, prune_data.path);
        }
 
        if (revs->def == NULL)
@@ -2154,12 +2154,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                revs->limited = 1;
 
        if (revs->prune_data.nr) {
-               diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
+               copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
                if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                        revs->prune = 1;
                if (!revs->full_diff)
-                       diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
+                       copy_pathspec(&revs->diffopt.pathspec,
+                                     &revs->prune_data);
        }
        if (revs->combine_merges)
                revs->ignore_merges = 0;
@@ -2763,7 +2764,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
        return retval;
 }
 
-static inline int want_ancestry(struct rev_info *revs)
+static inline int want_ancestry(const struct rev_info *revs)
 {
        return (revs->rewrite_parents || revs->children.name);
 }
@@ -2820,6 +2821,14 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
        if (action == commit_show &&
            !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
+               /*
+                * --full-diff on simplified parents is no good: it
+                * will show spurious changes from the commits that
+                * were elided.  So we save the parents on the side
+                * when --full-diff is in effect.
+                */
+               if (revs->full_diff)
+                       save_parents(revs, commit);
                if (rewrite_parents(revs, commit, rewrite_one) < 0)
                        return commit_error;
        }
@@ -2839,6 +2848,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                free(entry);
 
                if (revs->reflog_info) {
+                       save_parents(revs, commit);
                        fake_reflog_parent(revs->reflog_info, commit);
                        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
                }
@@ -3038,6 +3048,8 @@ struct commit *get_revision(struct rev_info *revs)
        c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
+       if (!c)
+               free_saved_parents(revs);
        return c;
 }
 
@@ -3069,3 +3081,54 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
        fputs(mark, stdout);
        putchar(' ');
 }
+
+define_commit_slab(saved_parents, struct commit_list *);
+
+#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
+
+void save_parents(struct rev_info *revs, struct commit *commit)
+{
+       struct commit_list **pp;
+
+       if (!revs->saved_parents_slab) {
+               revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
+               init_saved_parents(revs->saved_parents_slab);
+       }
+
+       pp = saved_parents_at(revs->saved_parents_slab, commit);
+
+       /*
+        * When walking with reflogs, we may visit the same commit
+        * several times: once for each appearance in the reflog.
+        *
+        * In this case, save_parents() will be called multiple times.
+        * We want to keep only the first set of parents.  We need to
+        * store a sentinel value for an empty (i.e., NULL) parent
+        * list to distinguish it from a not-yet-saved list, however.
+        */
+       if (*pp)
+               return;
+       if (commit->parents)
+               *pp = copy_commit_list(commit->parents);
+       else
+               *pp = EMPTY_PARENT_LIST;
+}
+
+struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
+{
+       struct commit_list *parents;
+
+       if (!revs->saved_parents_slab)
+               return commit->parents;
+
+       parents = *saved_parents_at(revs->saved_parents_slab, commit);
+       if (parents == EMPTY_PARENT_LIST)
+               return NULL;
+       return parents;
+}
+
+void free_saved_parents(struct rev_info *revs)
+{
+       if (revs->saved_parents_slab)
+               clear_saved_parents(revs->saved_parents_slab);
+}
index 95859ba119033ba17346c701493df1474eec5df2..e7f1d211bf0a203978a3024bcbae5c98f25c60cf 100644 (file)
@@ -25,6 +25,7 @@
 struct rev_info;
 struct log_info;
 struct string_list;
+struct saved_parents;
 
 struct rev_cmdline_info {
        unsigned int nr;
@@ -187,6 +188,9 @@ struct rev_info {
 
        /* line level range that we are chasing */
        struct decoration line_log_data;
+
+       /* copies of the parent lists, for --full-diff display */
+       struct saved_parents *saved_parents_slab;
 };
 
 #define REV_TREE_SAME          0
@@ -273,4 +277,20 @@ typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct
 
 extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
        rewrite_parent_fn_t rewrite_parent);
+
+/*
+ * Save a copy of the parent list, and return the saved copy.  This is
+ * used by the log machinery to retrieve the original parents when
+ * commit->parents has been modified by history simpification.
+ *
+ * You may only call save_parents() once per commit (this is checked
+ * for non-root commits).
+ *
+ * get_saved_parents() will transparently return commit->parents if
+ * history simplification is off.
+ */
+extern void save_parents(struct rev_info *revs, struct commit *commit);
+extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
+extern void free_saved_parents(struct rev_info *revs);
+
 #endif
index 7d172ef37f7b185f9586ce9015a08481d0bd259c..b228d65613c511ffa59eb3ab254ee2e2b6f26c9b 100644 (file)
@@ -5,6 +5,7 @@
 #include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
+#include "connect.h"
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
@@ -226,6 +227,7 @@ int send_pack(struct send_pack_args *args,
                case REF_STATUS_REJECT_ALREADY_EXISTS:
                case REF_STATUS_REJECT_FETCH_FIRST:
                case REF_STATUS_REJECT_NEEDS_FORCE:
+               case REF_STATUS_REJECT_STALE:
                case REF_STATUS_UPTODATE:
                        continue;
                default:
diff --git a/setup.c b/setup.c
index 5262319be69b3bf3fd2492dea3d30e7353d66a23..f08dd649761f4d53abef66da052340a3c78d0fe5 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,7 +5,19 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-static char *prefix_path_gently(const char *prefix, int len, const char *path)
+/*
+ * Normalize "path", prepending the "prefix" for relative paths. If
+ * remaining_prefix is not NULL, return the actual prefix still
+ * remains in the path. For example, prefix = sub1/sub2/ and path is
+ *
+ *  foo          -> sub1/sub2/foo  (full prefix)
+ *  ../foo       -> sub1/foo       (remaining prefix is sub1/)
+ *  ../../bar    -> bar            (no remaining prefix)
+ *  ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
+ *  `pwd`/../bar -> sub1/bar       (no remaining prefix)
+ */
+char *prefix_path_gently(const char *prefix, int len,
+                        int *remaining_prefix, const char *path)
 {
        const char *orig = path;
        char *sanitized;
@@ -13,13 +25,17 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
                const char *temp = real_path(path);
                sanitized = xmalloc(len + strlen(temp) + 1);
                strcpy(sanitized, temp);
+               if (remaining_prefix)
+                       *remaining_prefix = 0;
        } else {
                sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
+               if (remaining_prefix)
+                       *remaining_prefix = len;
        }
-       if (normalize_path_copy(sanitized, sanitized))
+       if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
                goto error_out;
        if (is_absolute_path(orig)) {
                size_t root_len, len, total;
@@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
 
 char *prefix_path(const char *prefix, int len, const char *path)
 {
-       char *r = prefix_path_gently(prefix, len, path);
+       char *r = prefix_path_gently(prefix, len, NULL, path);
        if (!r)
                die("'%s' is outside repository", path);
        return r;
@@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
 int path_inside_repo(const char *prefix, const char *path)
 {
        int len = prefix ? strlen(prefix) : 0;
-       char *r = prefix_path_gently(prefix, len, path);
+       char *r = prefix_path_gently(prefix, len, NULL, path);
        if (r) {
                free(r);
                return 1;
@@ -154,155 +170,6 @@ void verify_non_filename(const char *prefix, const char *arg)
            "'git <command> [<revision>...] -- [<file>...]'", arg);
 }
 
-/*
- * Magic pathspec
- *
- * NEEDSWORK: These need to be moved to dir.h or even to a new
- * pathspec.h when we restructure get_pathspec() users to use the
- * "struct pathspec" interface.
- *
- * Possible future magic semantics include stuff like:
- *
- *     { PATHSPEC_NOGLOB, '!', "noglob" },
- *     { PATHSPEC_ICASE, '\0', "icase" },
- *     { PATHSPEC_RECURSIVE, '*', "recursive" },
- *     { PATHSPEC_REGEXP, '\0', "regexp" },
- *
- */
-#define PATHSPEC_FROMTOP    (1<<0)
-
-static struct pathspec_magic {
-       unsigned bit;
-       char mnemonic; /* this cannot be ':'! */
-       const char *name;
-} pathspec_magic[] = {
-       { PATHSPEC_FROMTOP, '/', "top" },
-};
-
-/*
- * Take an element of a pathspec and check for magic signatures.
- * Append the result to the prefix.
- *
- * For now, we only parse the syntax and throw out anything other than
- * "top" magic.
- *
- * NEEDSWORK: This needs to be rewritten when we start migrating
- * get_pathspec() users to use the "struct pathspec" interface.  For
- * example, a pathspec element may be marked as case-insensitive, but
- * the prefix part must always match literally, and a single stupid
- * string cannot express such a case.
- */
-static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
-{
-       unsigned magic = 0;
-       const char *copyfrom = elt;
-       int i;
-
-       if (elt[0] != ':') {
-               ; /* nothing to do */
-       } else if (elt[1] == '(') {
-               /* longhand */
-               const char *nextat;
-               for (copyfrom = elt + 2;
-                    *copyfrom && *copyfrom != ')';
-                    copyfrom = nextat) {
-                       size_t len = strcspn(copyfrom, ",)");
-                       if (copyfrom[len] == ',')
-                               nextat = copyfrom + len + 1;
-                       else
-                               /* handle ')' and '\0' */
-                               nextat = copyfrom + len;
-                       if (!len)
-                               continue;
-                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-                               if (strlen(pathspec_magic[i].name) == len &&
-                                   !strncmp(pathspec_magic[i].name, copyfrom, len)) {
-                                       magic |= pathspec_magic[i].bit;
-                                       break;
-                               }
-                       if (ARRAY_SIZE(pathspec_magic) <= i)
-                               die("Invalid pathspec magic '%.*s' in '%s'",
-                                   (int) len, copyfrom, elt);
-               }
-               if (*copyfrom != ')')
-                       die("Missing ')' at the end of pathspec magic in '%s'", elt);
-               copyfrom++;
-       } else {
-               /* shorthand */
-               for (copyfrom = elt + 1;
-                    *copyfrom && *copyfrom != ':';
-                    copyfrom++) {
-                       char ch = *copyfrom;
-
-                       if (!is_pathspec_magic(ch))
-                               break;
-                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-                               if (pathspec_magic[i].mnemonic == ch) {
-                                       magic |= pathspec_magic[i].bit;
-                                       break;
-                               }
-                       if (ARRAY_SIZE(pathspec_magic) <= i)
-                               die("Unimplemented pathspec magic '%c' in '%s'",
-                                   ch, elt);
-               }
-               if (*copyfrom == ':')
-                       copyfrom++;
-       }
-
-       if (magic & PATHSPEC_FROMTOP)
-               return xstrdup(copyfrom);
-       else
-               return prefix_path(prefix, prefixlen, copyfrom);
-}
-
-/*
- * N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
- * based interface - see pathspec_magic above.
- *
- * Arguments:
- *  - prefix - a path relative to the root of the working tree
- *  - pathspec - a list of paths underneath the prefix path
- *
- * Iterates over pathspec, prepending each path with prefix,
- * and return the resulting list.
- *
- * If pathspec is empty, return a singleton list containing prefix.
- *
- * If pathspec and prefix are both empty, return an empty list.
- *
- * This is typically used by built-in commands such as add.c, in order
- * to normalize argv arguments provided to the built-in into a list of
- * paths to process, all relative to the root of the working tree.
- */
-const char **get_pathspec(const char *prefix, const char **pathspec)
-{
-       const char *entry = *pathspec;
-       const char **src, **dst;
-       int prefixlen;
-
-       if (!prefix && !entry)
-               return NULL;
-
-       if (!entry) {
-               static const char *spec[2];
-               spec[0] = prefix;
-               spec[1] = NULL;
-               return spec;
-       }
-
-       /* Otherwise we have to re-write the entries.. */
-       src = pathspec;
-       dst = pathspec;
-       prefixlen = prefix ? strlen(prefix) : 0;
-       while (*src) {
-               *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
-               src++;
-       }
-       *dst = NULL;
-       if (!*pathspec)
-               return NULL;
-       return pathspec;
-}
 
 /*
  * Test if it looks like we're at a git directory.
index 8e27db1bd2b49f28b235fcd7e18a0dda43a1f045..f1b649742f0d363c252c47f66f6c7083647f48e6 100644 (file)
@@ -614,7 +614,7 @@ static void scan_windows(struct packed_git *p,
        }
 }
 
-static int unuse_one_window(struct packed_git *current, int keep_fd)
+static int unuse_one_window(struct packed_git *current)
 {
        struct packed_git *p, *lru_p = NULL;
        struct pack_window *lru_w = NULL, *lru_l = NULL;
@@ -628,15 +628,8 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
                pack_mapped -= lru_w->len;
                if (lru_l)
                        lru_l->next = lru_w->next;
-               else {
+               else
                        lru_p->windows = lru_w->next;
-                       if (!lru_p->windows && lru_p->pack_fd != -1
-                               && lru_p->pack_fd != keep_fd) {
-                               close(lru_p->pack_fd);
-                               pack_open_fds--;
-                               lru_p->pack_fd = -1;
-                       }
-               }
                free(lru_w);
                pack_open_windows--;
                return 1;
@@ -644,10 +637,10 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
        return 0;
 }
 
-void release_pack_memory(size_t need, int fd)
+void release_pack_memory(size_t need)
 {
        size_t cur = pack_mapped;
-       while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd))
+       while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
                ; /* nothing */
 }
 
@@ -658,7 +651,7 @@ void *xmmap(void *start, size_t length,
        if (ret == MAP_FAILED) {
                if (!length)
                        return NULL;
-               release_pack_memory(length, fd);
+               release_pack_memory(length);
                ret = mmap(start, length, prot, flags, fd, offset);
                if (ret == MAP_FAILED)
                        die_errno("Out of memory? mmap failed");
@@ -682,6 +675,83 @@ void close_pack_windows(struct packed_git *p)
        }
 }
 
+/*
+ * The LRU pack is the one with the oldest MRU window, preferring packs
+ * with no used windows, or the oldest mtime if it has no windows allocated.
+ */
+static void find_lru_pack(struct packed_git *p, struct packed_git **lru_p, struct pack_window **mru_w, int *accept_windows_inuse)
+{
+       struct pack_window *w, *this_mru_w;
+       int has_windows_inuse = 0;
+
+       /*
+        * Reject this pack if it has windows and the previously selected
+        * one does not.  If this pack does not have windows, reject
+        * it if the pack file is newer than the previously selected one.
+        */
+       if (*lru_p && !*mru_w && (p->windows || p->mtime > (*lru_p)->mtime))
+               return;
+
+       for (w = this_mru_w = p->windows; w; w = w->next) {
+               /*
+                * Reject this pack if any of its windows are in use,
+                * but the previously selected pack did not have any
+                * inuse windows.  Otherwise, record that this pack
+                * has windows in use.
+                */
+               if (w->inuse_cnt) {
+                       if (*accept_windows_inuse)
+                               has_windows_inuse = 1;
+                       else
+                               return;
+               }
+
+               if (w->last_used > this_mru_w->last_used)
+                       this_mru_w = w;
+
+               /*
+                * Reject this pack if it has windows that have been
+                * used more recently than the previously selected pack.
+                * If the previously selected pack had windows inuse and
+                * we have not encountered a window in this pack that is
+                * inuse, skip this check since we prefer a pack with no
+                * inuse windows to one that has inuse windows.
+                */
+               if (*mru_w && *accept_windows_inuse == has_windows_inuse &&
+                   this_mru_w->last_used > (*mru_w)->last_used)
+                       return;
+       }
+
+       /*
+        * Select this pack.
+        */
+       *mru_w = this_mru_w;
+       *lru_p = p;
+       *accept_windows_inuse = has_windows_inuse;
+}
+
+static int close_one_pack(void)
+{
+       struct packed_git *p, *lru_p = NULL;
+       struct pack_window *mru_w = NULL;
+       int accept_windows_inuse = 1;
+
+       for (p = packed_git; p; p = p->next) {
+               if (p->pack_fd == -1)
+                       continue;
+               find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
+       }
+
+       if (lru_p) {
+               close(lru_p->pack_fd);
+               pack_open_fds--;
+               lru_p->pack_fd = -1;
+               return 1;
+       }
+
+       return 0;
+}
+
 void unuse_pack(struct pack_window **w_cursor)
 {
        struct pack_window *w = *w_cursor;
@@ -777,7 +847,7 @@ static int open_packed_git_1(struct packed_git *p)
                        pack_max_fds = 1;
        }
 
-       while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
+       while (pack_max_fds <= pack_open_fds && close_one_pack())
                ; /* nothing */
 
        p->pack_fd = git_open_noatime(p->pack_name);
@@ -893,7 +963,7 @@ unsigned char *use_pack(struct packed_git *p,
                        win->len = (size_t)len;
                        pack_mapped += win->len;
                        while (packed_git_limit < pack_mapped
-                               && unuse_one_window(p, p->pack_fd))
+                               && unuse_one_window(p))
                                ; /* nothing */
                        win->base = xmmap(NULL, win->len,
                                PROT_READ, MAP_PRIVATE,
@@ -939,7 +1009,7 @@ static struct packed_git *alloc_packed_git(int extra)
 
 static void try_to_free_pack_memory(size_t size)
 {
-       release_pack_memory(size, -1);
+       release_pack_memory(size);
 }
 
 struct packed_git *add_packed_git(const char *path, int path_len, int local)
@@ -2925,7 +2995,10 @@ int has_sha1_file(const unsigned char *sha1)
 
        if (find_pack_entry(sha1, &e))
                return 1;
-       return has_loose_object(sha1);
+       if (has_loose_object(sha1))
+               return 1;
+       reprepare_packed_git();
+       return find_pack_entry(sha1, &e);
 }
 
 static void check_tree(const void *buf, size_t size)
index 3f0a3f9419a3dead24cd8e850051f595d0b5974f..1905d75b2b09ee7e1503b35bf072710a659ea1ef 100644 (file)
@@ -10,6 +10,7 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "argv-array.h"
+#include "blob.h"
 
 static struct string_list config_name_for_path;
 static struct string_list config_fetch_recurse_submodules_for_name;
@@ -30,6 +31,118 @@ static struct sha1_array ref_tips_after_fetch;
  */
 static int gitmodules_is_unmerged;
 
+/*
+ * This flag is set if the .gitmodules file had unstaged modifications on
+ * startup. This must be checked before allowing modifications to the
+ * .gitmodules file with the intention to stage them later, because when
+ * continuing we would stage the modifications the user didn't stage herself
+ * too. That might change in a future version when we learn to stage the
+ * changes we do ourselves without staging any previous modifications.
+ */
+static int gitmodules_is_modified;
+
+
+int is_staging_gitmodules_ok(void)
+{
+       return !gitmodules_is_modified;
+}
+
+/*
+ * Try to update the "path" entry in the "submodule.<name>" section of the
+ * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<oldpath> setting was found and we could update it.
+ */
+int update_path_in_gitmodules(const char *oldpath, const char *newpath)
+{
+       struct strbuf entry = STRBUF_INIT;
+       struct string_list_item *path_option;
+
+       if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+               return -1;
+
+       if (gitmodules_is_unmerged)
+               die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+       path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
+       if (!path_option) {
+               warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
+               return -1;
+       }
+       strbuf_addstr(&entry, "submodule.");
+       strbuf_addstr(&entry, path_option->util);
+       strbuf_addstr(&entry, ".path");
+       if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
+               /* Maybe the user already did that, don't error out here */
+               warning(_("Could not update .gitmodules entry %s"), entry.buf);
+               strbuf_release(&entry);
+               return -1;
+       }
+       strbuf_release(&entry);
+       return 0;
+}
+
+/*
+ * Try to remove the "submodule.<name>" section from .gitmodules where the given
+ * path is configured. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<path> setting was found and we could remove it.
+ */
+int remove_path_from_gitmodules(const char *path)
+{
+       struct strbuf sect = STRBUF_INIT;
+       struct string_list_item *path_option;
+
+       if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+               return -1;
+
+       if (gitmodules_is_unmerged)
+               die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+       if (!path_option) {
+               warning(_("Could not find section in .gitmodules where path=%s"), path);
+               return -1;
+       }
+       strbuf_addstr(&sect, "submodule.");
+       strbuf_addstr(&sect, path_option->util);
+       if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
+               /* Maybe the user already did that, don't error out here */
+               warning(_("Could not remove .gitmodules entry for %s"), path);
+               strbuf_release(&sect);
+               return -1;
+       }
+       strbuf_release(&sect);
+       return 0;
+}
+
+void stage_updated_gitmodules(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct stat st;
+       int pos;
+       struct cache_entry *ce;
+       int namelen = strlen(".gitmodules");
+
+       pos = cache_name_pos(".gitmodules", namelen);
+       if (pos < 0) {
+               warning(_("could not find .gitmodules in index"));
+               return;
+       }
+       ce = active_cache[pos];
+       ce->ce_flags = namelen;
+       if (strbuf_read_file(&buf, ".gitmodules", 0) < 0)
+               die(_("reading updated .gitmodules failed"));
+       if (lstat(".gitmodules", &st) < 0)
+               die_errno(_("unable to stat updated .gitmodules"));
+       fill_stat_cache_info(ce, &st);
+       ce->ce_mode = ce_mode_from_stat(ce, st.st_mode);
+       if (remove_cache_entry_at(pos) < 0)
+               die(_("unable to remove .gitmodules from index"));
+       if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1))
+               die(_("adding updated .gitmodules failed"));
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+               die(_("staging updated .gitmodules failed"));
+}
+
 static int add_submodule_odb(const char *path)
 {
        struct strbuf objects_directory = STRBUF_INIT;
@@ -116,6 +229,11 @@ void gitmodules_config(void)
                                    !memcmp(ce->name, ".gitmodules", 11))
                                        gitmodules_is_unmerged = 1;
                        }
+               } else if (pos < active_nr) {
+                       struct stat st;
+                       if (lstat(".gitmodules", &st) == 0 &&
+                           ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)
+                               gitmodules_is_modified = 1;
                }
 
                if (!gitmodules_is_unmerged)
@@ -134,6 +252,9 @@ int parse_submodule_config_option(const char *var, const char *value)
                return 0;
 
        if (!strcmp(key, "path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+
                config = unsorted_string_list_lookup(&config_name_for_path, value);
                if (config)
                        free(config->util);
@@ -151,6 +272,9 @@ int parse_submodule_config_option(const char *var, const char *value)
        } else if (!strcmp(key, "ignore")) {
                char *name_cstr;
 
+               if (!value)
+                       return config_error_nonbool(var);
+
                if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
                    strcmp(value, "all") && strcmp(value, "none")) {
                        warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
@@ -1004,3 +1128,34 @@ int merge_submodule(unsigned char result[20], const char *path,
        free(merges.objects);
        return 0;
 }
+
+/* Update gitfile and core.worktree setting to connect work tree and git dir */
+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
+{
+       struct strbuf file_name = STRBUF_INIT;
+       struct strbuf rel_path = STRBUF_INIT;
+       const char *real_work_tree = xstrdup(real_path(work_tree));
+       FILE *fp;
+
+       /* Update gitfile */
+       strbuf_addf(&file_name, "%s/.git", work_tree);
+       fp = fopen(file_name.buf, "w");
+       if (!fp)
+               die(_("Could not create git link %s"), file_name.buf);
+       fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
+                                                 &rel_path));
+       fclose(fp);
+
+       /* Update core.worktree setting */
+       strbuf_reset(&file_name);
+       strbuf_addf(&file_name, "%s/config", git_dir);
+       if (git_config_set_in_file(file_name.buf, "core.worktree",
+                                  relative_path(real_work_tree, git_dir,
+                                                &rel_path)))
+               die(_("Could not set core.worktree in %s"),
+                   file_name.buf);
+
+       strbuf_release(&file_name);
+       strbuf_release(&rel_path);
+       free((void *)real_work_tree);
+}
index c7ffc7c399d23781d823954d74bab4620debb628..7beec4822b9a35a6286a303f6cf877be1c734568 100644 (file)
@@ -11,6 +11,10 @@ enum {
        RECURSE_SUBMODULES_ON = 2
 };
 
+int is_staging_gitmodules_ok(void);
+int update_path_in_gitmodules(const char *oldpath, const char *newpath);
+int remove_path_from_gitmodules(const char *path);
+void stage_updated_gitmodules(void);
 void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                const char *path);
 int submodule_config(const char *var, const char *value, void *cb);
@@ -36,5 +40,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c
 int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
                struct string_list *needs_pushing);
 int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
 
 #endif
index 1b97c5465b7d9e4404e11668b44c5c507a302d4a..2d44088f56e56fadae0eb9c8fe69ad05c1bbc441 100644 (file)
@@ -1 +1,2 @@
 t[0-9][0-9][0-9][0-9]/* -whitespace
+t0110/url-* binary
index d4e7f4736f74e58da504a04be3898b0a4078fedb..99caa42f5cdc199504d1c5fad442d8f8036e376e 100644 (file)
@@ -185,6 +185,26 @@ test_expect_success 'blame -L Y,X (undocumented)' '
        check_count -L6,3 B 1 B1 1 B2 1 D 1
 '
 
+test_expect_success 'blame -L -X' '
+       test_must_fail $PROG -L-1 file
+'
+
+test_expect_success 'blame -L 0' '
+       test_must_fail $PROG -L0 file
+'
+
+test_expect_success 'blame -L ,0' '
+       test_must_fail $PROG -L,0 file
+'
+
+test_expect_success 'blame -L ,+0' '
+       test_must_fail $PROG -L,+0 file
+'
+
+test_expect_success 'blame -L X,+0' '
+       test_must_fail $PROG -L1,+0 file
+'
+
 test_expect_success 'blame -L X,+1' '
        check_count -L3,+1 B2 1
 '
@@ -193,6 +213,14 @@ test_expect_success 'blame -L X,+N' '
        check_count -L3,+4 B 1 B1 1 B2 1 D 1
 '
 
+test_expect_success 'blame -L ,-0' '
+       test_must_fail $PROG -L,-0 file
+'
+
+test_expect_success 'blame -L X,-0' '
+       test_must_fail $PROG -L1,-0 file
+'
+
 test_expect_success 'blame -L X,-1' '
        check_count -L3,-1 B2 1
 '
@@ -225,14 +253,105 @@ test_expect_success 'blame -L /RE/,-N' '
        check_count -L/99/,-3 B 1 B2 1 D 1
 '
 
+# 'file' ends with an incomplete line, so 'wc' reports one fewer lines than
+# git-blame sees, hence the last line is actually $(wc...)+1.
+test_expect_success 'blame -L X (X == nlines)' '
+       n=$(expr $(wc -l <file) + 1) &&
+       check_count -L$n C 1
+'
+
+test_expect_success 'blame -L X (X == nlines + 1)' '
+       n=$(expr $(wc -l <file) + 2) &&
+       test_must_fail $PROG -L$n file
+'
+
 test_expect_success 'blame -L X (X > nlines)' '
        test_must_fail $PROG -L12345 file
 '
 
+test_expect_success 'blame -L ,Y (Y == nlines)' '
+       n=$(expr $(wc -l <file) + 1) &&
+       check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L ,Y (Y == nlines + 1)' '
+       n=$(expr $(wc -l <file) + 2) &&
+       test_must_fail $PROG -L,$n file
+'
+
 test_expect_success 'blame -L ,Y (Y > nlines)' '
        test_must_fail $PROG -L,12345 file
 '
 
+test_expect_success 'blame -L multiple (disjoint)' '
+       check_count -L2,3 -L6,7 A 1 B1 1 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (disjoint: unordered)' '
+       check_count -L6,7 -L2,3 A 1 B1 1 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (adjacent)' '
+       check_count -L2,3 -L4,5 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (adjacent: unordered)' '
+       check_count -L4,5 -L2,3 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (overlapping)' '
+       check_count -L2,4 -L3,5 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (overlapping: unordered)' '
+       check_count -L3,5 -L2,4 A 1 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L multiple (superset/subset)' '
+       check_count -L2,8 -L3,5 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L multiple (superset/subset: unordered)' '
+       check_count -L3,5 -L2,8 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative)' '
+       check_count -L3,3 -L/fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: no preceding range)' '
+       check_count -L/dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: adjacent)' '
+       check_count -L1,1 -L/dog/,+1 A 1 E 1
+'
+
+test_expect_success 'blame -L /RE/ (relative: not found)' '
+       test_must_fail $PROG -L4,4 -L/dog/ file
+'
+
+test_expect_success 'blame -L /RE/ (relative: end-of-file)' '
+       test_must_fail $PROG -L, -L/$/ file
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute)' '
+       check_count -L3,3 -L^/dog/,+2 A 1 B2 1
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: no preceding range)' '
+       check_count -L^/dog/,+2 A 1 B2 1
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: not found)' '
+       test_must_fail $PROG -L4,4 -L^/tambourine/ file
+'
+
+test_expect_success 'blame -L ^/RE/ (absolute: end-of-file)' '
+       n=$(expr $(wc -l <file) + 1) &&
+       check_count -L$n -L^/$/,+2 A 1 C 1 E 1
+'
+
 test_expect_success 'setup -L :regex' '
        tr Q "\\t" >hello.c <<-\EOF &&
        int main(int argc, const char *argv[])
@@ -275,12 +394,139 @@ test_expect_success 'blame -L :nomatch' '
        test_must_fail $PROG -L:nomatch hello.c
 '
 
-test_expect_success 'blame -L bogus' '
-       test_must_fail $PROG -L file &&
-       test_must_fail $PROG -L1,+ file &&
-       test_must_fail $PROG -L1,- file &&
-       test_must_fail $PROG -LX file &&
-       test_must_fail $PROG -L1,X file &&
-       test_must_fail $PROG -L1,+N file &&
+test_expect_success 'blame -L :RE (relative)' '
+       check_count -f hello.c -L3,3 -L:ma.. F 1 H 4
+'
+
+test_expect_success 'blame -L :RE (relative: no preceding range)' '
+       check_count -f hello.c -L:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L :RE (relative: not found)' '
+       test_must_fail $PROG -L3,3 -L:tambourine hello.c
+'
+
+test_expect_success 'blame -L :RE (relative: end-of-file)' '
+       test_must_fail $PROG -L, -L:main hello.c
+'
+
+test_expect_success 'blame -L ^:RE (absolute)' '
+       check_count -f hello.c -L3,3 -L^:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L ^:RE (absolute: no preceding range)' '
+       check_count -f hello.c -L^:ma.. F 4 G 1
+'
+
+test_expect_success 'blame -L ^:RE (absolute: not found)' '
+       test_must_fail $PROG -L4,4 -L^:tambourine hello.c
+'
+
+test_expect_success 'blame -L ^:RE (absolute: end-of-file)' '
+       n=$(printf "%d" $(wc -l <hello.c)) &&
+       check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1
+'
+
+test_expect_success 'setup incremental' '
+       (
+       GIT_AUTHOR_NAME=I &&
+       export GIT_AUTHOR_NAME &&
+       GIT_AUTHOR_EMAIL=I@test.git &&
+       export GIT_AUTHOR_EMAIL &&
+       >incremental &&
+       git add incremental &&
+       git commit -m "step 0" &&
+       printf "partial" >>incremental &&
+       git commit -a -m "step 0.5" &&
+       echo >>incremental &&
+       git commit -a -m "step 1"
+       )
+'
+
+test_expect_success 'blame empty' '
+       check_count -h HEAD^^ -f incremental
+'
+
+test_expect_success 'blame -L 0 empty' '
+       test_must_fail $PROG -L0 incremental HEAD^^
+'
+
+test_expect_success 'blame -L 1 empty' '
+       test_must_fail $PROG -L1 incremental HEAD^^
+'
+
+test_expect_success 'blame -L 2 empty' '
+       test_must_fail $PROG -L2 incremental HEAD^^
+'
+
+test_expect_success 'blame half' '
+       check_count -h HEAD^ -f incremental I 1
+'
+
+test_expect_success 'blame -L 0 half' '
+       test_must_fail $PROG -L0 incremental HEAD^
+'
+
+test_expect_success 'blame -L 1 half' '
+       check_count -h HEAD^ -f incremental -L1 I 1
+'
+
+test_expect_success 'blame -L 2 half' '
+       test_must_fail $PROG -L2 incremental HEAD^
+'
+
+test_expect_success 'blame -L 3 half' '
+       test_must_fail $PROG -L3 incremental HEAD^
+'
+
+test_expect_success 'blame full' '
+       check_count -f incremental I 1
+'
+
+test_expect_success 'blame -L 0 full' '
+       test_must_fail $PROG -L0 incremental
+'
+
+test_expect_success 'blame -L 1 full' '
+       check_count -f incremental -L1 I 1
+'
+
+test_expect_success 'blame -L 2 full' '
+       test_must_fail $PROG -L2 incremental
+'
+
+test_expect_success 'blame -L 3 full' '
+       test_must_fail $PROG -L3 incremental
+'
+
+test_expect_success 'blame -L' '
+       test_must_fail $PROG -L file
+'
+
+test_expect_success 'blame -L X,+' '
+       test_must_fail $PROG -L1,+ file
+'
+
+test_expect_success 'blame -L X,-' '
+       test_must_fail $PROG -L1,- file
+'
+
+test_expect_success 'blame -L X (non-numeric X)' '
+       test_must_fail $PROG -LX file
+'
+
+test_expect_success 'blame -L X,Y (non-numeric Y)' '
+       test_must_fail $PROG -L1,Y file
+'
+
+test_expect_success 'blame -L X,+N (non-numeric N)' '
+       test_must_fail $PROG -L1,+N file
+'
+
+test_expect_success 'blame -L X,-N (non-numeric N)' '
        test_must_fail $PROG -L1,-N file
 '
+
+test_expect_success 'blame -L ,^/RE/' '
+       test_must_fail $PROG -L1,^/99/ file
+'
index 895b9258b07ca65b78ac376023e203f482d8ac16..dab405d57479fe95dcb71b16a7a059410d2ae005 100644 (file)
@@ -141,10 +141,11 @@ stop_httpd() {
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
 
-test_http_push_nonff() {
+test_http_push_nonff () {
        REMOTE_REPO=$1
        LOCAL_REPO=$2
        BRANCH=$3
+       EXPECT_CAS_RESULT=${4-failure}
 
        test_expect_success 'non-fast-forward push fails' '
                cd "$REMOTE_REPO" &&
@@ -167,6 +168,22 @@ test_http_push_nonff() {
        test_expect_success 'non-fast-forward push shows help message' '
                test_i18ngrep "Updates were rejected because" output
        '
+
+       test_expect_failure 'force with lease aka cas' '
+               HEAD=$( cd "$REMOTE_REPO" && git rev-parse --verify HEAD ) &&
+               test_when_finished '\''
+                       (cd "$REMOTE_REPO" && git update-ref HEAD "$HEAD")
+               '\'' &&
+               (
+                       cd "$LOCAL_REPO" &&
+                       git push -v --force-with-lease=$BRANCH:$HEAD origin
+               ) &&
+               git rev-parse --verify "$BRANCH" >expect &&
+               (
+                       cd "$REMOTE_REPO" && git rev-parse --verify HEAD
+               ) >actual &&
+               test_cmp expect actual
+       '
 }
 
 setup_askpass_helper() {
index dd17e3a09d728a08c8038c5ce8187ea63112cdf3..397c4804010c7566a4f969822c413e46fe817e53 100644 (file)
@@ -22,6 +22,9 @@ ErrorLog error.log
 <IfModule !mod_version.c>
        LoadModule version_module modules/mod_version.so
 </IfModule>
+<IfModule !mod_headers.c>
+       LoadModule headers_module modules/mod_headers.so
+</IfModule>
 
 <IfVersion < 2.4>
 LockFile accept.lock
@@ -87,6 +90,11 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_HTTP_EXPORT_ALL
        SetEnv GIT_NAMESPACE ns
 </LocationMatch>
+<LocationMatch /smart_cookies/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+       Header set Set-Cookie name=value
+</LocationMatch>
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 <Directory ${GIT_EXEC_PATH}>
index c29342d6bcd005ecc0eaee0ad5e17f65cdf45c3d..96f40fedfb7285b50243a1c1bbe64649ef7f8edb 100755 (executable)
@@ -432,7 +432,7 @@ test_expect_success_multi SYMLINKS 'symlink' '::    a/symlink' '
 
 test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
        test_check_ignore "a/symlink/foo" 128 &&
-       test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
+       test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link"
 '
 
 test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
@@ -440,7 +440,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
                cd a &&
                test_check_ignore "symlink/foo" 128
        ) &&
-       test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
+       test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link"
 '
 
 ############################################################################
@@ -449,7 +449,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
 
 test_expect_success_multi 'submodule' '' '
        test_check_ignore "a/submodule/one" 128 &&
-       test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
+       test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
 '
 
 test_expect_success_multi 'submodule from subdirectory' '' '
@@ -457,7 +457,7 @@ test_expect_success_multi 'submodule from subdirectory' '' '
                cd a &&
                test_check_ignore "submodule/one" 128
        ) &&
-       test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
+       test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''"
 '
 
 ############################################################################
index e50f0f742fdc4dca766e3f236cd32e388f0c89aa..b92e6cb0469ebb2e328c490cba9b8aaa7c1dbb84 100755 (executable)
@@ -190,4 +190,18 @@ test_expect_success 'required filter clean failure' '
        test_must_fail git add test.fc
 '
 
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'filter large file' '
+       git config filter.largefile.smudge cat &&
+       git config filter.largefile.clean cat &&
+       for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+       echo "2GB filter=largefile" >.gitattributes &&
+       git add 2GB 2>err &&
+       ! test -s err &&
+       rm -f 2GB &&
+       git checkout -- 2GB 2>err &&
+       ! test -s err
+'
+
 test_done
index 05d78d22a6d0fa5534ae3f37cb3c289595b6c1c5..6b3cedcf24613b9c72c67b02c0b731db67055a26 100755 (executable)
@@ -91,6 +91,7 @@ test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
 test_expect_success "setup unicode normalization tests" '
        test_create_repo unicode &&
        cd unicode &&
+       git config core.precomposeunicode false &&
        touch "$aumlcdiar" &&
        git add "$aumlcdiar" &&
        git commit -m initial &&
index 986b2a8f2664b1734964049a5b24fc50cf938cfe..5ed69a6f566bf3a7944b41bace4cfbe26d015040 100755 (executable)
@@ -25,6 +25,10 @@ test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints file
        grep "cannotwrite/test" err
 '
 
+test_expect_success 'git_mkstemps_mode does not fail if fd 0 is not open' '
+       git commit --allow-empty -m message <&-
+'
+
 test_expect_success 'check for a bug in the regex routines' '
        # if this test fails, re-build git with NO_REGEX=1
        test-regex
diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh
new file mode 100755 (executable)
index 0000000..8d6096d
--- /dev/null
@@ -0,0 +1,177 @@
+#!/bin/sh
+
+test_description='urlmatch URL normalization'
+. ./test-lib.sh
+
+# The base name of the test url files
+tu="$TEST_DIRECTORY/t0110/url"
+
+# Note that only file: URLs should be allowed without a host
+
+test_expect_success 'url scheme' '
+       ! test-urlmatch-normalization "" &&
+       ! test-urlmatch-normalization "_" &&
+       ! test-urlmatch-normalization "scheme" &&
+       ! test-urlmatch-normalization "scheme:" &&
+       ! test-urlmatch-normalization "scheme:/" &&
+       ! test-urlmatch-normalization "scheme://" &&
+       ! test-urlmatch-normalization "file" &&
+       ! test-urlmatch-normalization "file:" &&
+       ! test-urlmatch-normalization "file:/" &&
+       test-urlmatch-normalization "file://" &&
+       ! test-urlmatch-normalization "://acme.co" &&
+       ! test-urlmatch-normalization "x_test://acme.co" &&
+       ! test-urlmatch-normalization "-test://acme.co" &&
+       ! test-urlmatch-normalization "0test://acme.co" &&
+       ! test-urlmatch-normalization "+test://acme.co" &&
+       ! test-urlmatch-normalization ".test://acme.co" &&
+       ! test-urlmatch-normalization "schem%6e://" &&
+       test-urlmatch-normalization "x-Test+v1.0://acme.co" &&
+       test "$(test-urlmatch-normalization -p "AbCdeF://x.Y")" = "abcdef://x.y/"
+'
+
+test_expect_success 'url authority' '
+       ! test-urlmatch-normalization "scheme://user:pass@" &&
+       ! test-urlmatch-normalization "scheme://?" &&
+       ! test-urlmatch-normalization "scheme://#" &&
+       ! test-urlmatch-normalization "scheme:///" &&
+       ! test-urlmatch-normalization "scheme://:" &&
+       ! test-urlmatch-normalization "scheme://:555" &&
+       test-urlmatch-normalization "file://user:pass@" &&
+       test-urlmatch-normalization "file://?" &&
+       test-urlmatch-normalization "file://#" &&
+       test-urlmatch-normalization "file:///" &&
+       test-urlmatch-normalization "file://:" &&
+       ! test-urlmatch-normalization "file://:555" &&
+       test-urlmatch-normalization "scheme://user:pass@host" &&
+       test-urlmatch-normalization "scheme://@host" &&
+       test-urlmatch-normalization "scheme://%00@host" &&
+       ! test-urlmatch-normalization "scheme://%%@host" &&
+       ! test-urlmatch-normalization "scheme://host_" &&
+       test-urlmatch-normalization "scheme://user:pass@host/" &&
+       test-urlmatch-normalization "scheme://@host/" &&
+       test-urlmatch-normalization "scheme://host/" &&
+       test-urlmatch-normalization "scheme://host?x" &&
+       test-urlmatch-normalization "scheme://host#x" &&
+       test-urlmatch-normalization "scheme://host/@" &&
+       test-urlmatch-normalization "scheme://host?@x" &&
+       test-urlmatch-normalization "scheme://host#@x" &&
+       test-urlmatch-normalization "scheme://[::1]" &&
+       test-urlmatch-normalization "scheme://[::1]/" &&
+       ! test-urlmatch-normalization "scheme://hos%41/" &&
+       test-urlmatch-normalization "scheme://[invalid....:/" &&
+       test-urlmatch-normalization "scheme://invalid....:]/" &&
+       ! test-urlmatch-normalization "scheme://invalid....:[/" &&
+       ! test-urlmatch-normalization "scheme://invalid....:["
+'
+
+test_expect_success 'url port checks' '
+       test-urlmatch-normalization "xyz://q@some.host:" &&
+       test-urlmatch-normalization "xyz://q@some.host:456/" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:0" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:0000000" &&
+       test-urlmatch-normalization "xyz://q@some.host:0000001?" &&
+       test-urlmatch-normalization "xyz://q@some.host:065535#" &&
+       test-urlmatch-normalization "xyz://q@some.host:65535" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:65536" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:99999" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:100000" &&
+       ! test-urlmatch-normalization "xyz://q@some.host:100001" &&
+       test-urlmatch-normalization "http://q@some.host:80" &&
+       test-urlmatch-normalization "https://q@some.host:443" &&
+       test-urlmatch-normalization "http://q@some.host:80/" &&
+       test-urlmatch-normalization "https://q@some.host:443?" &&
+       ! test-urlmatch-normalization "http://q@:8008" &&
+       ! test-urlmatch-normalization "http://:8080" &&
+       ! test-urlmatch-normalization "http://:" &&
+       test-urlmatch-normalization "xyz://q@some.host:456/" &&
+       test-urlmatch-normalization "xyz://[::1]:456/" &&
+       test-urlmatch-normalization "xyz://[::1]:/" &&
+       ! test-urlmatch-normalization "xyz://[::1]:000/" &&
+       ! test-urlmatch-normalization "xyz://[::1]:0%300/" &&
+       ! test-urlmatch-normalization "xyz://[::1]:0x80/" &&
+       ! test-urlmatch-normalization "xyz://[::1]:4294967297/" &&
+       ! test-urlmatch-normalization "xyz://[::1]:030f/"
+'
+
+test_expect_success 'url port normalization' '
+       test "$(test-urlmatch-normalization -p "http://x:800")" = "http://x:800/" &&
+       test "$(test-urlmatch-normalization -p "http://x:0800")" = "http://x:800/" &&
+       test "$(test-urlmatch-normalization -p "http://x:00000800")" = "http://x:800/" &&
+       test "$(test-urlmatch-normalization -p "http://x:065535")" = "http://x:65535/" &&
+       test "$(test-urlmatch-normalization -p "http://x:1")" = "http://x:1/" &&
+       test "$(test-urlmatch-normalization -p "http://x:80")" = "http://x/" &&
+       test "$(test-urlmatch-normalization -p "http://x:080")" = "http://x/" &&
+       test "$(test-urlmatch-normalization -p "http://x:000000080")" = "http://x/" &&
+       test "$(test-urlmatch-normalization -p "https://x:443")" = "https://x/" &&
+       test "$(test-urlmatch-normalization -p "https://x:0443")" = "https://x/" &&
+       test "$(test-urlmatch-normalization -p "https://x:000000443")" = "https://x/"
+'
+
+test_expect_success 'url general escapes' '
+       ! test-urlmatch-normalization "http://x.y?%fg" &&
+       test "$(test-urlmatch-normalization -p "X://W/%7e%41^%3a")" = "x://w/~A%5E%3A" &&
+       test "$(test-urlmatch-normalization -p "X://W/:/?#[]@")" = "x://w/:/?#[]@" &&
+       test "$(test-urlmatch-normalization -p "X://W/$&()*+,;=")" = "x://w/$&()*+,;=" &&
+       test "$(test-urlmatch-normalization -p "X://W/'\''")" = "x://w/'\''" &&
+       test "$(test-urlmatch-normalization -p "X://W?'\!'")" = "x://w/?'\!'"
+'
+
+test_expect_success 'url high-bit escapes' '
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-1")")" = "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-2")")" = "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-3")")" = "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-4")")" = "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-5")")" = "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-6")")" = "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-7")")" = "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-8")")" = "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-9")")" = "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-10")")" = "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF" &&
+       test "$(test-urlmatch-normalization -p "$(cat "$tu-11")")" = "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD"
+'
+
+test_expect_success 'url username/password escapes' '
+       test "$(test-urlmatch-normalization -p "x://%41%62(^):%70+d@foo")" = "x://Ab(%5E):p+d@foo/"
+'
+
+test_expect_success 'url normalized lengths' '
+       test "$(test-urlmatch-normalization -l "Http://%4d%65:%4d^%70@The.Host")" = 25 &&
+       test "$(test-urlmatch-normalization -l "http://%41:%42@x.y/%61/")" = 17 &&
+       test "$(test-urlmatch-normalization -l "http://@x.y/^")" = 15
+'
+
+test_expect_success 'url . and .. segments' '
+       test "$(test-urlmatch-normalization -p "x://y/.")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/./")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/.")" = "x://y/a" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./")" = "x://y/a/" &&
+       test "$(test-urlmatch-normalization -p "x://y/.?")" = "x://y/?" &&
+       test "$(test-urlmatch-normalization -p "x://y/./?")" = "x://y/?" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/.?")" = "x://y/a?" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./?")" = "x://y/a/?" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c")" = "x://y/c" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./b/../.././c/")" = "x://y/c/" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c/././.././.")" = "x://y/" &&
+       ! test-urlmatch-normalization "x://y/a/./b/.././../c/././.././.." &&
+       test "$(test-urlmatch-normalization -p "x://y/a/./?/././..")" = "x://y/a/?/././.." &&
+       test "$(test-urlmatch-normalization -p "x://y/%2e/")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/%2E/")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/a/%2e./")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/b/.%2E/")" = "x://y/" &&
+       test "$(test-urlmatch-normalization -p "x://y/c/%2e%2E/")" = "x://y/"
+'
+
+# http://@foo specifies an empty user name but does not specify a password
+# http://foo  specifies neither a user name nor a password
+# So they should not be equivalent
+test_expect_success 'url equivalents' '
+       test-urlmatch-normalization "httP://x" "Http://X/" &&
+       test-urlmatch-normalization "Http://%4d%65:%4d^%70@The.Host" "hTTP://Me:%4D^p@the.HOST:80/" &&
+       ! test-urlmatch-normalization "https://@x.y/^" "httpS://x.y:443/^" &&
+       test-urlmatch-normalization "https://@x.y/^" "httpS://@x.y:0443/^" &&
+       test-urlmatch-normalization "https://@x.y/^/../abc" "httpS://@x.y:0443/abc" &&
+       test-urlmatch-normalization "https://@x.y/^/.." "httpS://@x.y:0443/"
+'
+
+test_done
diff --git a/t/t0110/README b/t/t0110/README
new file mode 100644 (file)
index 0000000..ad4a50e
--- /dev/null
@@ -0,0 +1,9 @@
+The url data files in this directory contain URLs with characters
+in the range 0x01-0x1f and 0x7f-0xff to test the proper normalization
+of unprintable characters.
+
+A select few characters in the 0x01-0x1f range are skipped to help
+avoid problems running the test itself.
+
+The urls are in test files in this directory rather than being
+embedded in the test script for portability.
diff --git a/t/t0110/url-1 b/t/t0110/url-1
new file mode 100644 (file)
index 0000000..519019c
Binary files /dev/null and b/t/t0110/url-1 differ
diff --git a/t/t0110/url-10 b/t/t0110/url-10
new file mode 100644 (file)
index 0000000..b9965de
Binary files /dev/null and b/t/t0110/url-10 differ
diff --git a/t/t0110/url-11 b/t/t0110/url-11
new file mode 100644 (file)
index 0000000..f0a50f1
Binary files /dev/null and b/t/t0110/url-11 differ
diff --git a/t/t0110/url-2 b/t/t0110/url-2
new file mode 100644 (file)
index 0000000..43334b0
Binary files /dev/null and b/t/t0110/url-2 differ
diff --git a/t/t0110/url-3 b/t/t0110/url-3
new file mode 100644 (file)
index 0000000..7378c7b
Binary files /dev/null and b/t/t0110/url-3 differ
diff --git a/t/t0110/url-4 b/t/t0110/url-4
new file mode 100644 (file)
index 0000000..220b198
Binary files /dev/null and b/t/t0110/url-4 differ
diff --git a/t/t0110/url-5 b/t/t0110/url-5
new file mode 100644 (file)
index 0000000..1ccd927
Binary files /dev/null and b/t/t0110/url-5 differ
diff --git a/t/t0110/url-6 b/t/t0110/url-6
new file mode 100644 (file)
index 0000000..e8283aa
Binary files /dev/null and b/t/t0110/url-6 differ
diff --git a/t/t0110/url-7 b/t/t0110/url-7
new file mode 100644 (file)
index 0000000..fa7c10b
Binary files /dev/null and b/t/t0110/url-7 differ
diff --git a/t/t0110/url-8 b/t/t0110/url-8
new file mode 100644 (file)
index 0000000..79a0ba8
Binary files /dev/null and b/t/t0110/url-8 differ
diff --git a/t/t0110/url-9 b/t/t0110/url-9
new file mode 100644 (file)
index 0000000..8b44bec
Binary files /dev/null and b/t/t0110/url-9 differ
index 4e911fb43d8ea2c699b3a836547b618274db6e05..a420742494024e127d4e4233153c7054e077e471 100755 (executable)
@@ -78,6 +78,13 @@ $content"
        echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
+
+    test_expect_success '--batch-check with %(rest)' '
+       echo "$type this is some extra content" >expect &&
+       echo "$sha1    this is some extra content" |
+               git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
+       test_cmp expect actual
+    '
 }
 
 hello_content="Hello World"
@@ -91,6 +98,14 @@ test_expect_success "setup" '
 
 run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
 
+test_expect_success '--batch-check without %(rest) considers whole line' '
+       echo "$hello_sha1 blob $hello_size" >expect &&
+       git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+       test_when_finished "git update-index --remove \"white space\"" &&
+       echo ":white space" | git cat-file --batch-check >actual &&
+       test_cmp expect actual
+'
+
 tree_sha1=$(git write-tree)
 tree_size=33
 tree_pretty_content="100644 blob $hello_sha1   hello"
index c4a7d84f46fd218af1824ac1f78a1e9f4cb15cec..967359344dab8118dfd55816ef08f4809a9f33ad 100755 (executable)
@@ -652,16 +652,23 @@ test_expect_success numbers '
        test_cmp expect actual
 '
 
+test_expect_success '--int is at least 64 bits' '
+       git config giga.watts 121g &&
+       echo 129922760704 >expect &&
+       git config --int --get giga.watts >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'invalid unit' '
        git config aninvalid.unit "1auto" &&
        echo 1auto >expect &&
        git config aninvalid.unit >actual &&
        test_cmp expect actual &&
-       cat > expect <<-\EOF
-       fatal: bad config value for '\''aninvalid.unit'\'' in .git/config
+       cat >expect <<-\EOF
+       fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
        EOF
        test_must_fail git config --int --get aninvalid.unit 2>actual &&
-       test_cmp actual expect
+       test_i18ncmp expect actual
 '
 
 cat > expect << EOF
@@ -1087,6 +1094,31 @@ test_expect_success 'barf on incomplete string' '
        grep " line 3 " error
 '
 
+test_expect_success 'urlmatch' '
+       cat >.git/config <<-\EOF &&
+       [http]
+               sslVerify
+       [http "https://weak.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+       EOF
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo false >expect &&
+       git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo http.cookiefile /tmp/cookie.txt &&
+               echo http.sslverify false
+       } >expect &&
+       git config --get-urlmatch HTTP https://weak.example.com >actual &&
+       test_cmp expect actual
+'
+
 # good section hygiene
 test_expect_failure 'unsetting the last key in a section removes header' '
        cat >.git/config <<-\EOF &&
index 9a105fe21f0da2851fc0f2aae4ff10e2417c9842..6f47c0dd0ec9b5e59205ca98eea9bd729605e004 100755 (executable)
@@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' '
        test_cmp expect actual
 '
 
+# This guards against the alternative of showing the diffs vs. the
+# reflog ancestor.  The reflog used is designed to list the commits
+# more than once, so as to exercise the corresponding logic.
+test_expect_success 'git log -g -p shows diffs vs. parents' '
+       test_commit two &&
+       git branch flipflop &&
+       git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
+       git update-ref refs/heads/flipflop -m flop1 HEAD &&
+       git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
+       git log -g -p flipflop >reflog &&
+       grep -v ^Reflog reflog >actual &&
+       git log -1 -p HEAD^ >log.one &&
+       git log -1 -p HEAD >log.two &&
+       (
+               cat log.one; echo
+               cat log.two; echo
+               cat log.one; echo
+               cat log.two
+       ) >expect &&
+       test_cmp expect actual
+'
+
 test_done
index f611d799b690457e306571adf16813b238a27dc9..6d3b828a951e4c03b886087393269dbe4d5c5c8f 100755 (executable)
@@ -11,6 +11,7 @@ This test prepares the following in the cache:
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    pathx/ju    - a file in a directory
     submod1/   - a submodule
     submod2/   - another submodule
 
@@ -23,6 +24,7 @@ and the following on the filesystem:
     path4      - a file
     path5      - a symlink
     path6/file6 - a file in a directory
+    pathx/ju/nk - a file in a directory to be killed
     submod1/   - a submodule (modified from the cache)
     submod2/   - a submodule (matches the cache)
 
@@ -44,14 +46,15 @@ modified without reporting path9 and path10.  submod1 is also modified.
 test_expect_success 'git update-index --add to add various paths.' '
        date >path0 &&
        test_ln_s_add xyzzy path1 &&
-       mkdir path2 path3 &&
+       mkdir path2 path3 pathx &&
        date >path2/file2 &&
        date >path3/file3 &&
+       >pathx/ju &&
        : >path7 &&
        date >path8 &&
        : >path9 &&
        date >path10 &&
-       git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+       git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 &&
        for i in 1 2
        do
                git init submod$i &&
@@ -77,7 +80,7 @@ test_expect_success 'git ls-files -k to show killed files.' '
                date >path3 &&
                date >path5
        fi &&
-       mkdir path0 path1 path6 &&
+       mkdir -p path0 path1 path6 pathx/ju &&
        date >path0/file0 &&
        date >path1/file1 &&
        date >path6/file6 &&
@@ -85,16 +88,23 @@ test_expect_success 'git ls-files -k to show killed files.' '
        : >path8 &&
        : >path9 &&
        touch path10 &&
-       git ls-files -k >.output
-'
-
-test_expect_success 'validate git ls-files -k output.' '
-       cat >.expected <<-\EOF &&
+       >pathx/ju/nk &&
+       cat >.expected <<-\EOF
        path0/file0
        path1/file1
        path2
        path3
+       pathx/ju/nk
        EOF
+'
+
+test_expect_success 'git ls-files -k output (w/o icase)' '
+       git ls-files -k >.output
+       test_cmp .expected .output
+'
+
+test_expect_success 'git ls-files -k output (w/ icase)' '
+       git -c core.ignorecase=true ls-files -k >.output
        test_cmp .expected .output
 '
 
@@ -110,6 +120,7 @@ test_expect_success 'validate git ls-files -m output.' '
        path3/file3
        path7
        path8
+       pathx/ju
        submod1
        EOF
        test_cmp .expected .output
index 49ccb38f8856e6e5fa00961c6e9117f94c6ae233..50e22b1cadff4252dfb552a4db930f49544952aa 100755 (executable)
@@ -29,8 +29,6 @@ Initial setup:
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
-set_fake_editor
-
 # WARNING: Modifications to the initial repository can change the SHA ID used
 # in the expect2 file for the 'stop on conflicting pick' test.
 
@@ -72,6 +70,7 @@ export SHELL
 test_expect_success 'rebase -i with the exec command' '
        git checkout master &&
        (
+       set_fake_editor &&
        FAKE_LINES="1 exec_>touch-one
                2 exec_>touch-two exec_false exec_>touch-three
                3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
@@ -93,6 +92,7 @@ test_expect_success 'rebase -i with the exec command' '
 test_expect_success 'rebase -i with the exec command runs from tree root' '
        git checkout master &&
        mkdir subdir && (cd subdir &&
+       set_fake_editor &&
        FAKE_LINES="1 exec_>touch-subdir" \
                git rebase -i HEAD^
        ) &&
@@ -103,6 +103,7 @@ test_expect_success 'rebase -i with the exec command runs from tree root' '
 test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        git checkout master &&
        (
+       set_fake_editor &&
        FAKE_LINES="exec_echo_foo_>file1 1" &&
        export FAKE_LINES &&
        test_must_fail git rebase -i HEAD^
@@ -116,6 +117,7 @@ test_expect_success 'rebase -i with exec of inexistent command' '
        git checkout master &&
        test_when_finished "git rebase --abort" &&
        (
+       set_fake_editor &&
        FAKE_LINES="exec_this-command-does-not-exist 1" &&
        export FAKE_LINES &&
        test_must_fail git rebase -i HEAD^ >actual 2>&1
@@ -125,6 +127,7 @@ test_expect_success 'rebase -i with exec of inexistent command' '
 
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
+       set_fake_editor &&
        git rebase -i F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -134,6 +137,7 @@ test_expect_success 'test the [branch] option' '
        git checkout -b dead-end &&
        git rm file6 &&
        git commit -m "stop here" &&
+       set_fake_editor &&
        git rebase -i F branch2 &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse branch2) &&
@@ -142,6 +146,7 @@ test_expect_success 'test the [branch] option' '
 
 test_expect_success 'test --onto <branch>' '
        git checkout -b test-onto branch2 &&
+       set_fake_editor &&
        git rebase -i --onto branch1 F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
        test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
@@ -151,6 +156,7 @@ test_expect_success 'test --onto <branch>' '
 test_expect_success 'rebase on top of a non-conflicting commit' '
        git checkout branch1 &&
        git tag original-branch1 &&
+       set_fake_editor &&
        git rebase -i branch2 &&
        test file6 = $(git diff --name-only original-branch1) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
@@ -163,6 +169,7 @@ test_expect_success 'reflog for the branch shows state before rebase' '
 '
 
 test_expect_success 'exchange two commits' '
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -188,6 +195,7 @@ EOF
 
 test_expect_success 'stop on conflicting pick' '
        git tag new-branch1 &&
+       set_fake_editor &&
        test_must_fail git rebase -i master &&
        test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
        test_cmp expect .git/rebase-merge/patch &&
@@ -208,6 +216,7 @@ test_expect_success 'abort' '
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
+       set_fake_editor &&
        test_must_fail git rebase -i master > output 2>&1 &&
        grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
@@ -222,6 +231,7 @@ test_expect_success 'retain authorship' '
        test_tick &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
        git tag twerp &&
+       set_fake_editor &&
        git rebase -i --onto master HEAD^ &&
        git show HEAD | grep "^Author: Twerp Snog"
 '
@@ -232,6 +242,7 @@ test_expect_success 'squash' '
        test_tick &&
        GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
        echo "******************************" &&
+       set_fake_editor &&
        FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
                git rebase -i --onto master HEAD~2 &&
        test B = $(cat file7) &&
@@ -244,6 +255,7 @@ test_expect_success 'retain authorship when squashing' '
 
 test_expect_success '-p handles "no changes" gracefully' '
        HEAD=$(git rev-parse HEAD) &&
+       set_fake_editor &&
        git rebase -i -p HEAD^ &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -253,6 +265,7 @@ test_expect_success '-p handles "no changes" gracefully' '
 
 test_expect_failure 'exchange two commits with -p' '
        git checkout H &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -287,6 +300,7 @@ test_expect_success 'preserve merges with -p' '
        git commit -m M file1 &&
        git checkout -b to-be-rebased &&
        test_tick &&
+       set_fake_editor &&
        git rebase -i -p --onto branch1 master &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -301,6 +315,7 @@ test_expect_success 'preserve merges with -p' '
 '
 
 test_expect_success 'edit ancestor with -p' '
+       set_fake_editor &&
        FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
        echo 2 > unrelated-file &&
        test_tick &&
@@ -314,6 +329,7 @@ test_expect_success 'edit ancestor with -p' '
 
 test_expect_success '--continue tries to commit' '
        test_tick &&
+       set_fake_editor &&
        test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
        git add file1 &&
@@ -325,6 +341,7 @@ test_expect_success '--continue tries to commit' '
 test_expect_success 'verbose flag is heeded, even after --continue' '
        git reset --hard master@{1} &&
        test_tick &&
+       set_fake_editor &&
        test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
        git add file1 &&
@@ -334,6 +351,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
 
 test_expect_success 'multi-squash only fires up editor once' '
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -344,6 +362,7 @@ test_expect_success 'multi-squash only fires up editor once' '
 test_expect_success 'multi-fixup does not fire up editor' '
        git checkout -b multi-fixup E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
                git rebase -i $base &&
        test $base = $(git rev-parse HEAD^) &&
@@ -355,6 +374,7 @@ test_expect_success 'multi-fixup does not fire up editor' '
 test_expect_success 'commit message used after conflict' '
        git checkout -b conflict-fixup conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 fixup 3 fixup 4" &&
                export FAKE_LINES &&
@@ -373,6 +393,7 @@ test_expect_success 'commit message used after conflict' '
 test_expect_success 'commit message retained after conflict' '
        git checkout -b conflict-squash conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 fixup 3 squash 4" &&
                export FAKE_LINES &&
@@ -399,6 +420,7 @@ EOF
 test_expect_success 'squash and fixup generate correct log messages' '
        git checkout -b squash-fixup E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -411,6 +433,7 @@ test_expect_success 'squash and fixup generate correct log messages' '
 test_expect_success 'squash ignores comments' '
        git checkout -b skip-comments E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -423,6 +446,7 @@ test_expect_success 'squash ignores comments' '
 test_expect_success 'squash ignores blank lines' '
        git checkout -b skip-blank-lines E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -435,6 +459,7 @@ test_expect_success 'squash ignores blank lines' '
 test_expect_success 'squash works as expected' '
        git checkout -b squash-works no-conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
                git rebase -i HEAD~3 &&
        test $one = $(git rev-parse HEAD~2)
@@ -443,6 +468,7 @@ test_expect_success 'squash works as expected' '
 test_expect_success 'interrupted squash works as expected' '
        git checkout -b interrupted-squash conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 squash 3 2" &&
                export FAKE_LINES &&
@@ -460,6 +486,7 @@ test_expect_success 'interrupted squash works as expected' '
 test_expect_success 'interrupted squash works as expected (case 2)' '
        git checkout -b interrupted-squash2 conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        (
                FAKE_LINES="3 squash 1 2" &&
                export FAKE_LINES &&
@@ -484,6 +511,7 @@ test_expect_success '--continue tries to commit, even for "edit"' '
        git commit -m "unrelated change" &&
        parent=$(git rev-parse HEAD^) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo edited > file7 &&
        git add file7 &&
@@ -496,6 +524,7 @@ test_expect_success '--continue tries to commit, even for "edit"' '
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
        old=$(git rev-parse HEAD) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
@@ -510,6 +539,7 @@ test_expect_success 'aborted --continue does not squash commits after "edit"' '
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
@@ -528,6 +558,7 @@ test_expect_success 'auto-amend only edited commits after "edit"' '
 test_expect_success 'clean error after failed "exec"' '
        test_tick &&
        test_when_finished "git rebase --abort || :" &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 exec_false" &&
                export FAKE_LINES &&
@@ -543,6 +574,7 @@ test_expect_success 'rebase a detached HEAD' '
        grandparent=$(git rev-parse HEAD~2) &&
        git checkout $(git rev-parse HEAD) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 &&
        test $grandparent = $(git rev-parse HEAD~2)
 '
@@ -559,6 +591,7 @@ test_expect_success 'rebase a commit violating pre-commit' '
        test_must_fail git commit -m doesnt-verify file1 &&
        git commit -m doesnt-verify --no-verify file1 &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES=2 git rebase -i HEAD~2
 
 '
@@ -580,6 +613,7 @@ test_expect_success 'rebase with a file named HEAD in worktree' '
                git commit -m "Add body"
        ) &&
 
+       set_fake_editor &&
        FAKE_LINES="1 squash 2" git rebase -i to-be-rebased &&
        test "$(git show -s --pretty=format:%an)" = "Squashed Away"
 
@@ -591,6 +625,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' '
        GIT_EDITOR=: git commit --amend \
                --author="Somebody else <somebody@else.com>" &&
        test $(git rev-parse branch3) != $(git rev-parse branch4) &&
+       set_fake_editor &&
        git rebase -i branch3 &&
        test $(git rev-parse branch3) = $(git rev-parse branch4)
 
@@ -615,10 +650,12 @@ test_expect_success 'submodule rebase setup' '
                git commit -a -m "submodule second"
        ) &&
        test_tick &&
+       set_fake_editor &&
        git commit -a -m "Three changes submodule"
 '
 
 test_expect_success 'submodule rebase -i' '
+       set_fake_editor &&
        FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
@@ -636,6 +673,7 @@ test_expect_success 'submodule conflict setup' '
 '
 
 test_expect_success 'rebase -i continue with only submodule staged' '
+       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git add sub &&
        git rebase --continue &&
@@ -645,6 +683,7 @@ test_expect_success 'rebase -i continue with only submodule staged' '
 test_expect_success 'rebase -i continue with unstaged submodule' '
        git checkout submodule-topic &&
        git reset --hard &&
+       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git reset &&
        git rebase --continue &&
@@ -657,6 +696,7 @@ test_expect_success 'avoid unnecessary reset' '
        test-chmtime =123456789 file3 &&
        git update-index --refresh &&
        HEAD=$(git rev-parse HEAD) &&
+       set_fake_editor &&
        git rebase -i HEAD~4 &&
        test $HEAD = $(git rev-parse HEAD) &&
        MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
@@ -665,6 +705,7 @@ test_expect_success 'avoid unnecessary reset' '
 
 test_expect_success 'reword' '
        git checkout -b reword-branch master &&
+       set_fake_editor &&
        FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
        git show HEAD | grep "E changed" &&
        test $(git rev-parse master) != $(git rev-parse HEAD) &&
@@ -684,6 +725,7 @@ test_expect_success 'rebase -i can copy notes' '
        test_commit n2 &&
        test_commit n3 &&
        git notes add -m"a note" n3 &&
+       set_fake_editor &&
        git rebase -i --onto n1 n2 &&
        test "a note" = "$(git notes show HEAD)"
 '
@@ -697,6 +739,7 @@ EOF
 test_expect_success 'rebase -i can copy notes over a fixup' '
        git reset --hard n3 &&
        git notes add -m"an earlier note" n2 &&
+       set_fake_editor &&
        GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
        git notes show > output &&
        test_cmp expect output
@@ -706,6 +749,7 @@ test_expect_success 'rebase while detaching HEAD' '
        git symbolic-ref HEAD &&
        grandparent=$(git rev-parse HEAD~2) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
        test $grandparent = $(git rev-parse HEAD~2) &&
        test_must_fail git symbolic-ref HEAD
@@ -715,6 +759,7 @@ test_tick # Ensure that the rebased commits get a different timestamp.
 test_expect_success 'always cherry-pick with --no-ff' '
        git checkout no-ff-branch &&
        git tag original-no-ff-branch &&
+       set_fake_editor &&
        git rebase -i --no-ff A &&
        touch empty &&
        for p in 0 1 2
@@ -747,6 +792,7 @@ test_expect_success 'set up commits with funny messages' '
 test_expect_success 'rebase-i history with funny messages' '
        git rev-list A..funny >expect &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="1 2 3 4" git rebase -i A &&
        git rev-list A.. >actual &&
        test_cmp expect actual
@@ -763,6 +809,7 @@ test_expect_success 'prepare for rebase -i --exec' '
 
 
 test_expect_success 'running "git rebase -i --exec git show HEAD"' '
+       set_fake_editor &&
        git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -776,6 +823,7 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' '
 
 test_expect_success 'running "git rebase --exec git show HEAD -i"' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -789,6 +837,7 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' '
 
 test_expect_success 'running "git rebase -ix git show HEAD"' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -ix "git show HEAD" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -802,6 +851,7 @@ test_expect_success 'running "git rebase -ix git show HEAD"' '
 
 test_expect_success 'rebase -ix with several <CMD>' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
@@ -815,6 +865,7 @@ test_expect_success 'rebase -ix with several <CMD>' '
 
 test_expect_success 'rebase -ix with several instances of --exec' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
@@ -836,6 +887,7 @@ test_expect_success 'rebase -ix with --autosquash' '
        echo bis >bis.txt &&
        git add bis.txt &&
        git commit -m "fixup! two_exec" &&
+       set_fake_editor &&
        (
                git checkout -b autosquash_actual &&
                git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
@@ -854,6 +906,7 @@ test_expect_success 'rebase -ix with --autosquash' '
 
 test_expect_success 'rebase --exec without -i shows error message' '
        git reset --hard execute &&
+       set_fake_editor &&
        test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
        echo "The --exec option must be used with the --interactive option" >expected &&
        test_i18ncmp expected actual
@@ -862,6 +915,7 @@ test_expect_success 'rebase --exec without -i shows error message' '
 
 test_expect_success 'rebase -i --exec without <CMD>' '
        git reset --hard execute &&
+       set_fake_editor &&
        test_must_fail git rebase -i --exec 2>tmp &&
        sed -e "1d" tmp >actual &&
        test_must_fail git rebase -h >expected &&
@@ -871,6 +925,7 @@ test_expect_success 'rebase -i --exec without <CMD>' '
 
 test_expect_success 'rebase -i --root re-order and drop commits' '
        git checkout E &&
+       set_fake_editor &&
        FAKE_LINES="3 1 2 5" git rebase -i --root &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
@@ -884,6 +939,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' '
        echo B >file7 &&
        git add file7 &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+       set_fake_editor &&
        FAKE_LINES="2" git rebase -i --root &&
        git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
        git cat-file commit HEAD | grep -q "^different author$"
@@ -892,6 +948,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' '
 test_expect_success 'rebase -i --root temporary sentinel commit' '
        git checkout B &&
        (
+               set_fake_editor &&
                FAKE_LINES="2" &&
                export FAKE_LINES &&
                test_must_fail git rebase -i --root
@@ -902,6 +959,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '
 
 test_expect_success 'rebase -i --root fixup root commit' '
        git checkout B &&
+       set_fake_editor &&
        FAKE_LINES="1 fixup 2" git rebase -i --root &&
        test A = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git show HEAD:file1) &&
@@ -911,6 +969,7 @@ test_expect_success 'rebase -i --root fixup root commit' '
 test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' '
        git reset --hard &&
        git checkout conflict-branch &&
+       set_fake_editor &&
        test_must_fail git rebase --onto HEAD~2 HEAD~ &&
        test_must_fail git rebase --edit-todo &&
        git rebase --abort
@@ -919,6 +978,7 @@ test_expect_success 'rebase --edit-todo does not works on non-interactive rebase
 test_expect_success 'rebase --edit-todo can be used to modify todo' '
        git reset --hard &&
        git checkout no-conflict-branch^0 &&
+       set_fake_editor &&
        FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
        FAKE_LINES="2 1" git rebase --edit-todo &&
        git rebase --continue
@@ -929,6 +989,7 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
 test_expect_success 'rebase -i produces readable reflog' '
        git reset --hard &&
        git branch -f branch-reflog-test H &&
+       set_fake_editor &&
        git rebase -i --onto I F branch-reflog-test &&
        cat >expect <<-\EOF &&
        rebase -i (start): checkout I
@@ -976,4 +1037,41 @@ test_expect_success 'rebase -i with --strategy and -X' '
        test $(cat file1) = Z
 '
 
+test_expect_success 'rebase -i error on commits with \ in message' '
+       current_head=$(git rev-parse HEAD)
+       test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&
+       test_commit TO-REMOVE will-conflict old-content &&
+       test_commit "\temp" will-conflict new-content dummy &&
+       (
+       EDITOR=true &&
+       export EDITOR &&
+       test_must_fail git rebase -i HEAD^ --onto HEAD^^ 2>error
+       ) &&
+       test_expect_code 1 grep  "      emp" error
+'
+
+test_expect_success 'short SHA-1 setup' '
+       test_when_finished "git checkout master" &&
+       git checkout --orphan collide &&
+       git rm -rf . &&
+       (
+       unset test_tick &&
+       test_commit collide1 collide &&
+       test_commit --notick collide2 collide &&
+       test_commit --notick collide3 collide
+       )
+'
+
+test_expect_success 'short SHA-1 collide' '
+       test_when_finished "reset_rebase && git checkout master" &&
+       git checkout collide &&
+       (
+       unset test_tick &&
+       test_tick &&
+       set_fake_editor &&
+       FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+       FAKE_LINES="reword 1 2" git rebase -i HEAD~2
+       )
+'
+
 test_done
index 2e0c36415fe525663fdcc64675fd081c642732ed..8c251c57a6827317a9c17fac2a0e99df3c9becf8 100755 (executable)
@@ -28,6 +28,8 @@ export GIT_AUTHOR_EMAIL
 #     \--A3    <-- topic2
 #      \
 #       B2     <-- origin/topic
+#
+# Clone 4 (same as Clone 3)
 
 test_expect_success 'setup for merge-preserving rebase' \
        'echo First > A &&
@@ -64,6 +66,16 @@ test_expect_success 'setup for merge-preserving rebase' \
                git merge --no-ff topic2
        ) &&
 
+       git clone ./. clone4 &&
+       (
+               cd clone4 &&
+               git checkout -b topic2 origin/topic &&
+               echo Sixth > A &&
+               git commit -a -m "Modify A3" &&
+               git checkout -b topic origin/topic &&
+               git merge --no-ff topic2
+       ) &&
+
        git checkout topic &&
        echo Fourth >> B &&
        git commit -a -m "Modify B2"
@@ -96,4 +108,15 @@ test_expect_success 'rebase -p preserves no-ff merges' '
        )
 '
 
+test_expect_success 'rebase -p ignores merge.log config' '
+       (
+       cd clone4 &&
+       git fetch &&
+       git -c merge.log=1 rebase -p origin/topic &&
+       echo >expected &&
+       git log --format="%b" -1 >current &&
+       test_cmp expected current
+       )
+'
+
 test_done
index 6f489e20eecb48c6e1a2dc2b1fe90e47e7b3e7ad..46aaf2f511f6d37e8552d0b12fa64f7e30accd1d 100755 (executable)
@@ -100,7 +100,7 @@ test_expect_success 'revert forbidden on dirty working tree' '
 
 '
 
-test_expect_success 'chery-pick on unborn branch' '
+test_expect_success 'cherry-pick on unborn branch' '
        git checkout --orphan unborn &&
        git rm --cached -r . &&
        rm -rf * &&
index 373aad623c8f02f6f9753eb38c6393a69c027001..fb889ac6f05a3243b09fbb7ec05356ed61ff9cb0 100755 (executable)
@@ -105,7 +105,7 @@ test_expect_success 'cherry pick a root commit with --ff' '
        test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
 '
 
-test_expect_success 'chery-pick --ff on unborn branch' '
+test_expect_success 'cherry-pick --ff on unborn branch' '
        git checkout --orphan unborn &&
        git rm --cached -r . &&
        rm -rf * &&
index a5b6a5f3310316d289aee0961fcb6a2ae26fca99..1e5b3948dfc5d28a070ba83db361163002e7c3ae 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success 'Setup rename with file on one side matching different dirna
        echo content > sub/file &&
        echo foo > othersub/whatever &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git rm -rf othersub &&
        git mv sub/file othersub &&
index 5c87b55645e249929585c2ed0e7bcf98a1b62da3..639cb70941078cbf10fb0653ac34dd511941f909 100755 (executable)
@@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' '
 '
 
 cat >expect <<EOF
+M  .gitmodules
 D  submod
 EOF
 
@@ -270,6 +271,15 @@ cat >expect.modified <<EOF
  M submod
 EOF
 
+cat >expect.cached <<EOF
+D  submod
+EOF
+
+cat >expect.both_deleted<<EOF
+D  .gitmodules
+D  submod
+EOF
+
 test_expect_success 'rm removes empty submodules from work tree' '
        mkdir submod &&
        git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod &&
@@ -281,16 +291,20 @@ test_expect_success 'rm removes empty submodules from work tree' '
        git rm submod &&
        test ! -e submod &&
        git status -s -uno --ignore-submodules=none > actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
 '
 
-test_expect_success 'rm removes removed submodule from index' '
+test_expect_success 'rm removes removed submodule from index and .gitmodules' '
        git reset --hard &&
        git submodule update &&
        rm -rf submod &&
        git rm submod &&
        git status -s -uno --ignore-submodules=none > actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
 '
 
 test_expect_success 'rm removes work tree of unmodified submodules' '
@@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' '
        git rm submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none > actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
 '
 
 test_expect_success 'rm removes a submodule with a trailing /' '
@@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none > actual &&
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' '
+       git reset --hard &&
+       git submodule update &&
+       git rm --cached submod &&
+       test -d submod &&
+       test -f submod/.git &&
+       git status -s -uno >actual &&
+       test_cmp expect.cached actual &&
+       git config -f .gitmodules submodule.sub.url &&
+       git config -f .gitmodules submodule.sub.path
+'
+
+test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '
+       git reset --hard &&
+       git submodule update &&
+       git rm -n submod &&
+       test -f submod/.git &&
+       git diff-index --exit-code HEAD
+'
+
+test_expect_success 'rm does not complain when no .gitmodules file is found' '
+       git reset --hard &&
+       git submodule update &&
+       git rm .gitmodules &&
+       git rm submod >actual 2>actual.err &&
+       ! test -s actual.err &&
+       ! test -d submod &&
+       ! test -f submod/.git &&
+       git status -s -uno >actual &&
+       test_cmp expect.both_deleted actual
+'
+
+test_expect_success 'rm will error out on a modified .gitmodules file unless staged' '
+       git reset --hard &&
+       git submodule update &&
+       git config -f .gitmodules foo.bar true &&
+       test_must_fail git rm submod >actual 2>actual.err &&
+       test -s actual.err &&
+       test -d submod &&
+       test -f submod/.git &&
+       git diff-files --quiet -- submod &&
+       git add .gitmodules &&
+       git rm submod >actual 2>actual.err &&
+       ! test -s actual.err &&
+       ! test -d submod &&
+       ! test -f submod/.git &&
+       git status -s -uno >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rm issues a warning when section is not found in .gitmodules' '
+       git reset --hard &&
+       git submodule update &&
+       git config -f .gitmodules --remove-section submodule.sub &&
+       git add .gitmodules &&
+       echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
+       git rm submod >actual 2>actual.err &&
+       test_i18ncmp expect.err actual.err &&
+       ! test -d submod &&
+       ! test -f submod/.git &&
+       git status -s -uno >actual &&
        test_cmp expect actual
 '
 
@@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none > actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
 '
 
 test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' '
@@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none > actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       test_must_fail git config -f .gitmodules submodule.sub.url &&
+       test_must_fail git config -f .gitmodules submodule.sub.path
 '
 
 test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' '
index 9fab25cc96b079349a3ca3caffbd8d2bc322265c..9dc91d09d7ef3c66c39bcf930bb486312cf291af 100755 (executable)
@@ -4,18 +4,24 @@ test_description='add -i basic tests'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
-test_expect_success PERL 'setup (initial)' '
+if ! test_have_prereq PERL
+then
+       skip_all='skipping add -i tests, perl not available'
+       test_done
+fi
+
+test_expect_success 'setup (initial)' '
        echo content >file &&
        git add file &&
        echo more >>file &&
        echo lines >>file
 '
-test_expect_success PERL 'status works (initial)' '
+test_expect_success 'status works (initial)' '
        git add -i </dev/null >output &&
        grep "+1/-0 *+2/-0 file" output
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 new file mode 100644
 index 0000000..d95f3ad
@@ -26,19 +32,19 @@ index 0000000..d95f3ad
 EOF
 '
 
-test_expect_success PERL 'diff works (initial)' '
+test_expect_success 'diff works (initial)' '
        (echo d; echo 1) | git add -i >output &&
        sed -ne "/new file/,/content/p" <output >diff &&
        test_cmp expected diff
 '
-test_expect_success PERL 'revert works (initial)' '
+test_expect_success 'revert works (initial)' '
        git add file &&
        (echo r; echo 1) | git add -i &&
        git ls-files >output &&
        ! grep . output
 '
 
-test_expect_success PERL 'setup (commit)' '
+test_expect_success 'setup (commit)' '
        echo baseline >file &&
        git add file &&
        git commit -m commit &&
@@ -47,12 +53,12 @@ test_expect_success PERL 'setup (commit)' '
        echo more >>file &&
        echo lines >>file
 '
-test_expect_success PERL 'status works (commit)' '
+test_expect_success 'status works (commit)' '
        git add -i </dev/null >output &&
        grep "+1/-0 *+2/-0 file" output
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -63,12 +69,12 @@ index 180b47c..b6f2c08 100644
 EOF
 '
 
-test_expect_success PERL 'diff works (commit)' '
+test_expect_success 'diff works (commit)' '
        (echo d; echo 1) | git add -i >output &&
        sed -ne "/^index/,/content/p" <output >diff &&
        test_cmp expected diff
 '
-test_expect_success PERL 'revert works (commit)' '
+test_expect_success 'revert works (commit)' '
        git add file &&
        (echo r; echo 1) | git add -i &&
        git add -i </dev/null >output &&
@@ -76,24 +82,24 @@ test_expect_success PERL 'revert works (commit)' '
 '
 
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 EOF
 '
 
-test_expect_success PERL 'setup fake editor' '
+test_expect_success 'setup fake editor' '
        >fake_editor.sh &&
        chmod a+x fake_editor.sh &&
        test_set_editor "$(pwd)/fake_editor.sh"
 '
 
-test_expect_success PERL 'dummy edit works' '
+test_expect_success 'dummy edit works' '
        (echo e; echo a) | git add -p &&
        git diff > diff &&
        test_cmp expected diff
 '
 
-test_expect_success PERL 'setup patch' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 @@ -1,1 +1,4 @@
  this
@@ -103,7 +109,7 @@ cat >patch <<EOF
 EOF
 '
 
-test_expect_success PERL 'setup fake editor' '
+test_expect_success 'setup fake editor' '
        echo "#!$SHELL_PATH" >fake_editor.sh &&
        cat >>fake_editor.sh <<\EOF &&
 mv -f "$1" oldpatch &&
@@ -113,26 +119,26 @@ EOF
        test_set_editor "$(pwd)/fake_editor.sh"
 '
 
-test_expect_success PERL 'bad edit rejected' '
+test_expect_success 'bad edit rejected' '
        git reset &&
        (echo e; echo n; echo d) | git add -p >output &&
        grep "hunk does not apply" output
 '
 
-test_expect_success PERL 'setup patch' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 this patch
 is garbage
 EOF
 '
 
-test_expect_success PERL 'garbage edit rejected' '
+test_expect_success 'garbage edit rejected' '
        git reset &&
        (echo e; echo n; echo d) | git add -p >output &&
        grep "hunk does not apply" output
 '
 
-test_expect_success PERL 'setup patch' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 @@ -1,0 +1,0 @@
  baseline
@@ -142,7 +148,7 @@ cat >patch <<EOF
 EOF
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b5dd6c9..f910ae9 100644
@@ -157,13 +163,13 @@ index b5dd6c9..f910ae9 100644
 EOF
 '
 
-test_expect_success PERL 'real edit works' '
+test_expect_success 'real edit works' '
        (echo e; echo n; echo d) | git add -p &&
        git diff >output &&
        test_cmp expected output
 '
 
-test_expect_success PERL 'skip files similarly as commit -a' '
+test_expect_success 'skip files similarly as commit -a' '
        git reset &&
        echo file >.gitignore &&
        echo changed >file &&
@@ -177,7 +183,7 @@ test_expect_success PERL 'skip files similarly as commit -a' '
 '
 rm -f .gitignore
 
-test_expect_success PERL,FILEMODE 'patch does not affect mode' '
+test_expect_success FILEMODE 'patch does not affect mode' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -186,7 +192,7 @@ test_expect_success PERL,FILEMODE 'patch does not affect mode' '
        git diff file | grep "new mode"
 '
 
-test_expect_success PERL,FILEMODE 'stage mode but not hunk' '
+test_expect_success FILEMODE 'stage mode but not hunk' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -196,7 +202,7 @@ test_expect_success PERL,FILEMODE 'stage mode but not hunk' '
 '
 
 
-test_expect_success PERL,FILEMODE 'stage mode and hunk' '
+test_expect_success FILEMODE 'stage mode and hunk' '
        git reset --hard &&
        echo content >>file &&
        chmod +x file &&
@@ -208,14 +214,14 @@ test_expect_success PERL,FILEMODE 'stage mode and hunk' '
 
 # end of tests disabled when filemode is not usable
 
-test_expect_success PERL 'setup again' '
+test_expect_success 'setup again' '
        git reset --hard &&
        test_chmod +x file &&
        echo content >>file
 '
 
 # Write the patch file with a new line at the top and bottom
-test_expect_success PERL 'setup patch' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -229,7 +235,7 @@ EOF
 '
 
 # Expected output, similar to the patch but w/ diff at the top
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b6f2c08..61b9053 100755
@@ -244,7 +250,7 @@ EOF
 '
 
 # Test splitting the first patch, then adding both
-test_expect_success PERL 'add first line works' '
+test_expect_success 'add first line works' '
        git commit -am "clear local changes" &&
        git apply patch &&
        (echo s; echo y; echo y) | git add -p file &&
@@ -252,7 +258,7 @@ test_expect_success PERL 'add first line works' '
        test_cmp expected diff
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/non-empty b/non-empty
 deleted file mode 100644
@@ -264,7 +270,7 @@ index d95f3ad..0000000
 EOF
 '
 
-test_expect_success PERL 'deleting a non-empty file' '
+test_expect_success 'deleting a non-empty file' '
        git reset --hard &&
        echo content >non-empty &&
        git add non-empty &&
@@ -275,7 +281,7 @@ test_expect_success PERL 'deleting a non-empty file' '
        test_cmp expected diff
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/empty b/empty
 deleted file mode 100644
@@ -283,7 +289,7 @@ index e69de29..0000000
 EOF
 '
 
-test_expect_success PERL 'deleting an empty file' '
+test_expect_success 'deleting an empty file' '
        git reset --hard &&
        > empty &&
        git add empty &&
@@ -294,7 +300,7 @@ test_expect_success PERL 'deleting an empty file' '
        test_cmp expected diff
 '
 
-test_expect_success PERL 'split hunk setup' '
+test_expect_success 'split hunk setup' '
        git reset --hard &&
        for i in 10 20 30 40 50 60
        do
@@ -310,7 +316,7 @@ test_expect_success PERL 'split hunk setup' '
        done >test
 '
 
-test_expect_success PERL 'split hunk "add -p (edit)"' '
+test_expect_success 'split hunk "add -p (edit)"' '
        # Split, say Edit and do nothing.  Then:
        #
        # 1. Broken version results in a patch that does not apply and
index 5fe57c5438e72150639fd6b970471a1689d30911..e4ba6013e41927bc0ec3ff3876648a71a71ce6e1 100755 (executable)
@@ -36,7 +36,7 @@ Alongc=$Alongc$AEligatu$AEligatu                     #254 Byte
 
 test_expect_success "detect if nfd needed" '
        precomposeunicode=`git config core.precomposeunicode` &&
-       test "$precomposeunicode" = false &&
+       test "$precomposeunicode" = true &&
        git config core.precomposeunicode true
 '
 test_expect_success "setup" '
index 97172b46b2afd52ca8f278f9525da01a56823e59..cd0454356a6ea26c630f76748dd2c4f73bed8526 100755 (executable)
@@ -73,7 +73,7 @@ test_expect_success 'plumbing not affected' '
 test_expect_success 'non-integer config parsing' '
        git config diff.context no &&
        test_must_fail git diff 2>output &&
-       test_i18ngrep "bad config value" output
+       test_i18ngrep "bad numeric config value" output
 '
 
 test_expect_success 'negative integer config parsing' '
index baa4685dcce59f17223161706a8cf059ecdabcd1..0dd8b65d7cdec2ada739d7e0b524fd3c66dd5ef4 100755 (executable)
@@ -202,7 +202,8 @@ test_expect_success 'setup mailmap blob tests' '
        Blob Guy <author@example.com>
        Blob Guy <bugs@company.xx>
        EOF
-       git add just-bugs both &&
+       printf "Tricky Guy <author@example.com>" >no-newline &&
+       git add just-bugs both no-newline &&
        git commit -m "my mailmaps" &&
        echo "Repo Guy <author@example.com>" >.mailmap &&
        echo "Internal Guy <author@example.com>" >internal.map
@@ -286,6 +287,19 @@ test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
        )
 '
 
+test_expect_success 'mailmap.blob can handle blobs without trailing newline' '
+       cat >expect <<-\EOF &&
+       Tricky Guy (1):
+             initial
+
+       nick1 (1):
+             second
+
+       EOF
+       git -c mailmap.blob=map:no-newline shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'cleanup after mailmap.blob tests' '
        rm -f .mailmap
 '
@@ -470,4 +484,15 @@ test_expect_success 'Blame output (complex mapping)' '
        test_cmp expect actual.fuzz
 '
 
+cat >expect <<\EOF
+Some Dude <some@dude.xx>
+EOF
+
+test_expect_success 'commit --author honors mailmap' '
+       test_must_fail git commit --author "nick" --allow-empty -meight &&
+       git commit --author "Some Dude" --allow-empty -meight &&
+       git show --pretty=format:"%an <%ae>%n" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 7665d6785c1a91ed0171e8cac61174ebee9b9a37..7369d3c517294feda894eba85f7b02ac9a57ccae 100755 (executable)
@@ -48,7 +48,7 @@ canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f
 canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
 
 canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
-canned_test "-L 4,18:a.c -L :main:a.c simple" multiple-overlapping
+canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping
 canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
 canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
 canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
@@ -64,17 +64,34 @@ test_bad_opts "-L 1,1000:b.c" "has only.*lines"
 test_bad_opts "-L :b.c" "argument.*not of the form"
 test_bad_opts "-L :foo:b.c" "no match"
 
-# There is a separate bug when an empty -L range is the first -L encountered,
-# thus to demonstrate this particular bug, the empty -L range must follow a
-# non-empty -L range.
-test_expect_success '-L {empty-range} (any -L)' '
+test_expect_success '-L X (X == nlines)' '
+       n=$(wc -l <b.c) &&
+       git log -L $n:b.c
+'
+
+test_expect_success '-L X (X == nlines + 1)' '
        n=$(expr $(wc -l <b.c) + 1) &&
-       git log -L1,1:b.c -L$n:b.c
+       test_must_fail git log -L $n:b.c
+'
+
+test_expect_success '-L X (X == nlines + 2)' '
+       n=$(expr $(wc -l <b.c) + 2) &&
+       test_must_fail git log -L $n:b.c
 '
 
-test_expect_success '-L {empty-range} (first -L)' '
+test_expect_success '-L ,Y (Y == nlines)' '
+       n=$(printf "%d" $(wc -l <b.c)) &&
+       git log -L ,$n:b.c
+'
+
+test_expect_success '-L ,Y (Y == nlines + 1)' '
        n=$(expr $(wc -l <b.c) + 1) &&
-       git log -L$n:b.c
+       test_must_fail git log -L ,$n:b.c
+'
+
+test_expect_success '-L ,Y (Y == nlines + 2)' '
+       n=$(expr $(wc -l <b.c) + 2) &&
+       test_must_fail git log -L ,$n:b.c
 '
 
 test_done
index fd2598e60190fe04381652c7dfe997e01b4ea0c5..a80584ea0eaba854ff21318e3c9646f72325bee7 100755 (executable)
@@ -505,4 +505,20 @@ test_expect_success 'test --all, --depth, and explicit tag' '
        ) >out-adt 2>error-adt
 '
 
+test_expect_success 'shallow fetch with tags does not break the repository' '
+       mkdir repo1 &&
+       (
+               cd repo1 &&
+               git init &&
+               test_commit 1 &&
+               test_commit 2 &&
+               test_commit 3 &&
+               mkdir repo2 &&
+               cd repo2 &&
+               git init &&
+               git fetch --depth=2 ../.git master:branch &&
+               git fsck
+       )
+'
+
 test_done
index fde689166a5dcb419e0519580d628f91f1f29f83..1f0f8e6827773b1bb9a419a822de2d1ebde8e37a 100755 (executable)
@@ -497,6 +497,88 @@ test_expect_success "should be able to fetch with duplicate refspecs" '
        )
 '
 
+# configured prune tests
+
+set_config_tristate () {
+       # var=$1 val=$2
+       case "$2" in
+       unset)  test_unconfig "$1" ;;
+       *)      git config "$1" "$2" ;;
+       esac
+}
+
+test_configured_prune () {
+       fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4
+
+       test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" '
+               # make sure a newbranch is there in . and also in one
+               git branch -f newbranch &&
+               (
+                       cd one &&
+                       test_unconfig fetch.prune &&
+                       test_unconfig remote.origin.prune &&
+                       git fetch &&
+                       git rev-parse --verify refs/remotes/origin/newbranch
+               )
+
+               # now remove it
+               git branch -d newbranch &&
+
+               # then test
+               (
+                       cd one &&
+                       set_config_tristate fetch.prune $fetch_prune &&
+                       set_config_tristate remote.origin.prune $remote_origin_prune &&
+
+                       git fetch $cmdline &&
+                       case "$expected" in
+                       pruned)
+                               test_must_fail git rev-parse --verify refs/remotes/origin/newbranch
+                               ;;
+                       kept)
+                               git rev-parse --verify refs/remotes/origin/newbranch
+                               ;;
+                       esac
+               )
+       '
+}
+
+test_configured_prune unset unset ""           kept
+test_configured_prune unset unset "--no-prune" kept
+test_configured_prune unset unset "--prune"    pruned
+
+test_configured_prune false unset ""           kept
+test_configured_prune false unset "--no-prune" kept
+test_configured_prune false unset "--prune"    pruned
+
+test_configured_prune true  unset ""           pruned
+test_configured_prune true  unset "--prune"    pruned
+test_configured_prune true  unset "--no-prune" kept
+
+test_configured_prune unset false ""           kept
+test_configured_prune unset false "--no-prune" kept
+test_configured_prune unset false "--prune"    pruned
+
+test_configured_prune false false ""           kept
+test_configured_prune false false "--no-prune" kept
+test_configured_prune false false "--prune"    pruned
+
+test_configured_prune true  false ""           kept
+test_configured_prune true  false "--prune"    pruned
+test_configured_prune true  false "--no-prune" kept
+
+test_configured_prune unset true  ""           pruned
+test_configured_prune unset true  "--no-prune" kept
+test_configured_prune unset true  "--prune"    pruned
+
+test_configured_prune false true  ""           pruned
+test_configured_prune false true  "--no-prune" kept
+test_configured_prune false true  "--prune"    pruned
+
+test_configured_prune true  true  ""           pruned
+test_configured_prune true  true  "--prune"    pruned
+test_configured_prune true  true  "--no-prune" kept
+
 test_expect_success 'all boundary commits are excluded' '
        test_commit base &&
        test_commit oneside &&
index 4691d51b8cde48dbb97ac5dfc1e3b75fe90f8da3..99c32d75391bb02fae779ad831879903abca5d25 100755 (executable)
@@ -1172,4 +1172,21 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '
        test_cmp expect actual
 '
 
+test_expect_success 'push --no-thin must produce non-thin pack' '
+       cat >>path1 <<\EOF &&
+keep base version of path1 big enough, compared to the new changes
+later, in order to pass size heuristics in
+builtin/pack-objects.c:try_delta()
+EOF
+       git commit -am initial &&
+       git init no-thin &&
+       git --git-dir=no-thin/.git config receive.unpacklimit 0 &&
+       git push no-thin/.git refs/heads/master:refs/heads/foo &&
+       echo modified >> path1 &&
+       git commit -am modified &&
+       git repack -adf &&
+       rcvpck="git receive-pack --reject-thin-pack-for-testing" &&
+       git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
+'
+
 test_done
index ed4d9c83181bc45456074f053ed5a0f137fc7e27..227d29335020f3e52a40b03b2f933f4d63942b91 100755 (executable)
@@ -148,6 +148,95 @@ test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
        test new = $(git show HEAD:file2)
 '
 
+# add a feature branch, keep-merge, that is merged into master, so the
+# test can try preserving the merge commit (or not) with various
+# --rebase flags/pull.rebase settings.
+test_expect_success 'preserve merge setup' '
+       git reset --hard before-rebase &&
+       git checkout -b keep-merge second^ &&
+       test_commit file3 &&
+       git checkout to-rebase &&
+       git merge keep-merge &&
+       git tag before-preserve-rebase
+'
+
+test_expect_success 'pull.rebase=false create a new merge commit' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase false &&
+       git pull . copy &&
+       test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=true flattens keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase true &&
+       git pull . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase 1 &&
+       git pull . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase preserve &&
+       git pull . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
+'
+
+test_expect_success 'pull.rebase=invalid fails' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase invalid &&
+       ! git pull . copy
+'
+
+test_expect_success '--rebase=false create a new merge commit' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase true &&
+       git pull --rebase=false . copy &&
+       test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success '--rebase=true rebases and flattens keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase preserve &&
+       git pull --rebase=true . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success '--rebase=preserve rebases and merges keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase true &&
+       git pull --rebase=preserve . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
+'
+
+test_expect_success '--rebase=invalid fails' '
+       git reset --hard before-preserve-rebase &&
+       ! git pull --rebase=invalid . copy
+'
+
+test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
+       git reset --hard before-preserve-rebase &&
+       test_config pull.rebase preserve &&
+       git pull --rebase . copy &&
+       test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+       test file3 = $(git show HEAD:file3.t)
+'
+
 test_expect_success '--rebase with rebased upstream' '
 
        git remote add -f me . &&
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
new file mode 100755 (executable)
index 0000000..ba20d83
--- /dev/null
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+test_description='compare & swap push force/delete safety'
+
+. ./test-lib.sh
+
+setup_srcdst_basic () {
+       rm -fr src dst &&
+       git clone --no-local . src &&
+       git clone --no-local src dst &&
+       (
+               cd src && git checkout HEAD^0
+       )
+}
+
+test_expect_success setup '
+       : create template repository
+       test_commit A &&
+       test_commit B &&
+       test_commit C
+'
+
+test_expect_success 'push to update (protected)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               test_commit D &&
+               test_must_fail git push --force-with-lease=master:master origin master
+       ) &&
+       git ls-remote . refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, forced)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               test_commit D &&
+               git push --force --force-with-lease=master:master origin master
+       ) &&
+       git ls-remote dst refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, tracking)' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git checkout master &&
+               test_commit D &&
+               git checkout HEAD^0
+       ) &&
+       git ls-remote src refs/heads/master >expect &&
+       (
+               cd dst &&
+               test_commit E &&
+               git ls-remote . refs/remotes/origin/master >expect &&
+               test_must_fail git push --force-with-lease=master origin master &&
+               git ls-remote . refs/remotes/origin/master >actual &&
+               test_cmp expect actual
+       ) &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (protected, tracking, forced)' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git checkout master &&
+               test_commit D &&
+               git checkout HEAD^0
+       ) &&
+       (
+               cd dst &&
+               test_commit E &&
+               git ls-remote . refs/remotes/origin/master >expect &&
+               git push --force --force-with-lease=master origin master
+       ) &&
+       git ls-remote dst refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               test_commit D &&
+               git push --force-with-lease=master:master^ origin master
+       ) &&
+       git ls-remote dst refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed, tracking)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               test_commit D &&
+               git push --force-with-lease=master origin master
+       ) &&
+       git ls-remote dst refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to update (allowed even though no-ff)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git reset --hard HEAD^ &&
+               test_commit D &&
+               git push --force-with-lease=master origin master
+       ) &&
+       git ls-remote dst refs/heads/master >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to delete (protected)' '
+       setup_srcdst_basic &&
+       git ls-remote src refs/heads/master >expect &&
+       (
+               cd dst &&
+               test_must_fail git push --force-with-lease=master:master^ origin :master
+       ) &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to delete (protected, forced)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git push --force --force-with-lease=master:master^ origin :master
+       ) &&
+       >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to delete (allowed)' '
+       setup_srcdst_basic &&
+       (
+               cd dst &&
+               git push --force-with-lease=master origin :master
+       ) &&
+       >expect &&
+       git ls-remote src refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cover everything with default force-with-lease (protected)' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git branch naster master^
+       )
+       git ls-remote src refs/heads/\* >expect &&
+       (
+               cd dst &&
+               test_must_fail git push --force-with-lease origin master master:naster
+       ) &&
+       git ls-remote src refs/heads/\* >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cover everything with default force-with-lease (allowed)' '
+       setup_srcdst_basic &&
+       (
+               cd src &&
+               git branch naster master^
+       )
+       (
+               cd dst &&
+               git fetch &&
+               git push --force-with-lease origin master master:naster
+       ) &&
+       git ls-remote dst refs/heads/master |
+       sed -e "s/master/naster/" >expect &&
+       git ls-remote src refs/heads/naster >actual &&
+       test_cmp expect actual
+'
+
+test_done
index beb00be4b1593a058308d8897fed81a7cf1556d7..470ac54295e8640feb4d23a81c5dc8ce362857d0 100755 (executable)
@@ -153,7 +153,7 @@ test_expect_success 'used receive-pack service' '
 '
 
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-       "$ROOT_PATH"/test_repo_clone master
+       "$ROOT_PATH"/test_repo_clone master             success
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
        # create a dissimilarly-named remote ref so that git is unable to match the
index 55a866af803452e164e2c11c421b1e63f62d2e1e..8196af19f68ac3772a81326828f7cdf83ae399a1 100755 (executable)
@@ -187,6 +187,22 @@ test_expect_success 'dumb clone via http-backend respects namespace' '
        test_cmp expect actual
 '
 
+cat >cookies.txt <<EOF
+127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+EOF
+cat >expect_cookies.txt <<EOF
+
+127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+127.0.0.1      FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
+EOF
+test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+       git config http.cookiefile cookies.txt &&
+       git config http.savecookies true &&
+       git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
+       tail -3 cookies.txt > cookies_tail.txt
+       test_cmp expect_cookies.txt cookies_tail.txt
+'
+
 test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
 
 test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
index 8c4c5396a8447fc39d6f0c697af761631b08b3f7..613f69a254bab23e1f09126b4a7f3dca8692deaa 100755 (executable)
@@ -182,6 +182,17 @@ test_expect_success 'push update refs' '
        )
 '
 
+test_expect_success 'push update refs disabled by no-private-update' '
+       (cd local &&
+       echo more-update >>file &&
+       git commit -a -m more-update &&
+       git rev-parse --verify testgit/origin/heads/update >expect &&
+       GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE=t git push origin update &&
+       git rev-parse --verify testgit/origin/heads/update >actual &&
+       test_cmp expect actual
+       )
+'
+
 test_expect_success 'push update refs failure' '
        (cd local &&
        git checkout update &&
diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh
new file mode 100755 (executable)
index 0000000..878faf2
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='ext::cmd remote "connect" helper'
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_tick &&
+       git commit --allow-empty -m initial &&
+       test_tick &&
+       git commit --allow-empty -m second &&
+       test_tick &&
+       git commit --allow-empty -m third &&
+       test_tick &&
+       git tag -a -m "tip three" three &&
+
+       test_tick &&
+       git commit --allow-empty -m fourth
+'
+
+test_expect_success clone '
+       cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") &&
+       git clone "ext::sh -c %S% ." dst &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git config remote.origin.url "ext::sh -c $cmd" &&
+               git for-each-ref refs/heads/ refs/tags/
+       ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update following tag' '
+       test_tick &&
+       git commit --allow-empty -m fifth &&
+       test_tick &&
+       git tag -a -m "tip five" five &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag' '
+       test_tick &&
+       git commit --allow-empty -m sixth &&
+       test_tick &&
+       git tag -a -m "tip two" two three^1 &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag without primary transfer' '
+       test_tick &&
+       git tag -a -m "tip one " one two^1 &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_done
index 57ce2395d6738617797d0ba270874abd55a3b899..fde5e712eb512791c893de7b393fe36e382a3f6d 100755 (executable)
@@ -127,4 +127,10 @@ test_expect_success 'full history simplification without parent' '
        }
 '
 
+test_expect_success '--full-diff is not affected by --parents' '
+       git log -p --pretty="%H" --full-diff -- file >expected &&
+       git log -p --pretty="%H" --full-diff --parents -- file >actual &&
+       test_cmp expected actual
+'
+
 test_done
index c680f789a7028f5300a4665ca068c9a7e87bb22a..a89dfbef08a5a9ff2d4c8727b67b44279fe522e5 100755 (executable)
@@ -259,7 +259,7 @@ test_expect_success 'setup for rename + d/f conflicts' '
        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file &&
        echo foo >dir/file-in-the-way &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        echo 11 >>sub/file &&
        echo more >>dir/file-in-the-way &&
@@ -439,7 +439,7 @@ test_expect_success 'setup both rename source and destination involved in D/F co
        mkdir one &&
        echo stuff >one/file &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git mv one/file destdir &&
        git commit -m "Renamed to destdir" &&
@@ -479,7 +479,7 @@ test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
        echo stuff >one/file &&
        echo other >two/file &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git rm -rf one &&
        git mv two/file one &&
@@ -539,7 +539,7 @@ test_expect_success 'setup rename of one file to two, with directories in the wa
 
        echo stuff >original &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        mkdir two &&
        >two/file &&
@@ -583,7 +583,7 @@ test_expect_success 'setup rename one file to two; directories moving out of the
        mkdir one two &&
        touch one/file two/file &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git rm -rf one &&
        git mv original one &&
@@ -618,7 +618,7 @@ test_expect_success 'setup avoid unnecessary update, normal rename' '
 
        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git mv original rename &&
        echo 11 >>rename &&
@@ -649,7 +649,7 @@ test_expect_success 'setup to test avoiding unnecessary update, with D/F conflic
        mkdir df &&
        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file &&
        git add -A &&
-       git commit -m "Common commmit" &&
+       git commit -m "Common commit" &&
 
        git mv df/file temp &&
        rm -rf df &&
index 39ef61994f6a4b4bea1731002f7637232e7dd349..ea00d71e77e05af212e2d69b3eced450b119fcf1 100755 (executable)
@@ -32,6 +32,16 @@ test_expect_success 'star pathspec globs' '
        test_cmp expect actual
 '
 
+test_expect_success 'star pathspec globs' '
+       cat >expect <<-\EOF &&
+       bracket
+       star
+       vanilla
+       EOF
+       git log --format=%s -- ":(glob)f*" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'bracket pathspec globs and matches literal brackets' '
        cat >expect <<-\EOF &&
        bracket
@@ -41,28 +51,105 @@ test_expect_success 'bracket pathspec globs and matches literal brackets' '
        test_cmp expect actual
 '
 
+test_expect_success 'bracket pathspec globs and matches literal brackets' '
+       cat >expect <<-\EOF &&
+       bracket
+       vanilla
+       EOF
+       git log --format=%s -- ":(glob)f[o][o]" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'no-glob option matches literally (vanilla)' '
        echo vanilla >expect &&
        git --literal-pathspecs log --format=%s -- foo >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (vanilla)' '
+       echo vanilla >expect &&
+       git log --format=%s -- ":(literal)foo" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'no-glob option matches literally (star)' '
        echo star >expect &&
        git --literal-pathspecs log --format=%s -- "f*" >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (star)' '
+       echo star >expect &&
+       git log --format=%s -- ":(literal)f*" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'no-glob option matches literally (bracket)' '
        echo bracket >expect &&
        git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'no-glob option matches literally (bracket)' '
+       echo bracket >expect &&
+       git log --format=%s -- ":(literal)f[o][o]" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'no-glob option disables :(literal)' '
+       : >expect &&
+       git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'no-glob environment variable works' '
        echo star >expect &&
        GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'setup xxx/bar' '
+       mkdir xxx &&
+       test_commit xxx xxx/bar
+'
+
+test_expect_success '**/ works with :(glob)' '
+       cat >expect <<-\EOF &&
+       xxx
+       unrelated
+       EOF
+       git log --format=%s -- ":(glob)**/bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '**/ does not work with --noglob-pathspecs' '
+       : >expect &&
+       git --noglob-pathspecs log --format=%s -- "**/bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '**/ works with :(glob) and --noglob-pathspecs' '
+       cat >expect <<-\EOF &&
+       xxx
+       unrelated
+       EOF
+       git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '**/ works with --glob-pathspecs' '
+       cat >expect <<-\EOF &&
+       xxx
+       unrelated
+       EOF
+       git --glob-pathspecs log --format=%s -- "**/bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' '
+       : >expect &&
+       git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
new file mode 100755 (executable)
index 0000000..8d4a7fc
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='test case insensitive pathspec limiting'
+. ./test-lib.sh
+
+if test_have_prereq CASE_INSENSITIVE_FS
+then
+       skip_all='skipping case sensitive tests - case insensitive file system'
+       test_done
+fi
+
+test_expect_success 'create commits with glob characters' '
+       test_commit bar bar &&
+       test_commit bAr bAr &&
+       test_commit BAR BAR &&
+       mkdir foo &&
+       test_commit foo/bar foo/bar &&
+       test_commit foo/bAr foo/bAr &&
+       test_commit foo/BAR foo/BAR &&
+       mkdir fOo &&
+       test_commit fOo/bar fOo/bar &&
+       test_commit fOo/bAr fOo/bAr &&
+       test_commit fOo/BAR fOo/BAR &&
+       mkdir FOO &&
+       test_commit FOO/bar FOO/bar &&
+       test_commit FOO/bAr FOO/bAr &&
+       test_commit FOO/BAR FOO/BAR
+'
+
+test_expect_success 'tree_entry_interesting matches bar' '
+       echo bar >expect &&
+       git log --format=%s -- "bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar' '
+       cat <<-EOF >expect &&
+       BAR
+       bAr
+       bar
+       EOF
+       git log --format=%s -- ":(icase)bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' '
+       cat <<-EOF >expect &&
+       fOo/BAR
+       fOo/bAr
+       fOo/bar
+       EOF
+       ( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' '
+       cat <<-EOF >expect &&
+       FOO/BAR
+       FOO/bAr
+       FOO/bar
+       fOo/BAR
+       fOo/bAr
+       fOo/bar
+       foo/BAR
+       foo/bAr
+       foo/bar
+       EOF
+       ( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec_depth matches :(icase)bar' '
+       cat <<-EOF >expect &&
+       BAR
+       bAr
+       bar
+       EOF
+       git ls-files ":(icase)bar" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' '
+       cat <<-EOF >expect &&
+       fOo/BAR
+       fOo/bAr
+       fOo/bar
+       EOF
+       ( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' '
+       cat <<-EOF >expect &&
+       bar
+       fOo/BAR
+       fOo/bAr
+       fOo/bar
+       EOF
+       ( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 101816e718d6149a0e8ee500026455f04a8384ed..d432f42bcbd509b0dae46848d2745eba26fc6872 100755 (executable)
@@ -259,4 +259,132 @@ test_expect_success SYMLINKS 'check moved symlink' '
 
 rm -f moved symlink
 
+test_expect_success 'setup submodule' '
+       git commit -m initial &&
+       git reset --hard &&
+       git submodule add ./. sub &&
+       echo content >file &&
+       git add file &&
+       git commit -m "added sub and file"
+'
+
+test_expect_success 'git mv cannot move a submodule in a file' '
+       test_must_fail git mv sub file
+'
+
+test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       git rm .gitmodules &&
+       (
+               cd sub &&
+               rm -f .git &&
+               cp -a ../.git/modules/sub .git &&
+               GIT_WORK_TREE=. git config --unset core.worktree
+       ) &&
+       mkdir mod &&
+       git mv sub mod/sub &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
+test_expect_success 'git mv moves a submodule with gitfile' '
+       rm -rf mod/sub &&
+       git reset --hard &&
+       git submodule update &&
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       (
+               cd mod &&
+               git mv ../sub/ .
+       ) &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       echo mod/sub >expected &&
+       git config -f .gitmodules submodule.sub.path >actual &&
+       test_cmp expected actual &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
+test_expect_success 'mv does not complain when no .gitmodules file is found' '
+       rm -rf mod/sub &&
+       git reset --hard &&
+       git submodule update &&
+       git rm .gitmodules &&
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       git mv sub mod/sub 2>actual.err &&
+       ! test -s actual.err &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
+test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
+       rm -rf mod/sub &&
+       git reset --hard &&
+       git submodule update &&
+       git config -f .gitmodules foo.bar true &&
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       test_must_fail git mv sub mod/sub 2>actual.err &&
+       test -s actual.err &&
+       test -e sub &&
+       git diff-files --quiet -- sub &&
+       git add .gitmodules &&
+       git mv sub mod/sub 2>actual.err &&
+       ! test -s actual.err &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
+test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
+       rm -rf mod/sub &&
+       git reset --hard &&
+       git submodule update &&
+       git config -f .gitmodules --remove-section submodule.sub &&
+       git add .gitmodules &&
+       entry="$(git ls-files --stage sub | cut -f 1)" &&
+       echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
+       git mv sub mod/sub 2>actual.err &&
+       test_i18ncmp expect.err actual.err &&
+       ! test -e sub &&
+       [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
+       (
+               cd mod/sub &&
+               git status
+       ) &&
+       git update-index --refresh &&
+       git diff-files --quiet
+'
+
+test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
+       rm -rf mod/sub &&
+       git reset --hard &&
+       git submodule update &&
+       git mv -n sub mod/sub 2>actual.err &&
+       test -f sub/.git &&
+       git diff-index --exit-code HEAD &&
+       git update-index --refresh &&
+       git diff-files --quiet -- sub .gitmodules
+'
+
 test_done
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
new file mode 100755 (executable)
index 0000000..a997f7a
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='filter-branch removal of trees with null sha1'
+. ./test-lib.sh
+
+test_expect_success 'setup: base commits' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three
+'
+
+test_expect_success 'setup: a commit with a bogus null sha1 in the tree' '
+       {
+               git ls-tree HEAD &&
+               printf "160000 commit $_z40\\tbroken\\n"
+       } >broken-tree
+       echo "add broken entry" >msg &&
+
+       tree=$(git mktree <broken-tree) &&
+       test_tick &&
+       commit=$(git commit-tree $tree -p HEAD <msg) &&
+       git update-ref HEAD "$commit"
+'
+
+# we have to make one more commit on top removing the broken
+# entry, since otherwise our index does not match HEAD (and filter-branch will
+# complain). We could make the index match HEAD, but doing so would involve
+# writing a null sha1 into the index.
+test_expect_success 'setup: bring HEAD and index in sync' '
+       test_tick &&
+       git commit -a -m "back to normal"
+'
+
+test_expect_success 'filter commands are still checked' '
+       test_must_fail git filter-branch \
+               --force --prune-empty \
+               --index-filter "git rm --cached --ignore-unmatch three.t"
+'
+
+test_expect_success 'removing the broken entry works' '
+       echo three >expect &&
+       git filter-branch \
+               --force --prune-empty \
+               --index-filter "git rm --cached --ignore-unmatch broken" &&
+       git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 8062cf502bc473cee0d0788145f978b534246679..af00ab4d88cc75ec720e31da3a0d5d3a1ef8df40 100755 (executable)
@@ -11,7 +11,10 @@ test_expect_success 'setup' '
 test_expect_success 'reset' '
        git add a b &&
        git reset &&
-       test "$(git ls-files)" = ""
+
+       >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'reset HEAD' '
@@ -24,28 +27,42 @@ test_expect_success 'reset $file' '
        rm .git/index &&
        git add a b &&
        git reset a &&
-       test "$(git ls-files)" = "b"
+
+       echo b >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success 'reset -p' '
+test_expect_success PERL 'reset -p' '
        rm .git/index &&
        git add a &&
-       echo y | git reset -p &&
-       test "$(git ls-files)" = ""
+       echo y >yes &&
+       git reset -p <yes &&
+
+       >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'reset --soft is a no-op' '
        rm .git/index &&
        git add a &&
-       git reset --soft
-       test "$(git ls-files)" = "a"
+       git reset --soft &&
+
+       echo a >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'reset --hard' '
        rm .git/index &&
        git add a &&
+       test_when_finished "echo a >a" &&
        git reset --hard &&
-       test "$(git ls-files)" = "" &&
+
+       >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual &&
        test_path_is_missing a
 '
 
index 5ee97b003a879a0b6a558265e94c18582aa836c0..10f89bd0ce3656a1dee841d19582e5d1bfc9736a 100755 (executable)
@@ -18,6 +18,16 @@ test_expect_success 'setup - initial commit' '
        git branch initial
 '
 
+test_expect_success 'configuration parsing' '
+       test_when_finished "rm -f .gitmodules" &&
+       cat >.gitmodules <<-\EOF &&
+       [submodule "s"]
+               path
+               ignore
+       EOF
+       test_must_fail git status
+'
+
 test_expect_success 'setup - repository in init subdirectory' '
        mkdir init &&
        (
@@ -773,13 +783,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano
                        test_cmp expect .git
                ) &&
                echo "repo" >expect &&
-               git config -f .gitmodules submodule.repo.path >actual &&
-               test_cmp expect actual &&
+               test_must_fail git config -f .gitmodules submodule.repo.path &&
                git config -f .gitmodules submodule.repo_new.path >actual &&
                test_cmp expect actual&&
                echo "$submodurl/repo" >expect &&
-               git config -f .gitmodules submodule.repo.url >actual &&
-               test_cmp expect actual &&
+               test_must_fail git config -f .gitmodules submodule.repo.url &&
                echo "$submodurl/bare.git" >expect &&
                git config -f .gitmodules submodule.repo_new.url >actual &&
                test_cmp expect actual &&
@@ -799,12 +807,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' '
                git rm repo &&
                test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo &&
                test ! -d repo &&
-               echo "repo" >expect &&
-               git config -f .gitmodules submodule.repo_new.path >actual &&
-               test_cmp expect actual&&
-               echo "$submodurl/bare.git" >expect &&
-               git config -f .gitmodules submodule.repo_new.url >actual &&
-               test_cmp expect actual &&
+               test_must_fail git config -f .gitmodules submodule.repo_new.path &&
+               test_must_fail git config -f .gitmodules submodule.repo_new.url &&
                echo "$submodurl/bare.git" >expect &&
                git config submodule.repo_new.url >actual &&
                test_cmp expect actual &&
@@ -958,7 +962,6 @@ test_expect_success 'submodule with UTF-8 name' '
                git add sub &&
                git commit -m "init sub"
        ) &&
-       test_config core.precomposeunicode true &&
        git submodule add ./"$svname" &&
        git submodule >&2 &&
        test -n "$(git submodule | grep "$svname")"
index b192f936bcbe794f0691550d1bf38714b1359d01..f0b33053ab01c692a4dda15c0f631aaa45fb81b9 100755 (executable)
@@ -58,7 +58,7 @@ test_expect_success 'setup a submodule tree' '
         git submodule add ../merging merging &&
         test_tick &&
         git commit -m "rebasing"
-       )
+       ) &&
        (cd super &&
         git submodule add ../none none &&
         test_tick &&
index 99ce36f5ef91ae91c0f2ebd02ae70c4cba4c937c..f04798f87232fbbf705dd8a24300a76a2a6e435a 100755 (executable)
@@ -53,7 +53,7 @@ test_expect_success PERL 'can use paths with --interactive' '
 '
 
 test_expect_success 'using invalid commit with -C' '
-       test_must_fail git commit -C bogus
+       test_must_fail git commit --allow-empty -C bogus
 '
 
 test_expect_success 'nothing to commit' '
index d526b1d96a2e98bc304f4d36eaaa60858080e63d..05d9db090d7813d231a397c190603f4a3615a8d0 100755 (executable)
@@ -253,7 +253,7 @@ test_expect_success 'deleted vs modified submodule' '
     git checkout -b test6 branch1 &&
     git submodule update -N &&
     mv submod submod-movedaside &&
-    git rm submod &&
+    git rm --cached submod &&
     git commit -m "Submodule deleted from branch" &&
     git checkout -b test6.a test6 &&
     test_must_fail git merge master &&
@@ -322,7 +322,7 @@ test_expect_success 'file vs modified submodule' '
     git checkout -b test7 branch1 &&
     git submodule update -N &&
     mv submod submod-movedaside &&
-    git rm submod &&
+    git rm --cached submod &&
     echo not a submodule >submod &&
     git add submod &&
     git commit -m "Submodule path becomes file" &&
@@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' '
 test_expect_success 'directory vs modified submodule' '
     git checkout -b test11 branch1 &&
     mv submod submod-movedaside &&
-    git rm submod &&
+    git rm --cached submod &&
     mkdir submod &&
     echo not a submodule >submod/file16 &&
     git add submod/file16 &&
index ac6f3b6af25c67ae3a2c0748c34f99b4cd0d4b32..31a770d9bc58d7c01d69f7817fcbc06622859662 100755 (executable)
@@ -1031,6 +1031,32 @@ test_expect_success \
         git diff-tree -M -r M3^ M3 >actual &&
         compare_diff_raw expect actual'
 
+cat >input <<INPUT_END
+commit refs/heads/M4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+rename root
+COMMIT
+
+from refs/heads/M2^0
+R "" sub
+
+INPUT_END
+
+cat >expect <<EOF
+:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100  file2/oldf      sub/file2/oldf
+:100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100  file4   sub/file4
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100  i/am/new/to/you sub/i/am/new/to/you
+:100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100  newdir/exec.sh  sub/newdir/exec.sh
+:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100  newdir/interesting      sub/newdir/interesting
+EOF
+test_expect_success \
+       'M: rename root to subdirectory' \
+       'git fast-import <input &&
+        git diff-tree -M -r M4^ M4 >actual &&
+        cat actual &&
+        compare_diff_raw expect actual'
+
 ###
 ### series N
 ###
@@ -1227,6 +1253,29 @@ test_expect_success \
         git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
         compare_diff_raw expect actual'
 
+test_expect_success \
+       'N: copy root by path' \
+       'cat >expect <<-\EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      oldroot/file2/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      oldroot/file2/oldf
+       :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100   file4   oldroot/file4
+       :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100   newdir/exec.sh  oldroot/newdir/exec.sh
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      oldroot/newdir/interesting
+       EOF
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N-copy-root-path
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy root directory by (empty) path
+       COMMIT
+
+       from refs/heads/branch^0
+       C "" oldroot
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
+        compare_diff_raw expect actual'
+
 test_expect_success \
        'N: delete directory by copying' \
        'cat >expect <<-\EOF &&
@@ -2934,4 +2983,20 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
        test_i18ngrep "space after tree-ish" err
 '
 
+###
+### series T (ls)
+###
+# Setup is carried over from series S.
+
+test_expect_success 'T: ls root tree' '
+       sed -e "s/Z\$//" >expect <<-EOF &&
+       040000 tree $(git rev-parse S^{tree})   Z
+       EOF
+       sha1=$(git rev-parse --verify S) &&
+       git fast-import --import-marks=marks <<-EOF >actual &&
+       ls $sha1 ""
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 272a071e85b0b7c6b34da06fd7492a257b09fa52..2d4beb5e50c257fed2f4b6475cb5834425b5a64f 100755 (executable)
@@ -69,7 +69,7 @@ run_completion ()
        local -a COMPREPLY _words
        local _cword
        _words=( $1 )
-       test "${1: -1}" = ' ' && _words+=('')
+       test "${1: -1}" = ' ' && _words[${#_words[@]}+1]=''
        (( _cword = ${#_words[@]} - 1 ))
        __git_wrap__git_main && print_comp
 }
index 3c3e4e8c38023595978368e0b1665e122e0bdfab..59f875e830df432ed200eeca96e70100a1fb2d2b 100755 (executable)
@@ -61,6 +61,29 @@ test_expect_success 'prompt - unborn branch' '
        test_cmp expected "$actual"
 '
 
+repo_with_newline='repo
+with
+newline'
+
+if mkdir "$repo_with_newline" 2>/dev/null
+then
+       test_set_prereq FUNNYNAMES
+else
+       say 'Your filesystem does not allow newlines in filenames.'
+fi
+
+test_expect_success FUNNYNAMES 'prompt - with newline in path' '
+       printf " (master)" >expected &&
+       git init "$repo_with_newline" &&
+       test_when_finished "rm -rf \"$repo_with_newline\"" &&
+       mkdir "$repo_with_newline"/subdir &&
+       (
+               cd "$repo_with_newline/subdir" &&
+               __git_ps1 >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
 test_expect_success 'prompt - detached head' '
        printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
        test_config core.abbrev 13 &&
index 15ab6d8e7ea05ca54f0b755828e166fafe9bc28c..1f3bcebfd76ae1b37e1542631a87e5a5e3e6b1eb 100755 (executable)
@@ -30,6 +30,7 @@ do
        if [ "$local_sha" = $z40 ]
        then
                # Handle delete
+               :
        else
                if [ "$remote_sha" = $z40 ]
                then
diff --git a/test-urlmatch-normalization.c b/test-urlmatch-normalization.c
new file mode 100644 (file)
index 0000000..090bf21
--- /dev/null
@@ -0,0 +1,50 @@
+#include "git-compat-util.h"
+#include "urlmatch.h"
+
+int main(int argc, char **argv)
+{
+       const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>";
+       char *url1, *url2;
+       int opt_p = 0, opt_l = 0;
+
+       /*
+        * For one url, succeed if url_normalize succeeds on it, fail otherwise.
+        * For two urls, succeed only if url_normalize succeeds on both and
+        * the results compare equal with strcmp.  If -p is given (one url only)
+        * and url_normalize succeeds, print the result followed by "\n".  If
+        * -l is given (one url only) and url_normalize succeeds, print the
+        * returned length in decimal followed by "\n".
+        */
+
+       if (argc > 1 && !strcmp(argv[1], "-p")) {
+               opt_p = 1;
+               argc--;
+               argv++;
+       } else if (argc > 1 && !strcmp(argv[1], "-l")) {
+               opt_l = 1;
+               argc--;
+               argv++;
+       }
+
+       if (argc < 2 || argc > 3)
+               die("%s", usage);
+
+       if (argc == 2) {
+               struct url_info info;
+               url1 = url_normalize(argv[1], &info);
+               if (!url1)
+                       return 1;
+               if (opt_p)
+                       printf("%s\n", url1);
+               if (opt_l)
+                       printf("%u\n", (unsigned)info.url_len);
+               return 0;
+       }
+
+       if (opt_p || opt_l)
+               die("%s", usage);
+
+       url1 = url_normalize(argv[1], NULL);
+       url2 = url_normalize(argv[2], NULL);
+       return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1;
+}
index 63cabc37e37dd338d3d9ee0764f099840d937600..b32e2d64dd344c3f8c4d5f42b6c48dd6bdb8d29f 100644 (file)
@@ -27,7 +27,9 @@ struct helper_data {
                push : 1,
                connect : 1,
                signed_tags : 1,
-               no_disconnect_req : 1;
+               check_connectivity : 1,
+               no_disconnect_req : 1,
+               no_private_update : 1;
        char *export_marks;
        char *import_marks;
        /* These go from remote name (as in "list") to private name */
@@ -186,6 +188,8 @@ static struct child_process *get_helper(struct transport *transport)
                        data->bidi_import = 1;
                else if (!strcmp(capname, "export"))
                        data->export = 1;
+               else if (!strcmp(capname, "check-connectivity"))
+                       data->check_connectivity = 1;
                else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
@@ -205,6 +209,8 @@ static struct child_process *get_helper(struct transport *transport)
                        strbuf_addstr(&arg, "--import-marks=");
                        strbuf_addstr(&arg, capname + strlen("import-marks "));
                        data->import_marks = strbuf_detach(&arg, NULL);
+               } else if (!prefixcmp(capname, "no-private-update")) {
+                       data->no_private_update = 1;
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
                            "helper probably needs newer version of Git.",
@@ -349,6 +355,9 @@ static int fetch_with_fetch(struct transport *transport,
        struct strbuf buf = STRBUF_INIT;
 
        standard_options(transport);
+       if (data->check_connectivity &&
+           data->transport_options.check_self_contained_and_connected)
+               set_helper_option(transport, "check-connectivity", "true");
 
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
@@ -372,6 +381,10 @@ static int fetch_with_fetch(struct transport *transport,
                        else
                                transport->pack_lockfile = xstrdup(name);
                }
+               else if (data->check_connectivity &&
+                        data->transport_options.check_self_contained_and_connected &&
+                        !strcmp(buf.buf, "connectivity-ok"))
+                       data->transport_options.self_contained_and_connected = 1;
                else if (!buf.len)
                        break;
                else
@@ -683,6 +696,11 @@ static int push_update_ref_status(struct strbuf *buf,
                        free(msg);
                        msg = NULL;
                }
+               else if (!strcmp(msg, "stale info")) {
+                       status = REF_STATUS_REJECT_STALE;
+                       free(msg);
+                       msg = NULL;
+               }
        }
 
        if (*ref)
@@ -723,7 +741,7 @@ static void push_update_refs_status(struct helper_data *data,
                if (push_update_ref_status(&buf, &ref, remote_refs))
                        continue;
 
-               if (!data->refspecs)
+               if (!data->refspecs || data->no_private_update)
                        continue;
 
                /* propagate back the update to the remote namespace */
@@ -737,13 +755,15 @@ static void push_update_refs_status(struct helper_data *data,
 }
 
 static int push_refs_with_push(struct transport *transport,
-               struct ref *remote_refs, int flags)
+                              struct ref *remote_refs, int flags)
 {
        int force_all = flags & TRANSPORT_PUSH_FORCE;
        int mirror = flags & TRANSPORT_PUSH_MIRROR;
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref;
+       struct string_list cas_options = STRING_LIST_INIT_DUP;
+       struct string_list_item *cas_option;
 
        get_helper(transport);
        if (!data->push)
@@ -756,6 +776,7 @@ static int push_refs_with_push(struct transport *transport,
                /* Check for statuses set by set_ref_status_for_push() */
                switch (ref->status) {
                case REF_STATUS_REJECT_NONFASTFORWARD:
+               case REF_STATUS_REJECT_STALE:
                case REF_STATUS_REJECT_ALREADY_EXISTS:
                case REF_STATUS_UPTODATE:
                        continue;
@@ -778,11 +799,29 @@ static int push_refs_with_push(struct transport *transport,
                strbuf_addch(&buf, ':');
                strbuf_addstr(&buf, ref->name);
                strbuf_addch(&buf, '\n');
+
+               /*
+                * The "--force-with-lease" options without explicit
+                * values to expect have already been expanded into
+                * the ref->old_sha1_expect[] field; we can ignore
+                * transport->smart_options->cas altogether and instead
+                * can enumerate them from the refs.
+                */
+               if (ref->expect_old_sha1) {
+                       struct strbuf cas = STRBUF_INIT;
+                       strbuf_addf(&cas, "%s:%s",
+                                   ref->name, sha1_to_hex(ref->old_sha1_expect));
+                       string_list_append(&cas_options, strbuf_detach(&cas, NULL));
+               }
        }
-       if (buf.len == 0)
+       if (buf.len == 0) {
+               string_list_clear(&cas_options, 0);
                return 0;
+       }
 
        standard_options(transport);
+       for_each_string_list_item(cas_option, &cas_options)
+               set_helper_option(transport, "cas", cas_option->string);
 
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
index e15db9808c85a3918a8de3c409500aaa78d1328e..7202b7777d804b75eb2ec54f666e81173690678c 100644 (file)
@@ -3,6 +3,8 @@
 #include "run-command.h"
 #include "pkt-line.h"
 #include "fetch-pack.h"
+#include "remote.h"
+#include "connect.h"
 #include "send-pack.h"
 #include "walker.h"
 #include "bundle.h"
@@ -707,6 +709,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
                                                 "needs force", porcelain);
                break;
+       case REF_STATUS_REJECT_STALE:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                                                "stale info", porcelain);
+               break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
                                                 ref->deletion ? NULL : ref->peer_ref,
@@ -875,6 +881,8 @@ void transport_take_over(struct transport *transport,
        transport->push_refs = git_transport_push;
        transport->disconnect = disconnect_git;
        transport->smart_options = &(data->options);
+
+       transport->cannot_reuse = 1;
 }
 
 static int is_local(const char *url)
@@ -1076,6 +1084,7 @@ static int run_pre_push_hook(struct transport *transport,
        for (r = remote_refs; r; r = r->next) {
                if (!r->peer_ref) continue;
                if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
+               if (r->status == REF_STATUS_REJECT_STALE) continue;
                if (r->status == REF_STATUS_UPTODATE) continue;
 
                strbuf_reset(&buf);
@@ -1140,6 +1149,12 @@ int transport_push(struct transport *transport,
                        return -1;
                }
 
+               if (transport->smart_options &&
+                   transport->smart_options->cas &&
+                   !is_empty_cas(transport->smart_options->cas))
+                       apply_push_cas(transport->smart_options->cas,
+                                      transport->remote, remote_refs);
+
                set_ref_status_for_push(remote_refs,
                        flags & TRANSPORT_PUSH_MIRROR,
                        flags & TRANSPORT_PUSH_FORCE);
index ea70ea7e4a1b61c3006c614712ddd1aa41205ac6..8f96bed775e720043658169b46f70e192cf76b41 100644 (file)
@@ -2,6 +2,7 @@
 #define TRANSPORT_H
 
 #include "cache.h"
+#include "run-command.h"
 #include "remote.h"
 
 struct git_transport_options {
@@ -13,6 +14,7 @@ struct git_transport_options {
        int depth;
        const char *uploadpack;
        const char *receivepack;
+       struct push_cas_option *cas;
 };
 
 struct transport {
@@ -27,6 +29,12 @@ struct transport {
         */
        unsigned got_remote_refs : 1;
 
+       /*
+        * Transports that call take-over destroys the data specific to
+        * the transport type while doing so, and cannot be reused.
+        */
+       unsigned cannot_reuse : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
@@ -126,6 +134,9 @@ struct transport *transport_get(struct remote *, const char *);
 /* Transfer the data as a thin pack if not null */
 #define TRANS_OPT_THIN "thin"
 
+/* Check the current value of the remote ref */
+#define TRANS_OPT_CAS "cas"
+
 /* Keep the pack that was transferred if not null */
 #define TRANS_OPT_KEEP "keep"
 
index ba01563a0241041cb401cfb03495e6067847248e..ccf9d7c8fd41c00f13136ef99d24180b36a098c1 100644 (file)
@@ -138,7 +138,6 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 
        /* Enable recursion indefinitely */
        opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
-       opt->pathspec.max_depth = -1;
 
        strbuf_init(&base, PATH_MAX);
        strbuf_add(&base, base_str, baselen);
@@ -196,9 +195,27 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        struct diff_options diff_opts;
        struct diff_queue_struct *q = &diff_queued_diff;
        struct diff_filepair *choice;
-       const char *paths[1];
        int i;
 
+       /*
+        * follow-rename code is very specific, we need exactly one
+        * path. Magic that matches more than one path is not
+        * supported.
+        */
+       GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
+#if 0
+       /*
+        * We should reject wildcards as well. Unfortunately we
+        * haven't got a reliable way to detect that 'foo\*bar' in
+        * fact has no wildcards. nowildcard_len is merely a hint for
+        * optimization. Let it slip for now until wildmatch is taught
+        * about dry-run mode and returns wildcard info.
+        */
+       if (opt->pathspec.has_wildcard)
+               die("BUG:%s:%d: wildcards are not supported",
+                   __FILE__, __LINE__);
+#endif
+
        /* Remove the file creation entry from the diff queue, and remember it */
        choice = q->queue[0];
        q->nr = 0;
@@ -207,15 +224,13 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
        DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       diff_opts.single_follow = opt->pathspec.raw[0];
+       diff_opts.single_follow = opt->pathspec.items[0].match;
        diff_opts.break_opt = opt->break_opt;
        diff_opts.rename_score = opt->rename_score;
-       paths[0] = NULL;
-       diff_tree_setup_paths(paths, &diff_opts);
        diff_setup_done(&diff_opts);
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
-       diff_tree_release_paths(&diff_opts);
+       free_pathspec(&diff_opts.pathspec);
 
        /* Go through the new set of filepairing, and see if we find a more interesting one */
        opt->found_follow = 0;
@@ -228,15 +243,18 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                 * the future!
                 */
                if ((p->status == 'R' || p->status == 'C') &&
-                   !strcmp(p->two->path, opt->pathspec.raw[0])) {
+                   !strcmp(p->two->path, opt->pathspec.items[0].match)) {
+                       const char *path[2];
+
                        /* Switch the file-pairs around */
                        q->queue[i] = choice;
                        choice = p;
 
                        /* Update the path we use from now on.. */
-                       diff_tree_release_paths(opt);
-                       opt->pathspec.raw[0] = xstrdup(p->one->path);
-                       diff_tree_setup_paths(opt->pathspec.raw, opt);
+                       path[0] = p->one->path;
+                       path[1] = NULL;
+                       free_pathspec(&opt->pathspec);
+                       parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC, 0, "", path);
 
                        /*
                         * The caller expects us to return a set of vanilla
@@ -310,13 +328,3 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
        free(tree);
        return retval;
 }
-
-void diff_tree_release_paths(struct diff_options *opt)
-{
-       free_pathspec(&opt->pathspec);
-}
-
-void diff_tree_setup_paths(const char **p, struct diff_options *opt)
-{
-       init_pathspec(&opt->pathspec, p);
-}
index c626135234f2fbf2d8711e32a020d2e228a307b2..5ece8c3477b11f3bf0c54196924ac24568309225 100644 (file)
@@ -3,6 +3,7 @@
 #include "unpack-trees.h"
 #include "dir.h"
 #include "tree.h"
+#include "pathspec.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
 {
@@ -487,13 +488,25 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
        return retval;
 }
 
-static int match_entry(const struct name_entry *entry, int pathlen,
+static int match_entry(const struct pathspec_item *item,
+                      const struct name_entry *entry, int pathlen,
                       const char *match, int matchlen,
                       enum interesting *never_interesting)
 {
        int m = -1; /* signals that we haven't called strncmp() */
 
-       if (*never_interesting != entry_not_interesting) {
+       if (item->magic & PATHSPEC_ICASE)
+               /*
+                * "Never interesting" trick requires exact
+                * matching. We could do something clever with inexact
+                * matching, but it's trickier (and not to forget that
+                * strcasecmp is locale-dependent, at least in
+                * glibc). Just disable it for now. It can't be worse
+                * than the wildcard's codepath of '[Tt][Hi][Is][Ss]'
+                * pattern.
+                */
+               *never_interesting = entry_not_interesting;
+       else if (*never_interesting != entry_not_interesting) {
                /*
                 * We have not seen any match that sorts later
                 * than the current path.
@@ -539,7 +552,7 @@ static int match_entry(const struct name_entry *entry, int pathlen,
                 * we cheated and did not do strncmp(), so we do
                 * that here.
                 */
-               m = strncmp(match, entry->path, pathlen);
+               m = ps_strncmp(item, match, entry->path, pathlen);
 
        /*
         * If common part matched earlier then it is a hit,
@@ -547,15 +560,39 @@ static int match_entry(const struct name_entry *entry, int pathlen,
         * leading directory and is shorter than match.
         */
        if (!m)
+               /*
+                * match_entry does not check if the prefix part is
+                * matched case-sensitively. If the entry is a
+                * directory and part of prefix, it'll be rematched
+                * eventually by basecmp with special treatment for
+                * the prefix.
+                */
                return 1;
 
        return 0;
 }
 
-static int match_dir_prefix(const char *base,
+/* :(icase)-aware string compare */
+static int basecmp(const struct pathspec_item *item,
+                  const char *base, const char *match, int len)
+{
+       if (item->magic & PATHSPEC_ICASE) {
+               int ret, n = len > item->prefix ? item->prefix : len;
+               ret = strncmp(base, match, n);
+               if (ret)
+                       return ret;
+               base += n;
+               match += n;
+               len -= n;
+       }
+       return ps_strncmp(item, base, match, len);
+}
+
+static int match_dir_prefix(const struct pathspec_item *item,
+                           const char *base,
                            const char *match, int matchlen)
 {
-       if (strncmp(base, match, matchlen))
+       if (basecmp(item, base, match, matchlen))
                return 0;
 
        /*
@@ -592,7 +629,7 @@ static int match_wildcard_base(const struct pathspec_item *item,
                 */
                if (baselen >= matchlen) {
                        *matched = matchlen;
-                       return !strncmp(base, match, matchlen);
+                       return !basecmp(item, base, match, matchlen);
                }
 
                dirlen = matchlen;
@@ -605,7 +642,7 @@ static int match_wildcard_base(const struct pathspec_item *item,
                 * base ends with '/' so we are sure it really matches
                 * directory
                 */
-               if (strncmp(base, match, baselen))
+               if (basecmp(item, base, match, baselen))
                        return 0;
                *matched = baselen;
        } else
@@ -634,8 +671,17 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
        enum interesting never_interesting = ps->has_wildcard ?
                entry_not_interesting : all_entries_not_interesting;
 
+       GUARD_PATHSPEC(ps,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE);
+
        if (!ps->nr) {
-               if (!ps->recursive || ps->max_depth == -1)
+               if (!ps->recursive ||
+                   !(ps->magic & PATHSPEC_MAXDEPTH) ||
+                   ps->max_depth == -1)
                        return all_entries_interesting;
                return within_depth(base->buf + base_offset, baselen,
                                    !!S_ISDIR(entry->mode),
@@ -653,10 +699,12 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
 
                if (baselen >= matchlen) {
                        /* If it doesn't match, move along... */
-                       if (!match_dir_prefix(base_str, match, matchlen))
+                       if (!match_dir_prefix(item, base_str, match, matchlen))
                                goto match_wildcards;
 
-                       if (!ps->recursive || ps->max_depth == -1)
+                       if (!ps->recursive ||
+                           !(ps->magic & PATHSPEC_MAXDEPTH) ||
+                           ps->max_depth == -1)
                                return all_entries_interesting;
 
                        return within_depth(base_str + matchlen + 1,
@@ -667,15 +715,14 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
                }
 
                /* Either there must be no base, or the base must match. */
-               if (baselen == 0 || !strncmp(base_str, match, baselen)) {
-                       if (match_entry(entry, pathlen,
+               if (baselen == 0 || !basecmp(item, base_str, match, baselen)) {
+                       if (match_entry(item, entry, pathlen,
                                        match + baselen, matchlen - baselen,
                                        &never_interesting))
                                return entry_interesting;
 
                        if (item->nowildcard_len < item->len) {
-                               if (!git_fnmatch(match + baselen, entry->path,
-                                                item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+                               if (!git_fnmatch(item, match + baselen, entry->path,
                                                 item->nowildcard_len - baselen))
                                        return entry_interesting;
 
@@ -716,8 +763,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
 
                strbuf_add(base, entry->path, pathlen);
 
-               if (!git_fnmatch(match, base->buf + base_offset,
-                                item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+               if (!git_fnmatch(item, match, base->buf + base_offset,
                                 item->nowildcard_len)) {
                        strbuf_setlen(base, base_offset + baselen);
                        return entry_interesting;
diff --git a/tree.c b/tree.c
index c0e568c08f670c45ab9615af2a41bda43569b5fd..c8c49d7b78174199da94d802e1ca7037866b5f04 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -47,7 +47,7 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int
 }
 
 static int read_tree_1(struct tree *tree, struct strbuf *base,
-                      int stage, struct pathspec *pathspec,
+                      int stage, const struct pathspec *pathspec,
                       read_tree_fn_t fn, void *context)
 {
        struct tree_desc desc;
@@ -116,7 +116,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
 
 int read_tree_recursive(struct tree *tree,
                        const char *base, int baselen,
-                       int stage, struct pathspec *pathspec,
+                       int stage, const struct pathspec *pathspec,
                        read_tree_fn_t fn, void *context)
 {
        struct strbuf sb = STRBUF_INIT;
@@ -225,6 +225,14 @@ int parse_tree(struct tree *item)
        return parse_tree_buffer(item, buffer, size);
 }
 
+void free_tree_buffer(struct tree *tree)
+{
+       free(tree->buffer);
+       tree->buffer = NULL;
+       tree->size = 0;
+       tree->object.parsed = 0;
+}
+
 struct tree *parse_tree_indirect(const unsigned char *sha1)
 {
        struct object *obj = parse_object(sha1);
diff --git a/tree.h b/tree.h
index 69bcb5e0ec27de6699e349b8dcec26f1cbc4e741..d84ac63e511c30ae4e1c8ee2b9719d67fd9424da 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -16,6 +16,7 @@ struct tree *lookup_tree(const unsigned char *sha1);
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
 
 int parse_tree(struct tree *tree);
+void free_tree_buffer(struct tree *tree);
 
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
@@ -25,7 +26,7 @@ typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const ch
 
 extern int read_tree_recursive(struct tree *tree,
                               const char *base, int baselen,
-                              int stage, struct pathspec *pathspec,
+                              int stage, const struct pathspec *pathspec,
                               read_tree_fn_t fn, void *context);
 
 extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec);
index bf01717015b662cec594d2d3c73e57f409037533..1a61e6f363d6a03b262ad5e5a28c58c2caf722d5 100644 (file)
@@ -1154,8 +1154,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
-       if (o->dst_index)
+       if (o->dst_index) {
+               discard_index(o->dst_index);
                *o->dst_index = o->result;
+       }
 
 done:
        clear_exclude_list(&el);
index 127e59a6037a14ad63648a727e873fe4a9e38381..b03492e664daa53f09d13787494c925af702d949 100644 (file)
@@ -10,6 +10,7 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "run-command.h"
+#include "connect.h"
 #include "sigchain.h"
 #include "version.h"
 #include "string-list.h"
diff --git a/urlmatch.c b/urlmatch.c
new file mode 100644 (file)
index 0000000..1db76c8
--- /dev/null
@@ -0,0 +1,535 @@
+#include "cache.h"
+#include "urlmatch.h"
+
+#define URL_ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+#define URL_DIGIT "0123456789"
+#define URL_ALPHADIGIT URL_ALPHA URL_DIGIT
+#define URL_SCHEME_CHARS URL_ALPHADIGIT "+.-"
+#define URL_HOST_CHARS URL_ALPHADIGIT ".-[:]" /* IPv6 literals need [:] */
+#define URL_UNSAFE_CHARS " <>\"%{}|\\^`" /* plus 0x00-0x1F,0x7F-0xFF */
+#define URL_GEN_RESERVED ":/?#[]@"
+#define URL_SUB_RESERVED "!$&'()*+,;="
+#define URL_RESERVED URL_GEN_RESERVED URL_SUB_RESERVED /* only allowed delims */
+
+static int append_normalized_escapes(struct strbuf *buf,
+                                    const char *from,
+                                    size_t from_len,
+                                    const char *esc_extra,
+                                    const char *esc_ok)
+{
+       /*
+        * Append to strbuf 'buf' characters from string 'from' with length
+        * 'from_len' while unescaping characters that do not need to be escaped
+        * and escaping characters that do.  The set of characters to escape
+        * (the complement of which is unescaped) starts out as the RFC 3986
+        * unsafe characters (0x00-0x1F,0x7F-0xFF," <>\"#%{}|\\^`").  If
+        * 'esc_extra' is not NULL, those additional characters will also always
+        * be escaped.  If 'esc_ok' is not NULL, those characters will be left
+        * escaped if found that way, but will not be unescaped otherwise (used
+        * for delimiters).  If a %-escape sequence is encountered that is not
+        * followed by 2 hexadecimal digits, the sequence is invalid and
+        * false (0) will be returned.  Otherwise true (1) will be returned for
+        * success.
+        *
+        * Note that all %-escape sequences will be normalized to UPPERCASE
+        * as indicated in RFC 3986.  Unless included in esc_extra or esc_ok
+        * alphanumerics and "-._~" will always be unescaped as per RFC 3986.
+        */
+
+       while (from_len) {
+               int ch = *from++;
+               int was_esc = 0;
+
+               from_len--;
+               if (ch == '%') {
+                       if (from_len < 2 ||
+                           !isxdigit((unsigned char)from[0]) ||
+                           !isxdigit((unsigned char)from[1]))
+                               return 0;
+                       ch = hexval_table[(unsigned char)*from++] << 4;
+                       ch |= hexval_table[(unsigned char)*from++];
+                       from_len -= 2;
+                       was_esc = 1;
+               }
+               if ((unsigned char)ch <= 0x1F || (unsigned char)ch >= 0x7F ||
+                   strchr(URL_UNSAFE_CHARS, ch) ||
+                   (esc_extra && strchr(esc_extra, ch)) ||
+                   (was_esc && strchr(esc_ok, ch)))
+                       strbuf_addf(buf, "%%%02X", (unsigned char)ch);
+               else
+                       strbuf_addch(buf, ch);
+       }
+
+       return 1;
+}
+
+char *url_normalize(const char *url, struct url_info *out_info)
+{
+       /*
+        * Normalize NUL-terminated url using the following rules:
+        *
+        * 1. Case-insensitive parts of url will be converted to lower case
+        * 2. %-encoded characters that do not need to be will be unencoded
+        * 3. Characters that are not %-encoded and must be will be encoded
+        * 4. All %-encodings will be converted to upper case hexadecimal
+        * 5. Leading 0s are removed from port numbers
+        * 6. If the default port for the scheme is given it will be removed
+        * 7. A path part (including empty) not starting with '/' has one added
+        * 8. Any dot segments (. or ..) in the path are resolved and removed
+        * 9. IPv6 host literals are allowed (but not normalized or validated)
+        *
+        * The rules are based on information in RFC 3986.
+        *
+        * Please note this function requires a full URL including a scheme
+        * and host part (except for file: URLs which may have an empty host).
+        *
+        * The return value is a newly allocated string that must be freed
+        * or NULL if the url is not valid.
+        *
+        * If out_info is non-NULL, the url and err fields therein will always
+        * be set.  If a non-NULL value is returned, it will be stored in
+        * out_info->url as well, out_info->err will be set to NULL and the
+        * other fields of *out_info will also be filled in.  If a NULL value
+        * is returned, NULL will be stored in out_info->url and out_info->err
+        * will be set to a brief, translated, error message, but no other
+        * fields will be filled in.
+        *
+        * This is NOT a URL validation function.  Full URL validation is NOT
+        * performed.  Some invalid host names are passed through this function
+        * undetected.  However, most all other problems that make a URL invalid
+        * will be detected (including a missing host for non file: URLs).
+        */
+
+       size_t url_len = strlen(url);
+       struct strbuf norm;
+       size_t spanned;
+       size_t scheme_len, user_off=0, user_len=0, passwd_off=0, passwd_len=0;
+       size_t host_off=0, host_len=0, port_len=0, path_off, path_len, result_len;
+       const char *slash_ptr, *at_ptr, *colon_ptr, *path_start;
+       char *result;
+
+       /*
+        * Copy lowercased scheme and :// suffix, %-escapes are not allowed
+        * First character of scheme must be URL_ALPHA
+        */
+       spanned = strspn(url, URL_SCHEME_CHARS);
+       if (!spanned || !isalpha(url[0]) || spanned + 3 > url_len ||
+           url[spanned] != ':' || url[spanned+1] != '/' || url[spanned+2] != '/') {
+               if (out_info) {
+                       out_info->url = NULL;
+                       out_info->err = _("invalid URL scheme name or missing '://' suffix");
+               }
+               return NULL; /* Bad scheme and/or missing "://" part */
+       }
+       strbuf_init(&norm, url_len);
+       scheme_len = spanned;
+       spanned += 3;
+       url_len -= spanned;
+       while (spanned--)
+               strbuf_addch(&norm, tolower(*url++));
+
+
+       /*
+        * Copy any username:password if present normalizing %-escapes
+        */
+       at_ptr = strchr(url, '@');
+       slash_ptr = url + strcspn(url, "/?#");
+       if (at_ptr && at_ptr < slash_ptr) {
+               user_off = norm.len;
+               if (at_ptr > url) {
+                       if (!append_normalized_escapes(&norm, url, at_ptr - url,
+                                                      "", URL_RESERVED)) {
+                               if (out_info) {
+                                       out_info->url = NULL;
+                                       out_info->err = _("invalid %XX escape sequence");
+                               }
+                               strbuf_release(&norm);
+                               return NULL;
+                       }
+                       colon_ptr = strchr(norm.buf + scheme_len + 3, ':');
+                       if (colon_ptr) {
+                               passwd_off = (colon_ptr + 1) - norm.buf;
+                               passwd_len = norm.len - passwd_off;
+                               user_len = (passwd_off - 1) - (scheme_len + 3);
+                       } else {
+                               user_len = norm.len - (scheme_len + 3);
+                       }
+               }
+               strbuf_addch(&norm, '@');
+               url_len -= (++at_ptr - url);
+               url = at_ptr;
+       }
+
+
+       /*
+        * Copy the host part excluding any port part, no %-escapes allowed
+        */
+       if (!url_len || strchr(":/?#", *url)) {
+               /* Missing host invalid for all URL schemes except file */
+               if (strncmp(norm.buf, "file:", 5)) {
+                       if (out_info) {
+                               out_info->url = NULL;
+                               out_info->err = _("missing host and scheme is not 'file:'");
+                       }
+                       strbuf_release(&norm);
+                       return NULL;
+               }
+       } else {
+               host_off = norm.len;
+       }
+       colon_ptr = slash_ptr - 1;
+       while (colon_ptr > url && *colon_ptr != ':' && *colon_ptr != ']')
+               colon_ptr--;
+       if (*colon_ptr != ':') {
+               colon_ptr = slash_ptr;
+       } else if (!host_off && colon_ptr < slash_ptr && colon_ptr + 1 != slash_ptr) {
+               /* file: URLs may not have a port number */
+               if (out_info) {
+                       out_info->url = NULL;
+                       out_info->err = _("a 'file:' URL may not have a port number");
+               }
+               strbuf_release(&norm);
+               return NULL;
+       }
+       spanned = strspn(url, URL_HOST_CHARS);
+       if (spanned < colon_ptr - url) {
+               /* Host name has invalid characters */
+               if (out_info) {
+                       out_info->url = NULL;
+                       out_info->err = _("invalid characters in host name");
+               }
+               strbuf_release(&norm);
+               return NULL;
+       }
+       while (url < colon_ptr) {
+               strbuf_addch(&norm, tolower(*url++));
+               url_len--;
+       }
+
+
+       /*
+        * Check the port part and copy if not the default (after removing any
+        * leading 0s); no %-escapes allowed
+        */
+       if (colon_ptr < slash_ptr) {
+               /* skip the ':' and leading 0s but not the last one if all 0s */
+               url++;
+               url += strspn(url, "0");
+               if (url == slash_ptr && url[-1] == '0')
+                       url--;
+               if (url == slash_ptr) {
+                       /* Skip ":" port with no number, it's same as default */
+               } else if (slash_ptr - url == 2 &&
+                          !strncmp(norm.buf, "http:", 5) &&
+                          !strncmp(url, "80", 2)) {
+                       /* Skip http :80 as it's the default */
+               } else if (slash_ptr - url == 3 &&
+                          !strncmp(norm.buf, "https:", 6) &&
+                          !strncmp(url, "443", 3)) {
+                       /* Skip https :443 as it's the default */
+               } else {
+                       /*
+                        * Port number must be all digits with leading 0s removed
+                        * and since all the protocols we deal with have a 16-bit
+                        * port number it must also be in the range 1..65535
+                        * 0 is not allowed because that means "next available"
+                        * on just about every system and therefore cannot be used
+                        */
+                       unsigned long pnum = 0;
+                       spanned = strspn(url, URL_DIGIT);
+                       if (spanned < slash_ptr - url) {
+                               /* port number has invalid characters */
+                               if (out_info) {
+                                       out_info->url = NULL;
+                                       out_info->err = _("invalid port number");
+                               }
+                               strbuf_release(&norm);
+                               return NULL;
+                       }
+                       if (slash_ptr - url <= 5)
+                               pnum = strtoul(url, NULL, 10);
+                       if (pnum == 0 || pnum > 65535) {
+                               /* port number not in range 1..65535 */
+                               if (out_info) {
+                                       out_info->url = NULL;
+                                       out_info->err = _("invalid port number");
+                               }
+                               strbuf_release(&norm);
+                               return NULL;
+                       }
+                       strbuf_addch(&norm, ':');
+                       strbuf_add(&norm, url, slash_ptr - url);
+                       port_len = slash_ptr - url;
+               }
+               url_len -= slash_ptr - colon_ptr;
+               url = slash_ptr;
+       }
+       if (host_off)
+               host_len = norm.len - host_off;
+
+
+       /*
+        * Now copy the path resolving any . and .. segments being careful not
+        * to corrupt the URL by unescaping any delimiters, but do add an
+        * initial '/' if it's missing and do normalize any %-escape sequences.
+        */
+       path_off = norm.len;
+       path_start = norm.buf + path_off;
+       strbuf_addch(&norm, '/');
+       if (*url == '/') {
+               url++;
+               url_len--;
+       }
+       for (;;) {
+               const char *seg_start = norm.buf + norm.len;
+               const char *next_slash = url + strcspn(url, "/?#");
+               int skip_add_slash = 0;
+               /*
+                * RFC 3689 indicates that any . or .. segments should be
+                * unescaped before being checked for.
+                */
+               if (!append_normalized_escapes(&norm, url, next_slash - url, "",
+                                              URL_RESERVED)) {
+                       if (out_info) {
+                               out_info->url = NULL;
+                               out_info->err = _("invalid %XX escape sequence");
+                       }
+                       strbuf_release(&norm);
+                       return NULL;
+               }
+               if (!strcmp(seg_start, ".")) {
+                       /* ignore a . segment; be careful not to remove initial '/' */
+                       if (seg_start == path_start + 1) {
+                               strbuf_setlen(&norm, norm.len - 1);
+                               skip_add_slash = 1;
+                       } else {
+                               strbuf_setlen(&norm, norm.len - 2);
+                       }
+               } else if (!strcmp(seg_start, "..")) {
+                       /*
+                        * ignore a .. segment and remove the previous segment;
+                        * be careful not to remove initial '/' from path
+                        */
+                       const char *prev_slash = norm.buf + norm.len - 3;
+                       if (prev_slash == path_start) {
+                               /* invalid .. because no previous segment to remove */
+                               if (out_info) {
+                                       out_info->url = NULL;
+                                       out_info->err = _("invalid '..' path segment");
+                               }
+                               strbuf_release(&norm);
+                               return NULL;
+                       }
+                       while (*--prev_slash != '/') {}
+                       if (prev_slash == path_start) {
+                               strbuf_setlen(&norm, prev_slash - norm.buf + 1);
+                               skip_add_slash = 1;
+                       } else {
+                               strbuf_setlen(&norm, prev_slash - norm.buf);
+                       }
+               }
+               url_len -= next_slash - url;
+               url = next_slash;
+               /* if the next char is not '/' done with the path */
+               if (*url != '/')
+                       break;
+               url++;
+               url_len--;
+               if (!skip_add_slash)
+                       strbuf_addch(&norm, '/');
+       }
+       path_len = norm.len - path_off;
+
+
+       /*
+        * Now simply copy the rest, if any, only normalizing %-escapes and
+        * being careful not to corrupt the URL by unescaping any delimiters.
+        */
+       if (*url) {
+               if (!append_normalized_escapes(&norm, url, url_len, "", URL_RESERVED)) {
+                       if (out_info) {
+                               out_info->url = NULL;
+                               out_info->err = _("invalid %XX escape sequence");
+                       }
+                       strbuf_release(&norm);
+                       return NULL;
+               }
+       }
+
+
+       result = strbuf_detach(&norm, &result_len);
+       if (out_info) {
+               out_info->url = result;
+               out_info->err = NULL;
+               out_info->url_len = result_len;
+               out_info->scheme_len = scheme_len;
+               out_info->user_off = user_off;
+               out_info->user_len = user_len;
+               out_info->passwd_off = passwd_off;
+               out_info->passwd_len = passwd_len;
+               out_info->host_off = host_off;
+               out_info->host_len = host_len;
+               out_info->port_len = port_len;
+               out_info->path_off = path_off;
+               out_info->path_len = path_len;
+       }
+       return result;
+}
+
+static size_t url_match_prefix(const char *url,
+                              const char *url_prefix,
+                              size_t url_prefix_len)
+{
+       /*
+        * url_prefix matches url if url_prefix is an exact match for url or it
+        * is a prefix of url and the match ends on a path component boundary.
+        * Both url and url_prefix are considered to have an implicit '/' on the
+        * end for matching purposes if they do not already.
+        *
+        * url must be NUL terminated.  url_prefix_len is the length of
+        * url_prefix which need not be NUL terminated.
+        *
+        * The return value is the length of the match in characters (including
+        * the final '/' even if it's implicit) or 0 for no match.
+        *
+        * Passing NULL as url and/or url_prefix will always cause 0 to be
+        * returned without causing any faults.
+        */
+       if (!url || !url_prefix)
+               return 0;
+       if (!url_prefix_len || (url_prefix_len == 1 && *url_prefix == '/'))
+               return (!*url || *url == '/') ? 1 : 0;
+       if (url_prefix[url_prefix_len - 1] == '/')
+               url_prefix_len--;
+       if (strncmp(url, url_prefix, url_prefix_len))
+               return 0;
+       if ((strlen(url) == url_prefix_len) || (url[url_prefix_len] == '/'))
+               return url_prefix_len + 1;
+       return 0;
+}
+
+int match_urls(const struct url_info *url,
+              const struct url_info *url_prefix,
+              int *exactusermatch)
+{
+       /*
+        * url_prefix matches url if the scheme, host and port of url_prefix
+        * are the same as those of url and the path portion of url_prefix
+        * is the same as the path portion of url or it is a prefix that
+        * matches at a '/' boundary.  If url_prefix contains a user name,
+        * that must also exactly match the user name in url.
+        *
+        * If the user, host, port and path match in this fashion, the returned
+        * value is the length of the path match including any implicit
+        * final '/'.  For example, "http://me@example.com/path" is matched by
+        * "http://example.com" with a path length of 1.
+        *
+        * If there is a match and exactusermatch is not NULL, then
+        * *exactusermatch will be set to true if both url and url_prefix
+        * contained a user name or false if url_prefix did not have a
+        * user name.  If there is no match *exactusermatch is left untouched.
+        */
+       int usermatched = 0;
+       int pathmatchlen;
+
+       if (!url || !url_prefix || !url->url || !url_prefix->url)
+               return 0;
+
+       /* check the scheme */
+       if (url_prefix->scheme_len != url->scheme_len ||
+           strncmp(url->url, url_prefix->url, url->scheme_len))
+               return 0; /* schemes do not match */
+
+       /* check the user name if url_prefix has one */
+       if (url_prefix->user_off) {
+               if (!url->user_off || url->user_len != url_prefix->user_len ||
+                   strncmp(url->url + url->user_off,
+                           url_prefix->url + url_prefix->user_off,
+                           url->user_len))
+                       return 0; /* url_prefix has a user but it's not a match */
+               usermatched = 1;
+       }
+
+       /* check the host and port */
+       if (url_prefix->host_len != url->host_len ||
+           strncmp(url->url + url->host_off,
+                   url_prefix->url + url_prefix->host_off, url->host_len))
+               return 0; /* host names and/or ports do not match */
+
+       /* check the path */
+       pathmatchlen = url_match_prefix(
+               url->url + url->path_off,
+               url_prefix->url + url_prefix->path_off,
+               url_prefix->url_len - url_prefix->path_off);
+
+       if (pathmatchlen && exactusermatch)
+               *exactusermatch = usermatched;
+       return pathmatchlen;
+}
+
+int urlmatch_config_entry(const char *var, const char *value, void *cb)
+{
+       struct string_list_item *item;
+       struct urlmatch_config *collect = cb;
+       struct urlmatch_item *matched;
+       struct url_info *url = &collect->url;
+       const char *key, *dot;
+       struct strbuf synthkey = STRBUF_INIT;
+       size_t matched_len = 0;
+       int user_matched = 0;
+       int retval;
+
+       key = skip_prefix(var, collect->section);
+       if (!key || *(key++) != '.') {
+               if (collect->cascade_fn)
+                       return collect->cascade_fn(var, value, cb);
+               return 0; /* not interested */
+       }
+       dot = strrchr(key, '.');
+       if (dot) {
+               char *config_url, *norm_url;
+               struct url_info norm_info;
+
+               config_url = xmemdupz(key, dot - key);
+               norm_url = url_normalize(config_url, &norm_info);
+               free(config_url);
+               if (!norm_url)
+                       return 0;
+               matched_len = match_urls(url, &norm_info, &user_matched);
+               free(norm_url);
+               if (!matched_len)
+                       return 0;
+               key = dot + 1;
+       }
+
+       if (collect->key && strcmp(key, collect->key))
+               return 0;
+
+       item = string_list_insert(&collect->vars, key);
+       if (!item->util) {
+               matched = xcalloc(1, sizeof(*matched));
+               item->util = matched;
+       } else {
+               matched = item->util;
+               /*
+                * Is our match shorter?  Is our match the same
+                * length, and without user while the current
+                * candidate is with user?  Then we cannot use it.
+                */
+               if (matched_len < matched->matched_len ||
+                   ((matched_len == matched->matched_len) &&
+                    (!user_matched && matched->user_matched)))
+                       return 0;
+               /* Otherwise, replace it with this one. */
+       }
+
+       matched->matched_len = matched_len;
+       matched->user_matched = user_matched;
+       strbuf_addstr(&synthkey, collect->section);
+       strbuf_addch(&synthkey, '.');
+       strbuf_addstr(&synthkey, key);
+       retval = collect->collect_fn(synthkey.buf, value, collect->cb);
+
+       strbuf_release(&synthkey);
+       return retval;
+}
diff --git a/urlmatch.h b/urlmatch.h
new file mode 100644 (file)
index 0000000..b461dfd
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef URL_MATCH_H
+#include "string-list.h"
+
+struct url_info {
+       /* normalized url on success, must be freed, otherwise NULL */
+       char *url;
+       /* if !url, a brief reason for the failure, otherwise NULL */
+       const char *err;
+
+       /* the rest of the fields are only set if url != NULL */
+
+       size_t url_len;         /* total length of url (which is now normalized) */
+       size_t scheme_len;      /* length of scheme name (excluding final :) */
+       size_t user_off;        /* offset into url to start of user name (0 => none) */
+       size_t user_len;        /* length of user name; if user_off != 0 but
+                                  user_len == 0, an empty user name was given */
+       size_t passwd_off;      /* offset into url to start of passwd (0 => none) */
+       size_t passwd_len;      /* length of passwd; if passwd_off != 0 but
+                                  passwd_len == 0, an empty passwd was given */
+       size_t host_off;        /* offset into url to start of host name (0 => none) */
+       size_t host_len;        /* length of host name; this INCLUDES any ':portnum';
+                                * file urls may have host_len == 0 */
+       size_t port_len;        /* if a portnum is present (port_len != 0), it has
+                                * this length (excluding the leading ':') at the
+                                * end of the host name (always 0 for file urls) */
+       size_t path_off;        /* offset into url to the start of the url path;
+                                * this will always point to a '/' character
+                                * after the url has been normalized */
+       size_t path_len;        /* length of path portion excluding any trailing
+                                * '?...' and '#...' portion; will always be >= 1 */
+};
+
+extern char *url_normalize(const char *, struct url_info *);
+extern int match_urls(const struct url_info *url, const struct url_info *url_prefix, int *exactusermatch);
+
+struct urlmatch_item {
+       size_t matched_len;
+       char user_matched;
+};
+
+struct urlmatch_config {
+       struct string_list vars;
+       struct url_info url;
+       const char *section;
+       const char *key;
+
+       void *cb;
+       int (*collect_fn)(const char *var, const char *value, void *cb);
+       int (*cascade_fn)(const char *var, const char *value, void *cb);
+};
+
+extern int urlmatch_config_entry(const char *var, const char *value, void *cb);
+
+#endif /* URL_MATCH_H */
index be389dc9bf5161c31be29e3a72264fd6120a0bbc..633596e06fcaa1154980f95c858c61379c968d49 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -56,10 +56,7 @@ static int process_tree(struct walker *walker, struct tree *tree)
                if (!obj || process(walker, obj))
                        return -1;
        }
-       free(tree->buffer);
-       tree->buffer = NULL;
-       tree->size = 0;
-       tree->object.parsed = 0;
+       free_tree_buffer(tree);
        return 0;
 }
 
index 6a015de5f0564e1ccc9c8cffca891f4ddf4a3ac3..f92b14759833b1ae77070b4188d8ad8ea8d3799e 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -130,6 +130,14 @@ void *xcalloc(size_t nmemb, size_t size)
        return ret;
 }
 
+/*
+ * Limit size of IO chunks, because huge chunks only cause pain.  OS X
+ * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
+ * the absense of bugs, large chunks can result in bad latencies when
+ * you decide to kill the process.
+ */
+#define MAX_IO_SIZE (8*1024*1024)
+
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -138,6 +146,8 @@ void *xcalloc(size_t nmemb, size_t size)
 ssize_t xread(int fd, void *buf, size_t len)
 {
        ssize_t nr;
+       if (len > MAX_IO_SIZE)
+           len = MAX_IO_SIZE;
        while (1) {
                nr = read(fd, buf, len);
                if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
@@ -154,6 +164,8 @@ ssize_t xread(int fd, void *buf, size_t len)
 ssize_t xwrite(int fd, const void *buf, size_t len)
 {
        ssize_t nr;
+       if (len > MAX_IO_SIZE)
+           len = MAX_IO_SIZE;
        while (1) {
                nr = write(fd, buf, len);
                if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
index cb24f1fa9b9f9632e5fd92bbcceb44008619fa14..ff4b32426a36db38fba9e4cc51e09a988efe34a6 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "pathspec.h"
 #include "wt-status.h"
 #include "object.h"
 #include "dir.h"
@@ -438,7 +439,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
-       init_pathspec(&rev.prune_data, s->pathspec);
+       copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_files(&rev, 0);
 }
 
@@ -463,22 +464,20 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 200;
        rev.diffopt.break_opt = 0;
-       init_pathspec(&rev.prune_data, s->pathspec);
+       copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_index(&rev, 1);
 }
 
 static void wt_status_collect_changes_initial(struct wt_status *s)
 {
-       struct pathspec pathspec;
        int i;
 
-       init_pathspec(&pathspec, s->pathspec);
        for (i = 0; i < active_nr; i++) {
                struct string_list_item *it;
                struct wt_status_change_data *d;
                const struct cache_entry *ce = active_cache[i];
 
-               if (!ce_path_match(ce, &pathspec))
+               if (!ce_path_match(ce, &s->pathspec))
                        continue;
                it = string_list_insert(&s->change, ce->name);
                d = it->util;
@@ -493,7 +492,6 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
                else
                        d->index_status = DIFF_STATUS_ADDED;
        }
-       free_pathspec(&pathspec);
 }
 
 static void wt_status_collect_untracked(struct wt_status *s)
@@ -516,12 +514,12 @@ static void wt_status_collect_untracked(struct wt_status *s)
                dir.flags |= DIR_SHOW_IGNORED_TOO;
        setup_standard_excludes(&dir);
 
-       fill_directory(&dir, s->pathspec);
+       fill_directory(&dir, &s->pathspec);
 
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
                if (cache_name_is_other(ent->name, ent->len) &&
-                   match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+                   match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
                        string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
@@ -529,7 +527,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
        for (i = 0; i < dir.ignored_nr; i++) {
                struct dir_entry *ent = dir.ignored[i];
                if (cache_name_is_other(ent->name, ent->len) &&
-                   match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+                   match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL))
                        string_list_insert(&s->ignored, ent->name);
                free(ent);
        }
index fb7152e187a2fe7c080aab1d84b7fd2cbe6c47a1..9966c13deb678857d5fdb0c3648865fa7189686b 100644 (file)
@@ -44,7 +44,7 @@ struct wt_status {
        int is_initial;
        char *branch;
        const char *reference;
-       const char **pathspec;
+       struct pathspec pathspec;
        int verbose;
        int amend;
        enum commit_whence whence;