summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/CodingGuidelines8
-rw-r--r--Documentation/RelNotes/1.8.5.txt225
-rw-r--r--Documentation/SubmittingPatches15
-rw-r--r--Documentation/blame-options.txt10
-rw-r--r--Documentation/config.txt99
-rw-r--r--Documentation/git-blame.txt10
-rw-r--r--Documentation/git-cat-file.txt14
-rw-r--r--Documentation/git-check-attr.txt9
-rw-r--r--Documentation/git-config.txt29
-rw-r--r--Documentation/git-fast-import.txt4
-rw-r--r--Documentation/git-fetch-pack.txt4
-rw-r--r--Documentation/git-format-patch.txt1
-rw-r--r--Documentation/git-gc.txt6
-rw-r--r--Documentation/git-log.txt5
-rw-r--r--Documentation/git-merge-file.txt5
-rw-r--r--Documentation/git-mv.txt10
-rw-r--r--Documentation/git-pull.txt18
-rw-r--r--Documentation/git-push.txt77
-rw-r--r--Documentation/git-rev-parse.txt104
-rw-r--r--Documentation/git-rm.txt8
-rw-r--r--Documentation/git-whatchanged.txt40
-rw-r--r--Documentation/git.txt33
-rw-r--r--Documentation/gitcore-tutorial.txt39
-rw-r--r--Documentation/gitremote-helpers.txt21
-rw-r--r--Documentation/glossary-content.txt52
-rw-r--r--Documentation/line-range-format.txt16
-rw-r--r--Documentation/technical/api-setup.txt38
-rw-r--r--Documentation/technical/http-protocol.txt503
-rw-r--r--Documentation/technical/pack-heuristics.txt6
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile14
l---------RelNotes2
-rw-r--r--archive.c18
-rw-r--r--archive.h4
-rw-r--r--builtin/add.c160
-rw-r--r--builtin/apply.c24
-rw-r--r--builtin/bisect--helper.c8
-rw-r--r--builtin/blame.c115
-rw-r--r--builtin/branch.c13
-rw-r--r--builtin/cat-file.c31
-rw-r--r--builtin/check-attr.c26
-rw-r--r--builtin/check-ignore.c55
-rw-r--r--builtin/checkout-index.c8
-rw-r--r--builtin/checkout.c60
-rw-r--r--builtin/clean.c30
-rw-r--r--builtin/clone.c23
-rw-r--r--builtin/commit.c85
-rw-r--r--builtin/config.c153
-rw-r--r--builtin/describe.c20
-rw-r--r--builtin/diff-files.c2
-rw-r--r--builtin/diff-index.c2
-rw-r--r--builtin/diff.c6
-rw-r--r--builtin/fast-export.c10
-rw-r--r--builtin/fetch-pack.c11
-rw-r--r--builtin/fetch.c142
-rw-r--r--builtin/for-each-ref.c13
-rw-r--r--builtin/fsck.c16
-rw-r--r--builtin/gc.c71
-rw-r--r--builtin/grep.c48
-rw-r--r--builtin/hash-object.c8
-rw-r--r--builtin/log.c19
-rw-r--r--builtin/ls-files.c101
-rw-r--r--builtin/ls-tree.c19
-rw-r--r--builtin/merge-base.c10
-rw-r--r--builtin/merge-file.c2
-rw-r--r--builtin/merge.c12
-rw-r--r--builtin/mv.c141
-rw-r--r--builtin/name-rev.c14
-rw-r--r--builtin/notes.c12
-rw-r--r--builtin/pack-objects.c2
-rw-r--r--builtin/push.c24
-rw-r--r--builtin/receive-pack.c9
-rw-r--r--builtin/remote.c28
-rw-r--r--builtin/replace.c6
-rw-r--r--builtin/rerere.c8
-rw-r--r--builtin/reset.c35
-rw-r--r--builtin/rev-parse.c32
-rw-r--r--builtin/revert.c62
-rw-r--r--builtin/rm.c52
-rw-r--r--builtin/send-pack.c26
-rw-r--r--builtin/shortlog.c12
-rw-r--r--builtin/show-branch.c28
-rw-r--r--builtin/show-ref.c15
-rw-r--r--builtin/tag.c31
-rw-r--r--builtin/tar-tree.c11
-rw-r--r--builtin/update-index.c6
-rw-r--r--builtin/update-ref.c4
-rw-r--r--bulk-checkin.c2
-rw-r--r--cache.h108
-rw-r--r--combine-diff.c7
-rw-r--r--commit.c16
-rw-r--r--commit.h5
-rw-r--r--compat/apple-common-crypto.h86
-rw-r--r--compat/clipped-write.c13
-rw-r--r--compat/mingw.c6
-rw-r--r--config.c84
-rw-r--r--config.mak.uname1
-rw-r--r--connect.c1
-rw-r--r--connect.h13
-rw-r--r--contrib/completion/git-completion.bash2
-rw-r--r--contrib/completion/git-prompt.sh6
-rwxr-xr-xcontrib/contacts/git-contacts31
-rwxr-xr-xcontrib/examples/git-log.sh15
-rwxr-xr-xcontrib/examples/git-whatchanged.sh28
-rwxr-xr-xcontrib/hooks/post-receive-email15
-rw-r--r--contrib/mw-to-git/Makefile10
-rwxr-xr-xcontrib/mw-to-git/git-remote-mediawiki.perl4
-rwxr-xr-xcontrib/remote-helpers/git-remote-bzr33
-rwxr-xr-xcontrib/remote-helpers/git-remote-hg25
-rw-r--r--diff-delta.c2
-rw-r--r--diff-lib.c11
-rw-r--r--diff-no-index.c7
-rw-r--r--diff.c131
-rw-r--r--diff.h10
-rw-r--r--dir.c371
-rw-r--r--dir.h21
-rw-r--r--editor.c2
-rw-r--r--fast-import.c58
-rw-r--r--fetch-pack.c1
-rw-r--r--fetch-pack.h1
-rw-r--r--git-compat-util.h17
-rwxr-xr-xgit-p4.py8
-rwxr-xr-xgit-pull.sh31
-rw-r--r--git-rebase--interactive.sh35
-rwxr-xr-xgit-remote-testgit.sh1
-rw-r--r--git-sh-setup.sh2
-rw-r--r--git.c12
-rwxr-xr-xgitweb/gitweb.perl6
-rw-r--r--gitweb/static/gitweb.css6
-rw-r--r--http.c23
-rw-r--r--imap-send.c14
-rw-r--r--line-log.c31
-rw-r--r--line-log.h12
-rw-r--r--line-range.c66
-rw-r--r--line-range.h5
-rw-r--r--log-tree.c2
-rw-r--r--mailmap.c24
-rw-r--r--merge-recursive.c2
-rw-r--r--notes-merge.c4
-rw-r--r--notes-utils.h2
-rw-r--r--parse-options.c58
-rw-r--r--parse-options.h8
-rw-r--r--path.c15
-rw-r--r--pathspec.c451
-rw-r--r--pathspec.h88
-rw-r--r--perl/Git.pm31
-rw-r--r--perl/Git/SVN/Fetcher.pm6
-rw-r--r--preload-index.c21
-rw-r--r--quote.c61
-rw-r--r--quote.h8
-rw-r--r--read-cache.c35
-rw-r--r--refs.c8
-rw-r--r--remote-curl.c27
-rw-r--r--remote.c178
-rw-r--r--remote.h84
-rw-r--r--rerere.c7
-rw-r--r--rerere.h4
-rw-r--r--resolve-undo.c4
-rw-r--r--resolve-undo.h2
-rw-r--r--revision.c76
-rw-r--r--revision.h20
-rw-r--r--send-pack.c2
-rw-r--r--setup.c173
-rw-r--r--sha1_file.c100
-rw-r--r--submodule.c155
-rw-r--r--submodule.h5
-rw-r--r--t/.gitattributes1
-rw-r--r--t/annotate-tests.sh260
-rw-r--r--t/lib-httpd.sh19
-rw-r--r--t/lib-httpd/apache.conf8
-rwxr-xr-xt/t0008-ignores.sh8
-rwxr-xr-xt/t0021-conversion.sh14
-rwxr-xr-xt/t0070-fundamental.sh4
-rwxr-xr-xt/t0110-urlmatch-normalization.sh177
-rw-r--r--t/t0110/README9
-rw-r--r--t/t0110/url-11
-rw-r--r--t/t0110/url-101
-rw-r--r--t/t0110/url-111
-rw-r--r--t/t0110/url-21
-rw-r--r--t/t0110/url-31
-rw-r--r--t/t0110/url-41
-rw-r--r--t/t0110/url-51
-rw-r--r--t/t0110/url-61
-rw-r--r--t/t0110/url-71
-rw-r--r--t/t0110/url-81
-rw-r--r--t/t0110/url-91
-rwxr-xr-xt/t1006-cat-file.sh15
-rwxr-xr-xt/t1300-repo-config.sh38
-rwxr-xr-xt/t1411-reflog-show.sh22
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh27
-rwxr-xr-xt/t3404-rebase-interactive.sh102
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh23
-rwxr-xr-xt/t3501-revert-cherry-pick.sh2
-rwxr-xr-xt/t3506-cherry-pick-ff.sh2
-rwxr-xr-xt/t3509-cherry-pick-merge-df.sh2
-rwxr-xr-xt/t3600-rm.sh98
-rwxr-xr-xt/t4055-diff-context.sh2
-rwxr-xr-xt/t4203-mailmap.sh16
-rwxr-xr-xt/t4211-line-log.sh33
-rwxr-xr-xt/t5510-fetch.sh82
-rwxr-xr-xt/t5516-fetch-push.sh17
-rwxr-xr-xt/t5520-pull.sh89
-rwxr-xr-xt/t5533-push-cas.sh189
-rwxr-xr-xt/t5541-http-push.sh2
-rwxr-xr-xt/t5551-http-fetch.sh16
-rwxr-xr-xt/t5801-remote-helpers.sh11
-rwxr-xr-xt/t5802-connect-helper.sh72
-rwxr-xr-xt/t6012-rev-list-simplify.sh6
-rwxr-xr-xt/t6022-merge-rename.sh14
-rwxr-xr-xt/t6130-pathspec-noglob.sh87
-rwxr-xr-xt/t6131-pathspec-icase.sh103
-rwxr-xr-xt/t7001-mv.sh128
-rwxr-xr-xt/t7400-submodule-basic.sh24
-rwxr-xr-xt/t7610-mergetool.sh6
-rwxr-xr-xt/t9300-fast-import.sh65
-rwxr-xr-xt/t9902-completion.sh2
-rwxr-xr-xt/t9903-bash-prompt.sh23
-rw-r--r--test-urlmatch-normalization.c50
-rw-r--r--transport-helper.c47
-rw-r--r--transport.c15
-rw-r--r--transport.h11
-rw-r--r--tree-diff.c48
-rw-r--r--tree-walk.c78
-rw-r--r--tree.c4
-rw-r--r--tree.h2
-rw-r--r--unpack-trees.c4
-rw-r--r--upload-pack.c1
-rw-r--r--urlmatch.c535
-rw-r--r--urlmatch.h54
-rw-r--r--wrapper.c12
-rw-r--r--wt-status.c16
-rw-r--r--wt-status.h2
233 files changed, 7112 insertions, 1963 deletions
diff --git a/.gitignore b/.gitignore
index 6b1fd1b..66199ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -202,6 +202,7 @@
/test-string-list
/test-subprocess
/test-svn-fe
+/test-urlmatch-normalization
/test-wildmatch
/common-cmds.h
*.tar.gz
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 559d5f9..e5ca3b7 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -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
index 0000000..d6fb0c0
--- /dev/null
+++ b/Documentation/RelNotes/1.8.5.txt
@@ -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).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index d0a4733..7055576 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -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.
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 4e55b15..0cebc4f 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -11,12 +11,12 @@
-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::
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ec57a15..0fa3f89 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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
@@ -766,6 +764,10 @@ branch.<name>.rebase::
"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]
for details).
@@ -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
@@ -1826,6 +1881,10 @@ pull.rebase::
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]
for details).
@@ -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].
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 6cea7f1..f2c85cc 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -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):
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 10fbc6a..21cffe2 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -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)`.
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index a7be80d..00e2aa2 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -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:
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2dbe486..e9917b8 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -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
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index bf1a02a..4a9cc74 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -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
diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index 1e71754..444b805 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -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.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index e394276..9e0ef0e 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -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.
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 2402ed6..e158a3b 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -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
-------------
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index ac2694d..34097ef 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -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>::
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index d7db2a3..d2fc12e 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -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::
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index e93fcb4..b1f7988 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -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
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 6ef8d59..beea10b 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -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
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index f7dfe48..e2992f1 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -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
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 2b126c0..d068a65 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -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[]
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 1d876c2..9d731b4 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -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
diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt
index c600b61..8b63ceb 100644
--- a/Documentation/git-whatchanged.txt
+++ b/Documentation/git-whatchanged.txt
@@ -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
--------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index dca11cc..c4f0ed5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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]]
------------------------
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index f538a87..058a352 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -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
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index 0827f69..f1f4ca9 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -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]
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index dba5062..13a64d3 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -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.
diff --git a/Documentation/line-range-format.txt b/Documentation/line-range-format.txt
index 3e7ce72..d7f2603 100644
--- a/Documentation/line-range-format.txt
+++ b/Documentation/line-range-format.txt
@@ -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.
diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt
index 4f63a04..540e455 100644
--- a/Documentation/technical/api-setup.txt
+++ b/Documentation/technical/api-setup.txt
@@ -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
index 0000000..a1173ee
--- /dev/null
+++ b/Documentation/technical/http-protocol.txt
@@ -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
diff --git a/Documentation/technical/pack-heuristics.txt b/Documentation/technical/pack-heuristics.txt
index 8b7ae1c..b7bd951 100644
--- a/Documentation/technical/pack-heuristics.txt
+++ b/Documentation/technical/pack-heuristics.txt
@@ -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.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 06026ea..b444c18 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.4
+DEF_VER=v1.8.4.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 3588ca1..e2abb7b 100644
--- 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
diff --git a/RelNotes b/RelNotes
index fce99fb..61c3b54 120000
--- 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
diff --git a/archive.c b/archive.c
index d254fa5..99fadc8 100644
--- 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++;
}
}
diff --git a/archive.h b/archive.h
index 895afcd..4a791e1 100644
--- 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;
diff --git a/builtin/add.c b/builtin/add.c
index 8266a9c..31ddabd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -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)
diff --git a/builtin/apply.c b/builtin/apply.c
index 50912c9..ef32e4f 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -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,
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index e3884e3..3324229 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -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()
};
diff --git a/builtin/blame.c b/builtin/blame.c
index 079dcd3..6da7233 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -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 @@ parse_done:
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;
diff --git a/builtin/branch.c b/builtin/branch.c
index 0836890..0903763 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -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)
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 4253460..41afaa5 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -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
@@ -127,6 +128,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
* elements above, so you can retrieve the response from there.
@@ -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;
}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 075d01d..e9af7b2 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -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);
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 4a8fc70..e2a1cef 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -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");
}
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index b1feda7..69e167b 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -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>"),
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 7025938..0f57397 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -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);
diff --git a/builtin/clean.c b/builtin/clean.c
index 3c85e15..615cd57 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -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);
diff --git a/builtin/clone.c b/builtin/clone.c
index 430307b..ca3eb68 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -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")),
diff --git a/builtin/commit.c b/builtin/commit.c
index 10acc53..80d886a 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -202,17 +202,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 +224,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 +296,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 +348,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 +369,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 +414,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();
@@ -1091,7 +1089,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 +1226,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 +1257,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 +1433,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 +1459,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()
};
diff --git a/builtin/config.c b/builtin/config.c
index 4010c43..20e89fe 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -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);
diff --git a/builtin/describe.c b/builtin/describe.c
index 7d73722..c94e5c3 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -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(),
};
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 46085f8..9200069 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -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;
}
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 1c737f7..ce15b23 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -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;
}
diff --git a/builtin/diff.c b/builtin/diff.c
index 9fc273d..2fb8c5d 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -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;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 8e19058..b1b9b5e 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -674,11 +674,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()
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index aba4465..c8e8582 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -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))
diff --git a/builtin/fetch.c b/builtin/fetch.c
index d784b2e..9e654ef 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -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);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7f059c3..1d4083c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -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)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 9909b6d..39fa5e8 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -611,15 +611,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(),
diff --git a/builtin/gc.c b/builtin/gc.c
index 6be6c8d..891a2c2 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -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]);
diff --git a/builtin/grep.c b/builtin/grep.c
index d3b3b1d..03bc442 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -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;
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 8d184f1..d7fcf4c 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -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()
};
diff --git a/builtin/log.c b/builtin/log.c
index 2625f98..77d0f5f 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -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()
};
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 5cf3e31..e1cf6d8 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -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");
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index fb76e38..65ec931 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -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;
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 0c4cd2f..e88eb93 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -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()
};
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index c0570f2..844f84f 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -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),
diff --git a/builtin/merge.c b/builtin/merge.c
index 34a6166..a8cf4a2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -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()
};
diff --git a/builtin/mv.c b/builtin/mv.c
index 034fec9..aec79d1 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -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))
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 0aaa19e..20fcf8c 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -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);
}
diff --git a/builtin/notes.c b/builtin/notes.c
index e4100c4..d459e23 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -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()
};
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index f069462..4eb0521 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -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();
}
diff --git a/builtin/push.c b/builtin/push.c
index 04f0eaf..7b1b66c 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -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"),
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e3eb5fc..b7e71a0 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -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);
}
diff --git a/builtin/remote.c b/builtin/remote.c
index 5e54d36..eaac3e2 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -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()
};
diff --git a/builtin/replace.c b/builtin/replace.c
index 59d3115..11b0a55 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -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()
};
diff --git a/builtin/rerere.c b/builtin/rerere.c
index dc1708e..4e51add 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -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);
diff --git a/builtin/reset.c b/builtin/reset.c
index afa6e02..5e4c551 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -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;
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index de894c7..c76b89d 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -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");
diff --git a/builtin/revert.c b/builtin/revert.c
index 1d2648b..8e87acd 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -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;
diff --git a/builtin/rm.c b/builtin/rm.c
index 0df0b4d..9b59ab3 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -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) {
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 152c4ea..4482f16 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -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);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 1434f8f..ae73d17 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -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(),
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 9788eb1..001f29c 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -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,
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 87806ad..9f3f5e3 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -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"),
diff --git a/builtin/tag.c b/builtin/tag.c
index af3af3f..b577af5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -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) {
diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c
index 3f1e701..ba3ffe6 100644
--- a/builtin/tar-tree.c
+++ b/builtin/tar-tree.c
@@ -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);
}
diff --git a/builtin/update-index.c b/builtin/update-index.c
index c317981..e3a10d7 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -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
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 51d2684..7484d36 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -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(),
};
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 6b0b6d4..118c625 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -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 85b544f..a47b9c0 100644
--- 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);
diff --git a/combine-diff.c b/combine-diff.c
index 88525b3..3b92c448 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -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) {
diff --git a/commit.c b/commit.c
index a575564..de16a3c 100644
--- 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) {
diff --git a/commit.h b/commit.h
index d912a9d..90a5a3c 100644
--- 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
index 0000000..c8b9b0e
--- /dev/null
+++ b/compat/apple-common-crypto.h
@@ -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
index b8f98ff..0000000
--- a/compat/clipped-write.c
+++ /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);
-}
diff --git a/compat/mingw.c b/compat/mingw.c
index bb92c43..22ee9ef 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -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;
diff --git a/config.c b/config.c
index 9f9bf0c..6588cf5 100644
--- a/config.c
+++ b/config.c
@@ -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;
}
diff --git a/config.mak.uname b/config.mak.uname
index b27f51d..7d61531 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -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
diff --git a/connect.c b/connect.c
index a0783d4..a80ebd3 100644
--- 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
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
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 5da920e..e1b7313 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -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
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index a81ef5a..d6c61b2 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -84,6 +84,10 @@
# 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"
diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
index d80f7d1..fb6429b 100755
--- a/contrib/contacts/git-contacts
+++ b/contrib/contacts/git-contacts
@@ -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
index 0000000..c2ea71c
--- /dev/null
+++ b/contrib/examples/git-log.sh
@@ -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
index 0000000..1fb9feb
--- /dev/null
+++ b/contrib/examples/git-whatchanged.sh
@@ -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}
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 1531150..8ee410f 100755
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -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
}
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
index 76fcd4d..f206f96 100644
--- a/contrib/mw-to-git/Makefile
+++ b/contrib/mw-to-git/Makefile
@@ -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
diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl
index f8d7d2c..c9a4805 100755
--- a/contrib/mw-to-git/git-remote-mediawiki.perl
+++ b/contrib/mw-to-git/git-remote-mediawiki.perl
@@ -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}));
}
}
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index c3a3cac..1e0044b 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -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
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 0194c67..c276039 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -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]
diff --git a/diff-delta.c b/diff-delta.c
index 93385e1..3797ce6 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -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;
diff --git a/diff-lib.c b/diff-lib.c
index b6f4b21..346cac6 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -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))
diff --git a/diff-no-index.c b/diff-no-index.c
index e66fdf3..e301aaf 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -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 266112c..a04a34d 100644
--- 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 @@ free_queue:
}
}
-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 78b4091..44092c2 100644
--- 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 910bfcd..b439ff0 100644
--- 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;
}
@@ -183,113 +204,6 @@ int within_depth(const char *name, int namelen,
*
* 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
- *
- * (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_pathspec_item(const struct pathspec_item *item, int prefix,
const char *name, int namelen)
{
@@ -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 3d6b80c..9b7e4e7 100644
--- 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
diff --git a/editor.c b/editor.c
index 27bdecd..0abbd8d 100644
--- 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;
diff --git a/fast-import.c b/fast-import.c
index 23f625f..21db3fc 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -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 @@ del_entry:
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.
diff --git a/fetch-pack.c b/fetch-pack.c
index f5d99c1..094267f 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -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"
diff --git a/fetch-pack.h b/fetch-pack.h
index 40f08ba..461cbf3 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -2,6 +2,7 @@
#define FETCH_PACK_H
#include "string-list.h"
+#include "run-command.h"
struct fetch_pack_args {
const char *uploadpack;
diff --git a/git-compat-util.h b/git-compat-util.h
index 115cb1d..9549de6 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -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);
diff --git a/git-p4.py b/git-p4.py
index 31e71ff..a53a6dc 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -2180,9 +2180,13 @@ class P4Sync(Command, P4UserMap):
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
diff --git a/git-pull.sh b/git-pull.sh
index f0df41c..e11d9a0 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -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}"
;;
*)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 83d6d46..10bf318 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -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"
diff --git a/git-remote-testgit.sh b/git-remote-testgit.sh
index 2109070..6d2f282 100755
--- a/git-remote-testgit.sh
+++ b/git-remote-testgit.sh
@@ -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)
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 7a964ad..e15be51 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -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 2025f77..b3893e7 100644
--- 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)--;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index f429f75..b5d156f 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -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'}) {
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index cb86d2d..3b4d833 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -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;
diff --git a/http.c b/http.c
index 2d086ae..f3e1439 100644
--- 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);
diff --git a/imap-send.c b/imap-send.c
index d6b65e2..6f5cc4f 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -28,20 +28,6 @@
#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>";
diff --git a/line-log.c b/line-log.c
index c2d01dc..8b6e497 100644
--- a/line-log.c
+++ b/line-log.c
@@ -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);
}
}
diff --git a/line-log.h b/line-log.h
index 8bea45f..a9212d8 100644
--- a/line-log.h
+++ b/line-log.h
@@ -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.
*
diff --git a/line-range.c b/line-range.c
index 3942475..de4e32f 100644
--- a/line-range.c
+++ b/line-range.c
@@ -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);
diff --git a/line-range.h b/line-range.h
index ae3d012..83ba3c2 100644
--- a/line-range.h
+++ b/line-range.h
@@ -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);
diff --git a/log-tree.c b/log-tree.c
index a49d8e8..8534d91 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -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);
diff --git a/mailmap.c b/mailmap.c
index 44614fc..a7969c4 100644
--- 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;
diff --git a/merge-recursive.c b/merge-recursive.c
index f95933b..40eb840 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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;
diff --git a/notes-merge.c b/notes-merge.c
index ab18857..94a1a8a 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -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)
diff --git a/notes-utils.h b/notes-utils.h
index b4cb1bf..564e30c 100644
--- a/notes-utils.h
+++ b/notes-utils.h
@@ -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.
diff --git a/parse-options.c b/parse-options.c
index c2cbca2..62e9b1c 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -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 @@ is_abbreviated:
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 @@ is_abbreviated:
(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;
}
diff --git a/parse-options.h b/parse-options.h
index 9b94596..8736006 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -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 3d244d3..9fd28bcd 100644
--- 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
diff --git a/pathspec.c b/pathspec.c
index 6ea0867..ad1a9f5 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -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;
}
diff --git a/pathspec.h b/pathspec.h
index db0184a..944baeb 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -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);
diff --git a/perl/Git.pm b/perl/Git.pm
index 7a252ef..204fdc6 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -61,7 +61,7 @@ require Exporter;
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 )
diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm
index bd17418..10edb27 100644
--- a/perl/Git/SVN/Fetcher.pm
+++ b/perl/Git/SVN/Fetcher.pm
@@ -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);
diff --git a/preload-index.c b/preload-index.c
index 49cb08d..8c44ceb 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -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 5c88081..45e3db1 100644
--- 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 ed110a5..71dcc3a 100644
--- 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
diff --git a/read-cache.c b/read-cache.c
index c3d5e35..885943a 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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 */
};
diff --git a/refs.c b/refs.c
index 7922261..d78860c 100644
--- 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
diff --git a/remote-curl.c b/remote-curl.c
index 5b3ce9e..b5ebe01 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -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 {
diff --git a/remote.c b/remote.c
index efcba93..2433467 100644
--- 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);
+}
diff --git a/remote.h b/remote.h
index cf56724..131130a 100644
--- 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
diff --git a/rerere.c b/rerere.c
index 6fde8f9..1f2d21a 100644
--- 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);
}
diff --git a/rerere.h b/rerere.h
index 156d2aa..4aa06c9 100644
--- 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 *);
diff --git a/resolve-undo.c b/resolve-undo.c
index 77101f5..c09b006 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -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);
}
diff --git a/resolve-undo.h b/resolve-undo.h
index 7a30206..4630645 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -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
diff --git a/revision.c b/revision.c
index 84ccc05..6230a80 100644
--- a/revision.c
+++ b/revision.c
@@ -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;
@@ -1372,7 +1373,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 +2121,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 +2155,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 +2765,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 +2822,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 +2849,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 +3049,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 +3082,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);
+}
diff --git a/revision.h b/revision.h
index 95859ba..e7f1d21 100644
--- a/revision.h
+++ b/revision.h
@@ -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
diff --git a/send-pack.c b/send-pack.c
index 7d172ef..b228d65 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -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 5262319..f08dd64 100644
--- 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.
diff --git a/sha1_file.c b/sha1_file.c
index 8e27db1..8c2d1ed 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -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)
diff --git a/submodule.c b/submodule.c
index 3f0a3f9..1905d75 100644
--- a/submodule.c
+++ b/submodule.c
@@ -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);
+}
diff --git a/submodule.h b/submodule.h
index c7ffc7c..7beec48 100644
--- a/submodule.h
+++ b/submodule.h
@@ -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
diff --git a/t/.gitattributes b/t/.gitattributes
index 1b97c54..2d44088 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1 +1,2 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
+t0110/url-* binary
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index d4e7f47..99caa42 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -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
+'
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 895b925..dab405d 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -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() {
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index dd17e3a..397c480 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -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}>
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index c29342d..96f40fe 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -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'\''"
'
############################################################################
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index e50f0f7..b92e6cb 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -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
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 986b2a8..5ed69a6 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -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
index 0000000..8d6096d
--- /dev/null
+++ b/t/t0110-urlmatch-normalization.sh
@@ -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
index 0000000..ad4a50e
--- /dev/null
+++ b/t/t0110/README
@@ -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
index 0000000..519019c
--- /dev/null
+++ b/t/t0110/url-1
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-10 b/t/t0110/url-10
new file mode 100644
index 0000000..b9965de
--- /dev/null
+++ b/t/t0110/url-10
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-11 b/t/t0110/url-11
new file mode 100644
index 0000000..f0a50f1
--- /dev/null
+++ b/t/t0110/url-11
@@ -0,0 +1 @@
+x://q/€߿ࠀ�𐀀𯿽
diff --git a/t/t0110/url-2 b/t/t0110/url-2
new file mode 100644
index 0000000..43334b0
--- /dev/null
+++ b/t/t0110/url-2
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-3 b/t/t0110/url-3
new file mode 100644
index 0000000..7378c7b
--- /dev/null
+++ b/t/t0110/url-3
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-4 b/t/t0110/url-4
new file mode 100644
index 0000000..220b198
--- /dev/null
+++ b/t/t0110/url-4
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-5 b/t/t0110/url-5
new file mode 100644
index 0000000..1ccd927
--- /dev/null
+++ b/t/t0110/url-5
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-6 b/t/t0110/url-6
new file mode 100644
index 0000000..e8283aa
--- /dev/null
+++ b/t/t0110/url-6
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-7 b/t/t0110/url-7
new file mode 100644
index 0000000..fa7c10b
--- /dev/null
+++ b/t/t0110/url-7
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-8 b/t/t0110/url-8
new file mode 100644
index 0000000..79a0ba8
--- /dev/null
+++ b/t/t0110/url-8
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t0110/url-9 b/t/t0110/url-9
new file mode 100644
index 0000000..8b44bec
--- /dev/null
+++ b/t/t0110/url-9
@@ -0,0 +1 @@
+x://q/
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 4e911fb..a420742 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -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"
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index c4a7d84..9673593 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -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 &&
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 9a105fe..6f47c0d 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -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
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index f611d79..6d3b828 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -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
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 49ccb38..50e22b1 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -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
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index 2e0c364..8c251c5 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -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
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 6f489e2..46aaf2f 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -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 * &&
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index 373aad6..fb889ac 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -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 * &&
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
index a5b6a5f..1e5b394 100755
--- a/t/t3509-cherry-pick-merge-df.sh
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -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 &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 5c87b55..639cb70 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -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' '
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 97172b4..cd04543 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -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' '
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index baa4685..ce3eace 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -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
'
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 7665d67..7369d3c 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -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
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index fde6891..1f0f8e6 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -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 &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4691d51..99c32d7 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -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
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index ed4d9c8..227d293 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -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
index 0000000..ba20d83
--- /dev/null
+++ b/t/t5533-push-cas.sh
@@ -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
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
index beb00be..470ac54 100755
--- a/t/t5541-http-push.sh
+++ b/t/t5541-http-push.sh
@@ -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
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
index 55a866a..8196af1 100755
--- a/t/t5551-http-fetch.sh
+++ b/t/t5551-http-fetch.sh
@@ -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' '
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 8c4c539..613f69a 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -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
index 0000000..878faf2
--- /dev/null
+++ b/t/t5802-connect-helper.sh
@@ -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
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index 57ce239..fde5e71 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -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
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index c680f78..a89dfbe 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -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 &&
diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh
index 39ef619..ea00d71 100755
--- a/t/t6130-pathspec-noglob.sh
+++ b/t/t6130-pathspec-noglob.sh
@@ -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
index 0000000..8d4a7fc
--- /dev/null
+++ b/t/t6131-pathspec-icase.sh
@@ -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
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 101816e..d432f42 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -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/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 5ee97b0..4192fe0 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -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 &&
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index d526b1d..05d9db0 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -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 &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index ac6f3b6..31a770d 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -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
###
@@ -1228,6 +1254,29 @@ test_expect_success \
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 &&
OBJID
@@ -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
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 272a071..2d4beb5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -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
}
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 3c3e4e8..59f875e 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -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 &&
diff --git a/test-urlmatch-normalization.c b/test-urlmatch-normalization.c
new file mode 100644
index 0000000..090bf21
--- /dev/null
+++ b/test-urlmatch-normalization.c
@@ -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;
+}
diff --git a/transport-helper.c b/transport-helper.c
index 63cabc3..b32e2d6 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -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)
diff --git a/transport.c b/transport.c
index e15db98..7202b77 100644
--- a/transport.c
+++ b/transport.c
@@ -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);
diff --git a/transport.h b/transport.h
index ea70ea7..8f96bed 100644
--- a/transport.h
+++ b/transport.h
@@ -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"
diff --git a/tree-diff.c b/tree-diff.c
index ba01563..ccf9d7c 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -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);
-}
diff --git a/tree-walk.c b/tree-walk.c
index c626135..5ece8c3 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -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 @@ match_wildcards:
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 c0e568c..549e588 100644
--- 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;
diff --git a/tree.h b/tree.h
index 69bcb5e..9dc90ba 100644
--- a/tree.h
+++ b/tree.h
@@ -25,7 +25,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);
diff --git a/unpack-trees.c b/unpack-trees.c
index bf01717..1a61e6f 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -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);
diff --git a/upload-pack.c b/upload-pack.c
index 127e59a..b03492e 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -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
index 0000000..1db76c8
--- /dev/null
+++ b/urlmatch.c
@@ -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,