Compare commits

..

109 Commits

Author SHA1 Message Date
4cd1cf31ef Git 2.20.2
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:30:51 +01:00
c154745074 submodule: defend against submodule.update = !command in .gitmodules
In v2.15.4, we started to reject `submodule.update` settings in
`.gitmodules`. Let's raise a BUG if it somehow still made it through
from anywhere but the Git config.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
2019-12-06 16:30:50 +01:00
4cfc47de25 t7415: adjust test for dubiously-nested submodule gitdirs for v2.20.x
In v2.20.x, Git clones submodules recursively by first creating the
submodules' gitdirs and _then_ "updating" the submodules. This can lead
to the situation where the clone path is taken because the directory
(while it exists already) is not a git directory, but then the clone
fails because that gitdir is unexpectedly already a directory.

This _also_ works around the vulnerability that was fixed in "Disallow
dubiously-nested submodule git directories", but it produces a different
error message than the one expected by the test case, therefore we
adjust the test case accordingly.

Note: as the two submodules "race each other", there are actually two
possible error messages, therefore we have to teach the test case to
expect _two_ possible (and good) outcomes in addition to the one it
expected before.

Note: this workaround is only necessary for the v2.20.x release train;
The behavior changed again in v2.21.x so that the original test case's
expectations are met again.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:30:50 +01:00
d851d94151 Sync with 2.19.3
* maint-2.19: (34 commits)
  Git 2.19.3
  Git 2.18.2
  Git 2.17.3
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  ...
2019-12-06 16:30:49 +01:00
caccc527ca Git 2.19.3
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:30:40 +01:00
7c9fbda6e2 Sync with 2.18.2
* maint-2.18: (33 commits)
  Git 2.18.2
  Git 2.17.3
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  ...
2019-12-06 16:30:38 +01:00
9877106b01 Git 2.18.2
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:29:17 +01:00
14af7ed5a9 Sync with 2.17.3
* maint-2.17: (32 commits)
  Git 2.17.3
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  mingw: disallow backslash characters in tree objects' file names
  ...
2019-12-06 16:29:15 +01:00
a5ab8d0317 Git 2.17.3
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:27:38 +01:00
bb92255ebe fsck: reject submodule.update = !command in .gitmodules
This allows hosting providers to detect whether they are being used
to attack users using malicious 'update = !command' settings in
.gitmodules.

Since ac1fbbda20 (submodule: do not copy unknown update mode from
.gitmodules, 2013-12-02), in normal cases such settings have been
treated as 'update = none', so forbidding them should not produce any
collateral damage to legitimate uses.  A quick search does not reveal
any repositories making use of this construct, either.

Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:27:38 +01:00
bdfef0492c Sync with 2.16.6
* maint-2.16: (31 commits)
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  mingw: disallow backslash characters in tree objects' file names
  path: safeguard `.git` against NTFS Alternate Streams Accesses
  ...
2019-12-06 16:27:36 +01:00
eb288bc455 Git 2.16.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:27:20 +01:00
68440496c7 test-drop-caches: use has_dos_drive_prefix()
This is a companion patch to 'mingw: handle `subst`-ed "DOS drives"':
use the DOS drive prefix handling that is already provided by
`compat/mingw.c` (and which just learned to handle non-alphabetical
"drive letters").

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:27:20 +01:00
9ac92fed5b Sync with 2.15.4
* maint-2.15: (29 commits)
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  mingw: disallow backslash characters in tree objects' file names
  path: safeguard `.git` against NTFS Alternate Streams Accesses
  clone --recurse-submodules: prevent name squatting on Windows
  is_ntfs_dotgit(): only verify the leading segment
  ...
2019-12-06 16:27:18 +01:00
7cdafcaacf Git 2.15.4
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:26:58 +01:00
e904deb89d submodule: reject submodule.update = !command in .gitmodules
Since ac1fbbda20 (submodule: do not copy unknown update mode from
.gitmodules, 2013-12-02), Git has been careful to avoid copying

	[submodule "foo"]
		update = !run an arbitrary scary command

from .gitmodules to a repository's local config, copying in the
setting 'update = none' instead.  The gitmodules(5) manpage documents
the intention:

	The !command form is intentionally ignored here for security
	reasons

Unfortunately, starting with v2.20.0-rc0 (which integrated ee69b2a9
(submodule--helper: introduce new update-module-mode helper,
2018-08-13, first released in v2.20.0-rc0)), there are scenarios where
we *don't* ignore it: if the config store contains no
submodule.foo.update setting, the submodule-config API falls back to
reading .gitmodules and the repository-supplied !command gets run
after all.

This was part of a general change over time in submodule support to
read more directly from .gitmodules, since unlike .git/config it
allows a project to change values between branches and over time
(while still allowing .git/config to override things).  But it was
never intended to apply to this kind of dangerous configuration.

The behavior change was not advertised in ee69b2a9's commit message
and was missed in review.

Let's take the opportunity to make the protection more robust, even in
Git versions that are technically not affected: instead of quietly
converting 'update = !command' to 'update = none', noisily treat it as
an error.  Allowing the setting but treating it as meaning something
else was just confusing; users are better served by seeing the error
sooner.  Forbidding the construct makes the semantics simpler and
means we can check for it in fsck (in a separate patch).

As a result, the submodule-config API cannot read this value from
.gitmodules under any circumstance, and we can declare with confidence

	For security reasons, the '!command' form is not accepted
	here.

Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
2019-12-06 16:26:58 +01:00
d3ac8c3f27 Sync with 2.14.6
* maint-2.14: (28 commits)
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  mingw: disallow backslash characters in tree objects' file names
  path: safeguard `.git` against NTFS Alternate Streams Accesses
  clone --recurse-submodules: prevent name squatting on Windows
  is_ntfs_dotgit(): only verify the leading segment
  test-path-utils: offer to run a protectNTFS/protectHFS benchmark
  ...
2019-12-06 16:26:55 +01:00
66d2a6159f Git 2.14.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-06 16:26:15 +01:00
2ddcccf97a Merge branch 'win32-accommodate-funny-drive-names'
While the only permitted drive letters for physical drives on Windows
are letters of the US-English alphabet, this restriction does not apply
to virtual drives assigned via `subst <letter>: <path>`.

To prevent targeted attacks against systems where "funny" drive letters
such as `1` or `!` are assigned, let's handle them as regular drive
letters on Windows.

This fixes CVE-2019-1351.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:09 +01:00
65d30a19de Merge branch 'win32-filenames-cannot-have-trailing-spaces-or-periods'
On Windows, filenames cannot have trailing spaces or periods, when
opening such paths, they are stripped automatically. Read: you can open
the file `README` via the file name `README . . .`. This ambiguity can
be used in combination with other security bugs to cause e.g. remote
code execution during recursive clones. This patch series fixes that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:09 +01:00
5532ebdeb7 Merge branch 'fix-mingw-quoting-bug'
This patch fixes a vulnerability in the Windows-specific code where a
submodule names ending in a backslash were quoted incorrectly, and that
bug could be abused to insert command-line parameters e.g. to `ssh` in a
recursive clone.

Note: this bug is Windows-only, as we have to construct a command line
for the process-to-spawn, unlike Linux/macOS, where `execv()` accepts an
already-split command line.

While at it, other quoting issues are fixed as well.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:08 +01:00
76a681ce9c Merge branch 'dubiously-nested-submodules'
Recursive clones are currently affected by a vulnerability that is
caused by too-lax validation of submodule names.

This topic branch fixes that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:08 +01:00
dd53ea7220 Merge branch 'turn-on-protectntfs-by-default'
This patch series makes it safe to use Git on Windows drives, even if
running on a mounted network share or within the Windows Subsystem for
Linux (WSL).

This topic branch addresses CVE-2019-1353.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:08 +01:00
f82a97eb91 mingw: handle subst-ed "DOS drives"
Over a decade ago, in 25fe217b86 (Windows: Treat Windows style path
names., 2008-03-05), Git was taught to handle absolute Windows paths,
i.e. paths that start with a drive letter and a colon.

Unbeknownst to us, while drive letters of physical drives are limited to
letters of the English alphabet, there is a way to assign virtual drive
letters to arbitrary directories, via the `subst` command, which is
_not_ limited to English letters.

It is therefore possible to have absolute Windows paths of the form
`1:\what\the\hex.txt`. Even "better": pretty much arbitrary Unicode
letters can also be used, e.g. `ä:\tschibät.sch`.

While it can be sensibly argued that users who set up such funny drive
letters really seek adverse consequences, the Windows Operating System
is known to be a platform where many users are at the mercy of
administrators who have their very own idea of what constitutes a
reasonable setup.

Therefore, let's just make sure that such funny paths are still
considered absolute paths by Git, on Windows.

In addition to Unicode characters, pretty much any character is a valid
drive letter, as far as `subst` is concerned, even `:` and `"` or even a
space character. While it is probably the opposite of smart to use them,
let's safeguard `is_dos_drive_prefix()` against all of them.

Note: `[::1]:repo` is a valid URL, but not a valid path on Windows.
As `[` is now considered a valid drive letter, we need to be very
careful to avoid misinterpreting such a string as valid local path in
`url_is_local_not_ssh()`. To do that, we use the just-introduced
function `is_valid_path()` (which will label the string as invalid file
name because of the colon characters).

This fixes CVE-2019-1351.

Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:07 +01:00
7f3551dd68 Merge branch 'disallow-dotgit-via-ntfs-alternate-data-streams'
This patch series plugs an attack vector we had overlooked in our
December 2014 work on `core.protectNTFS`.

Essentially, the path `.git::$INDEX_ALLOCATION/config` is interpreted as
`.git/config` when NTFS Alternate Data Streams are available (which they
are on Windows, and at least on network shares that are SMB-mounted on
macOS).

Needless to say: we don't want that.

In fact, we want to stay on the very safe side and not even special-case
the `$INDEX_ALLOCATION` stream type: let's just prevent Git from
touching _any_ explicitly specified Alternate Data Stream of `.git`.

In essence, we'll prevent Git from tracking, or writing to, any path
with a segment of the form `.git:<anything>`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:07 +01:00
d2c84dad1c mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.

This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.

Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).

Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.

As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.

For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.

Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).

While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.

The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.

Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
379e51d1ae quote-stress-test: offer to test quoting arguments for MSYS2 sh
It is unfortunate that we need to quote arguments differently on
Windows, depending whether we build a command-line for MSYS2's `sh` or
for other Windows executables.

We already have a test helper to verify the latter, with this patch we
can also verify the former.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
817ddd64c2 mingw: refuse to access paths with illegal characters
Certain characters are not admissible in file names on Windows, even if
Cygwin/MSYS2 (and therefore, Git for Windows' Bash) pretend that they
are, e.g. `:`, `<`, `>`, etc

Let's disallow those characters explicitly in Windows builds of Git.

Note: just like trailing spaces or periods, it _is_ possible on Windows
to create commits adding files with such illegal characters, as long as
the operation leaves the worktree untouched. To allow for that, we
continue to guard `is_valid_win32_path()` behind the config setting
`core.protectNTFS`, so that users _can_ continue to do that, as long as
they turn the protections off via that config setting.

Among other problems, this prevents Git from trying to write to an "NTFS
Alternate Data Stream" (which refers to metadata stored alongside a
file, under a special name: "<filename>:<stream-name>"). This fix
therefore also prevents an attack vector that was exploited in
demonstrations of a number of recently-fixed security bugs.

Further reading on illegal characters in Win32 filenames:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
cc756edda6 unpack-trees: let merged_entry() pass through do_add_entry()'s errors
A `git clone` will end with exit code 0 when `merged_entry()` returns a
positive value during a call of `unpack_trees()` to `traverse_trees()`.
The reason is that `unpack_trees()` will interpret a positive value not
to be an error.

The problem is, however, that `add_index_entry()` (which is called by
`merged_entry()` can report an error, and we really should fail the
entire clone in such a case.

Let's fix this problem, in preparation for a Windows-specific patch
disallowing `mkdir()` with directory names that contain a trailing space
(which is illegal on NTFS): we want `git clone` to abort when a path
cannot be checked out due to that condition.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
7530a6287e quote-stress-test: allow skipping some trials
When the, say, 93rd trial run fails, it is a good idea to have a way to
skip the first 92 trials and dig directly into the 93rd in a debugger.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
35edce2056 t6130/t9350: prepare for stringent Win32 path validation
On Windows, file names cannot contain asterisks nor newline characters.
In an upcoming commit, we will make this limitation explicit,
disallowing even the creation of commits that introduce such file names.

However, in the test scripts touched by this patch, we _know_ that those
paths won't be checked out, so we _want_ to allow such file names.

Happily, the stringent path validation will be guarded via the
`core.protectNTFS` flag, so all we need to do is to force that flag off
temporarily.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:37:06 +01:00
55953c77c0 quote-stress-test: accept arguments to test via the command-line
When the stress test reported a problem with quoting certain arguments,
it is helpful to have a facility to play with those arguments in order
to find out whether variations of those arguments are affected, too.

Let's allow `test-run-command quote-stress-test -- <args>` to be used
for that purpose.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:53 +01:00
ad15592529 tests: add a helper to stress test argument quoting
On Windows, we have to do all the command-line argument quoting
ourselves. Worse: we have to have two versions of said quoting, one for
MSYS2 programs (which have their own dequoting rules) and the rest.

We care mostly about the rest, and to make sure that that works, let's
have a stress test that comes up with all kinds of awkward arguments,
verifying that a spawned sub-process receives those unharmed.

Signed-off-by: Garima Singh <garima.singh@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:52 +01:00
a8dee3ca61 Disallow dubiously-nested submodule git directories
Currently it is technically possible to let a submodule's git
directory point right into the git dir of a sibling submodule.

Example: the git directories of two submodules with the names `hippo`
and `hippo/hooks` would be `.git/modules/hippo/` and
`.git/modules/hippo/hooks/`, respectively, but the latter is already
intended to house the former's hooks.

In most cases, this is just confusing, but there is also a (quite
contrived) attack vector where Git can be fooled into mistaking remote
content for file contents it wrote itself during a recursive clone.

Let's plug this bug.

To do so, we introduce the new function `validate_submodule_git_dir()`
which simply verifies that no git dir exists for any leading directories
of the submodule name (if there are any).

Note: this patch specifically continues to allow sibling modules names
of the form `core/lib`, `core/doc`, etc, as long as `core` is not a
submodule name.

This fixes CVE-2019-1387.

Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:51 +01:00
9102f958ee protect_ntfs: turn on NTFS protection by default
Back in the DOS days, in the FAT file system, file names always
consisted of a base name of length 8 plus a file extension of length 3.
Shorter file names were simply padded with spaces to the full 8.3
format.

Later, the FAT file system was taught to support _also_ longer names,
with an 8.3 "short name" as primary file name. While at it, the same
facility allowed formerly illegal file names, such as `.git` (empty base
names were not allowed), which would have the "short name" `git~1`
associated with it.

For backwards-compatibility, NTFS supports alternative 8.3 short
filenames, too, even if starting with Windows Vista, they are only
generated on the system drive by default.

We addressed the problem that the `.git/` directory can _also_ be
accessed via `git~1/` (when short names are enabled) in 2b4c6efc82
(read-cache: optionally disallow NTFS .git variants, 2014-12-16), i.e.
since Git v1.9.5, by introducing the config setting `core.protectNTFS`
and enabling it by default on Windows.

In the meantime, Windows 10 introduced the "Windows Subsystem for Linux"
(short: WSL), i.e. a way to run Linux applications/distributions in a
thinly-isolated subsystem on Windows (giving rise to many a "2016 is the
Year of Linux on the Desktop" jokes). WSL is getting increasingly
popular, also due to the painless way Linux application can operate
directly ("natively") on files on Windows' file system: the Windows
drives are mounted automatically (e.g. `C:` as `/mnt/c/`).

Taken together, this means that we now have to enable the safe-guards of
Git v1.9.5 also in WSL: it is possible to access a `.git` directory
inside `/mnt/c/` via the 8.3 name `git~1` (unless short name generation
was disabled manually). Since regular Linux distributions run in WSL,
this means we have to enable `core.protectNTFS` at least on Linux, too.

To enable Services for Macintosh in Windows NT to store so-called
resource forks, NTFS introduced "Alternate Data Streams". Essentially,
these constitute additional metadata that are connected to (and copied
with) their associated files, and they are accessed via pseudo file
names of the form `filename:<stream-name>:<stream-type>`.

In a recent patch, we extended `core.protectNTFS` to also protect
against accesses via NTFS Alternate Data Streams, e.g. to prevent
contents of the `.git/` directory to be "tracked" via yet another
alternative file name.

While it is not possible (at least by default) to access files via NTFS
Alternate Data Streams from within WSL, the defaults on macOS when
mounting network shares via SMB _do_ allow accessing files and
directories in that way. Therefore, we need to enable `core.protectNTFS`
on macOS by default, too, and really, on any Operating System that can
mount network shares via SMB/CIFS.

A couple of approaches were considered for fixing this:

1. We could perform a dynamic NTFS check similar to the `core.symlinks`
   check in `init`/`clone`: instead of trying to create a symbolic link
   in the `.git/` directory, we could create a test file and try to
   access `.git/config` via 8.3 name and/or Alternate Data Stream.

2. We could simply "flip the switch" on `core.protectNTFS`, to make it
   "on by default".

The obvious downside of 1. is that it won't protect worktrees that were
clone with a vulnerable Git version already. We considered patching code
paths that check out files to check whether we're running on an NTFS
system dynamically and persist the result in the repository-local config
setting `core.protectNTFS`, but in the end decided that this solution
would be too fragile, and too involved.

The obvious downside of 2. is that everybody will have to "suffer" the
performance penalty incurred from calling `is_ntfs_dotgit()` on every
path, even in setups where.

After the recent work to accelerate `is_ntfs_dotgit()` in most cases,
it looks as if the time spent on validating ten million random
file names increases only negligibly (less than 20ms, well within the
standard deviation of ~50ms). Therefore the benefits outweigh the cost.

Another downside of this is that paths that might have been acceptable
previously now will be forbidden. Realistically, though, this is an
improvement because public Git hosters already would reject any `git
push` that contains such file names.

Note: There might be a similar problem mounting HFS+ on Linux. However,
this scenario has been considered unlikely and in light of the cost (in
the aforementioned benchmark, `core.protectHFS = true` increased the
time from ~440ms to ~610ms), it was decided _not_ to touch the default
of `core.protectHFS`.

This change addresses CVE-2019-1353.

Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
Helped-by: Garima Singh <garima.singh@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:51 +01:00
91bd46588e path: also guard .gitmodules against NTFS Alternate Data Streams
We just safe-guarded `.git` against NTFS Alternate Data Stream-related
attack vectors, and now it is time to do the same for `.gitmodules`.

Note: In the added regression test, we refrain from verifying all kinds
of variations between short names and NTFS Alternate Data Streams: as
the new code disallows _all_ Alternate Data Streams of `.gitmodules`, it
is enough to test one in order to know that all of them are guarded
against.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:51 +01:00
6d8684161e mingw: fix quoting of arguments
We need to be careful to follow proper quoting rules. For example, if an
argument contains spaces, we have to quote them. Double-quotes need to
be escaped. Backslashes need to be escaped, but only if they are
followed by a double-quote character.

We need to be _extra_ careful to consider the case where an argument
ends in a backslash _and_ needs to be quoted: in this case, we append a
double-quote character, i.e. the backslash now has to be escaped!

The current code, however, fails to recognize that, and therefore can
turn an argument that ends in a single backslash into a quoted argument
that now ends in an escaped double-quote character. This allows
subsequent command-line parameters to be split and part of them being
mistaken for command-line options, e.g. through a maliciously-crafted
submodule URL during a recursive clone.

Technically, we would not need to quote _all_ arguments which end in a
backslash _unless_ the argument needs to be quoted anyway. For example,
`test\` would not need to be quoted, while `test \` would need to be.

To keep the code simple, however, and therefore easier to reason about
and ensure its correctness, we now _always_ quote an argument that ends
in a backslash.

This addresses CVE-2019-1350.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:51 +01:00
3a85dc7d53 is_ntfs_dotgit(): speed it up
Previously, this function was written without focusing on speed,
intending to make reviewing the code as easy as possible, to avoid any
bugs in this critical code.

Turns out: we can do much better on both accounts. With this patch, we
make it as fast as this developer can make it go:

- We avoid the call to `is_dir_sep()` and make all the character
  comparisons explicit.

- We avoid the cost of calling `strncasecmp()` and unroll the test for
  `.git` and `git~1`, not even using `tolower()` because it is faster to
  compare against two constant values.

- We look for `.git` and `.git~1` first thing, and return early if not
  found.

- We also avoid calling a separate function for detecting chains of
  spaces and periods.

Each of these improvements has a noticeable impact on the speed of
`is_ntfs_dotgit()`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:51 +01:00
7c3745fc61 path: safeguard .git against NTFS Alternate Streams Accesses
Probably inspired by HFS' resource streams, NTFS supports "Alternate
Data Streams": by appending `:<stream-name>` to the file name,
information in addition to the file contents can be written and read,
information that is copied together with the file (unless copied to a
non-NTFS location).

These Alternate Data Streams are typically used for things like marking
an executable as having just been downloaded from the internet (and
hence not necessarily being trustworthy).

In addition to a stream name, a stream type can be appended, like so:
`:<stream-name>:<stream-type>`. Unless specified, the default stream
type is `$DATA` for files and `$INDEX_ALLOCATION` for directories. In
other words, `.git::$INDEX_ALLOCATION` is a valid way to reference the
`.git` directory!

In our work in Git v2.2.1 to protect Git on NTFS drives under
`core.protectNTFS`, we focused exclusively on NTFS short names, unaware
of the fact that NTFS Alternate Data Streams offer a similar attack
vector.

Let's fix this.

Seeing as it is better to be safe than sorry, we simply disallow paths
referring to *any* NTFS Alternate Data Stream of `.git`, not just
`::$INDEX_ALLOCATION`. This also simplifies the implementation.

This closes CVE-2019-1352.

Further reading about NTFS Alternate Data Streams:
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c54dec26-1551-4d3a-a0ea-4fa40f848eb3

Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:50 +01:00
288a74bcd2 is_ntfs_dotgit(): only verify the leading segment
The config setting `core.protectNTFS` is specifically designed to work
not only on Windows, but anywhere, to allow for repositories hosted on,
say, Linux servers to be protected against NTFS-specific attack vectors.

As a consequence, `is_ntfs_dotgit()` manually splits backslash-separated
paths (but does not do the same for paths separated by forward slashes),
under the assumption that the backslash might not be a valid directory
separator on the _current_ Operating System.

However, the two callers, `verify_path()` and `fsck_tree()`, are
supposed to feed only individual path segments to the `is_ntfs_dotgit()`
function.

This causes a lot of duplicate scanning (and very inefficient scanning,
too, as the inner loop of `is_ntfs_dotgit()` was optimized for
readability rather than for speed.

Let's simplify the design of `is_ntfs_dotgit()` by putting the burden of
splitting the paths by backslashes as directory separators on the
callers of said function.

Consequently, the `verify_path()` function, which already splits the
path by directory separators, now treats backslashes as directory
separators _explicitly_ when `core.protectNTFS` is turned on, even on
platforms where the backslash is _not_ a directory separator.

Note that we have to repeat some code in `verify_path()`: if the
backslash is not a directory separator on the current Operating System,
we want to allow file names like `\`, but we _do_ want to disallow paths
that are clearly intended to cause harm when the repository is cloned on
Windows.

The `fsck_tree()` function (the other caller of `is_ntfs_dotgit()`) now
needs to look for backslashes in tree entries' names specifically when
`core.protectNTFS` is turned on. While it would be tempting to
completely disallow backslashes in that case (much like `fsck` reports
names containing forward slashes as "full paths"), this would be
overzealous: when `core.protectNTFS` is turned on in a non-Windows
setup, backslashes are perfectly valid characters in file names while we
_still_ want to disallow tree entries that are clearly designed to
exploit NTFS-specific behavior.

This simplification will make subsequent changes easier to implement,
such as turning `core.protectNTFS` on by default (not only on Windows)
or protecting against attack vectors involving NTFS Alternate Data
Streams.

Incidentally, this change allows for catching malicious repositories
that contain tree entries of the form `dir\.gitmodules` already on the
server side rather than only on the client side (and previously only on
Windows): in contrast to `is_ntfs_dotgit()`, the
`is_ntfs_dotgitmodules()` function already expects the caller to split
the paths by directory separators.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:50 +01:00
a62f9d1ace test-path-utils: offer to run a protectNTFS/protectHFS benchmark
In preparation to flipping the default on `core.protectNTFS`, let's have
some way to measure the speed impact of this config setting reliably
(and for comparison, the `core.protectHFS` config setting).

For now, this is a manual performance benchmark:

	./t/helper/test-path-utils protect_ntfs_hfs [arguments...]

where the arguments are an optional number of file names to test with,
optionally followed by minimum and maximum length of the random file
names. The default values are one million, 3 and 20, respectively.

Just like `sqrti()` in `bisect.c`, we introduce a very simple function
to approximation the square root of a given value, in order to avoid
having to introduce the first user of `<math.h>` in Git's source code.

Note: this is _not_ implemented as a Unix shell script in t/perf/
because we really care about _very_ precise timings here, and Unix shell
scripts are simply unsuited for precise and consistent benchmarking.

Signed-off-by: Garima Singh <garima.singh@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-05 15:36:40 +01:00
4778452597 Merge branch 'prevent-name-squatting-on-windows'
This patch series fixes an issue where Git could formerly have been
tricked into creating a `.git` file with an unexpected (and therefore
unprotected) NTFS short name.

Incidentally, it also fixes an issue where a tree entry containing a
backslash could be tricked into following a symbolic link, i.e. Git
could be tricked into writing files outside the worktree.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-04 13:23:22 +01:00
a7b1ad3b05 Merge branch 'jk/fast-import-unsafe'
The `--export-marks` option of `git fast-import` is exposed also via the
in-stream command `feature export-marks=...` and it allows overwriting
arbitrary paths.

This topic branch prevents the in-stream version, to prevent arbitrary
file accesses by `git fast-import` streams coming from untrusted sources
(e.g. in remote helpers that are based on `git fast-import`).

This fixes CVE-2019-1348.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-04 13:23:22 +01:00
525e7fba78 path.c: document the purpose of is_ntfs_dotgit()
Previously, this function was completely undocumented. It is worth,
though, to explain what is going on, as it is not really obvious at all.

Suggested-by: Garima Singh <garima.singh@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-04 13:20:05 +01:00
e1d911dd4c mingw: disallow backslash characters in tree objects' file names
The backslash character is not a valid part of a file name on Windows.
Hence it is dangerous to allow writing files that were unpacked from
tree objects, when the stored file name contains a backslash character:
it will be misinterpreted as directory separator.

This not only causes ambiguity when a tree contains a blob `a\b` and a
tree `a` that contains a blob `b`, but it also can be used as part of an
attack vector to side-step the careful protections against writing into
the `.git/` directory during a clone of a maliciously-crafted
repository.

Let's prevent that, addressing CVE-2019-1354.

Note: we guard against backslash characters in tree objects' file names
_only_ on Windows (because on other platforms, even on those where NTFS
volumes can be mounted, the backslash character is _not_ a directory
separator), and _only_ when `core.protectNTFS = true` (because users
might need to generate tree objects for other platforms, of course
without touching the worktree, e.g. using `git update-index
--cacheinfo`).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-04 13:20:05 +01:00
0060fd1511 clone --recurse-submodules: prevent name squatting on Windows
In addition to preventing `.git` from being tracked by Git, on Windows
we also have to prevent `git~1` from being tracked, as the default NTFS
short name (also known as the "8.3 filename") for the file name `.git`
is `git~1`, otherwise it would be possible for malicious repositories to
write directly into the `.git/` directory, e.g. a `post-checkout` hook
that would then be executed _during_ a recursive clone.

When we implemented appropriate protections in 2b4c6efc82 (read-cache:
optionally disallow NTFS .git variants, 2014-12-16), we had analyzed
carefully that the `.git` directory or file would be guaranteed to be
the first directory entry to be written. Otherwise it would be possible
e.g. for a file named `..git` to be assigned the short name `git~1` and
subsequently, the short name generated for `.git` would be `git~2`. Or
`git~3`. Or even `~9999999` (for a detailed explanation of the lengths
we have to go to protect `.gitmodules`, see the commit message of
e7cb0b4455 (is_ntfs_dotgit: match other .git files, 2018-05-11)).

However, by exploiting two issues (that will be addressed in a related
patch series close by), it is currently possible to clone a submodule
into a non-empty directory:

- On Windows, file names cannot end in a space or a period (for
  historical reasons: the period separating the base name from the file
  extension was not actually written to disk, and the base name/file
  extension was space-padded to the full 8/3 characters, respectively).
  Helpfully, when creating a directory under the name, say, `sub.`, that
  trailing period is trimmed automatically and the actual name on disk
  is `sub`.

  This means that while Git thinks that the submodule names `sub` and
  `sub.` are different, they both access `.git/modules/sub/`.

- While the backslash character is a valid file name character on Linux,
  it is not so on Windows. As Git tries to be cross-platform, it
  therefore allows backslash characters in the file names stored in tree
  objects.

  Which means that it is totally possible that a submodule `c` sits next
  to a file `c\..git`, and on Windows, during recursive clone a file
  called `..git` will be written into `c/`, of course _before_ the
  submodule is cloned.

Note that the actual exploit is not quite as simple as having a
submodule `c` next to a file `c\..git`, as we have to make sure that the
directory `.git/modules/b` already exists when the submodule is checked
out, otherwise a different code path is taken in `module_clone()` that
does _not_ allow a non-empty submodule directory to exist already.

Even if we will address both issues nearby (the next commit will
disallow backslash characters in tree entries' file names on Windows,
and another patch will disallow creating directories/files with trailing
spaces or periods), it is a wise idea to defend in depth against this
sort of attack vector: when submodules are cloned recursively, we now
_require_ the directory to be empty, addressing CVE-2019-1349.

Note: the code path we patch is shared with the code path of `git
submodule update --init`, which must not expect, in general, that the
directory is empty. Hence we have to introduce the new option
`--force-init` and hand it all the way down from `git submodule` to the
actual `git submodule--helper` process that performs the initial clone.

Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-12-04 13:20:05 +01:00
a52ed76142 fast-import: disallow "feature import-marks" by default
As with export-marks in the previous commit, import-marks can access the
filesystem. This is significantly less dangerous than export-marks
because it only involves reading from arbitrary paths, rather than
writing them. However, it could still be surprising and have security
implications (e.g., exfiltrating data from a service that accepts
fast-import streams).

Let's lump it (and its "if-exists" counterpart) in with export-marks,
and enable the in-stream version only if --allow-unsafe-features is set.

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:04 +01:00
68061e3470 fast-import: disallow "feature export-marks" by default
The fast-import stream command "feature export-marks=<path>" lets the
stream write marks to an arbitrary path. This may be surprising if you
are running fast-import against an untrusted input (which otherwise
cannot do anything except update Git objects and refs).

Let's disallow the use of this feature by default, and provide a
command-line option to re-enable it (you can always just use the
command-line --export-marks as well, but the in-stream version provides
an easy way for exporters to control the process).

This is a backwards-incompatible change, since the default is flipping
to the new, safer behavior. However, since the main users of the
in-stream versions would be import/export-based remote helpers, and
since we trust remote helpers already (which are already running
arbitrary code), we'll pass the new option by default when reading a
remote helper's stream. This should minimize the impact.

Note that the implementation isn't totally simple, as we have to work
around the fact that fast-import doesn't parse its command-line options
until after it has read any "feature" lines from the stream. This is how
it lets command-line options override in-stream. But in our case, it's
important to parse the new --allow-unsafe-features first.

There are three options for resolving this:

  1. Do a separate "early" pass over the options. This is easy for us to
     do because there are no command-line options that allow the
     "unstuck" form (so there's no chance of us mistaking an argument
     for an option), though it does introduce a risk of incorrect
     parsing later (e.g,. if we convert to parse-options).

  2. Move the option parsing phase back to the start of the program, but
     teach the stream-reading code never to override an existing value.
     This is tricky, because stream "feature" lines override each other
     (meaning we'd have to start tracking the source for every option).

  3. Accept that we might parse a "feature export-marks" line that is
     forbidden, as long we don't _act_ on it until after we've parsed
     the command line options.

     This would, in fact, work with the current code, but only because
     the previous patch fixed the export-marks parser to avoid touching
     the filesystem.

     So while it works, it does carry risk of somebody getting it wrong
     in the future in a rather subtle and unsafe way.

I've gone with option (1) here as simple, safe, and unlikely to cause
regressions.

This fixes CVE-2019-1348.

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:04 +01:00
019683025f fast-import: delay creating leading directories for export-marks
When we parse the --export-marks option, we don't immediately open the
file, but we do create any leading directories. This can be especially
confusing when a command-line option overrides an in-stream one, in
which case we'd create the leading directory for the in-stream file,
even though we never actually write the file.

Let's instead create the directories just before opening the file, which
means we'll create only useful directories. Note that this could change
the handling of relative paths if we chdir() in between, but we don't
actually do so; the only permanent chdir is from setup_git_directory()
which runs before either code path (potentially we should take the
pre-setup dir into account to avoid surprising the user, but that's an
orthogonal change).

The test just adapts the existing "override" test to use paths with
leading directories. This checks both that the correct directory is
created (which worked before but was not tested), and that the
overridden one is not (our new fix here).

While we're here, let's also check the error result of
safe_create_leading_directories(). We'd presumably notice any failure
immediately after when we try to open the file itself, but we can give a
more specific error message in this case.

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:04 +01:00
e075dba372 fast-import: stop creating leading directories for import-marks
When asked to import marks from "subdir/file.marks", we create the
leading directory "subdir" if it doesn't exist. This makes no sense for
importing marks, where we only ever open the path for reading.

Most of the time this would be a noop, since if the marks file exists,
then the leading directories exist, too. But if it doesn't (e.g.,
because --import-marks-if-exists was used), then we'd create the useless
directory.

This dates back to 580d5f83e7 (fast-import: always create marks_file
directories, 2010-03-29). Even then it was useless, so it seems to have
been added in error alongside the --export-marks case (which _is_
helpful).

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:04 +01:00
11e934d56e fast-import: tighten parsing of boolean command line options
We parse options like "--max-pack-size=" using skip_prefix(), which
makes sense to get at the bytes after the "=". However, we also parse
"--quiet" and "--stats" with skip_prefix(), which allows things like
"--quiet-nonsense" to behave like "--quiet".

This was a mistaken conversion in 0f6927c229 (fast-import: put option
parsing code in separate functions, 2009-12-04). Let's tighten this to
an exact match, which was the original intent.

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:04 +01:00
816f806786 t9300: create marks files for double-import-marks test
Our tests confirm that providing two "import-marks" options in a
fast-import stream is an error. However, the invoked command would fail
even without covering this case, because the marks files themselves do
not actually exist.  Let's create the files to make sure we fail for the
right reason (we actually do, because the option parsing happens before
we open anything, but this future-proofs our test).

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:03 +01:00
f94804c1f2 t9300: drop some useless uses of cat
These waste a process, and make the line longer than it needs to be.

Signed-off-by: Jeff King <peff@peff.net>
2019-12-04 13:20:03 +01:00
0d0ac3826a Git 2.20.1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-15 12:31:34 +09:00
83243020c8 Merge branch 'jc/run-command-report-exec-failure-fix' into maint
A recent update accidentally squelched an error message when the
run_command API failed to run a missing command, which has been
corrected.

* jc/run-command-report-exec-failure-fix:
  run-command: report exec failure
2018-12-15 12:24:34 +09:00
916f56d38b Merge branch 'js/help-commands-verbose-by-default-fix' into maint
"git help -a" did not work well when an overly long alias is
defined, which has been corrected.

* js/help-commands-verbose-by-default-fix:
  help -a: handle aliases with long names gracefully
  help.h: fix coding style
2018-12-15 12:24:33 +09:00
bf29f074ed Merge branch 'nd/show-gitcomp-compilation-fix' into maint
Portability fix for a recent update to parse-options API.

* nd/show-gitcomp-compilation-fix:
  parse-options: fix SunCC compiler warning
2018-12-15 12:24:33 +09:00
6be6e6629f Merge branch 'js/t9902-send-email-completion-fix' into maint
* js/t9902-send-email-completion-fix:
  t9902: 'send-email' test case requires PERL
2018-12-15 12:24:32 +09:00
6cba471533 Merge branch 'js/mailinfo-format-flowed-fix' into maint
Test portability fix.

* js/mailinfo-format-flowed-fix:
  t4256: mark support files as LF-only
2018-12-15 12:24:32 +09:00
3f8c27c369 Merge branch 'ds/hash-independent-tests-fix' into maint
Test portability fix.

* ds/hash-independent-tests-fix:
  .gitattributes: ensure t/oid-info/* has eol=lf
2018-12-15 12:24:32 +09:00
fc767afe77 .gitattributes: ensure t/oid-info/* has eol=lf
The new test_oid machinery in the test library requires reading
some information from t/oid-info/hash-info and t/oid-info/oid.

The logic to read from these files in shell uses built-in "read"
command, which leaves CR at the end of these text files when they
are checked out with CRLF line endings, at least when run with bash
shipped with Git for Windows.  This results in an unexpected value
in the variable these lines are read into, leading the tests to
fail.

Mark them to be checked out always with the LF line endings.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-14 12:53:06 +09:00
0365b9ec59 t9902: 'send-email' test case requires PERL
The oneline notwithstanding, 13374987dd (completion: use _gitcompbuiltin
for format-patch, 2018-11-03) changed also the way send-email options
are completed, by asking the git send-email command itself what options
it offers.

Necessarily, this must fail when built with NO_PERL because send-email
itself is a Perl script. Which means that we need the PERL prerequisite
for the send-email test case in t9902.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-14 11:56:02 +09:00
98f2d930d1 t4256: mark support files as LF-only
The test t4256-am-format-flowed.sh requires carefully applying a
patch after ignoring padding whitespace. This breaks if the file
is munged to include CRLF line endings instead of LF.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-13 11:39:45 +09:00
a92ec7efe0 parse-options: fix SunCC compiler warning
The compiler reports this because show_gitcomp() never actually
returns a value:

    "parse-options.c", line 520: warning: Function has no return
    statement : show_gitcomp

We could shut the compiler up. But instead let's not bury exit() too
deep. Do the same as internal -h handling, return a special error code
and handle the exit() in parse_options() (and other
parse_options_step() callers) instead.

Reported-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-12 17:21:33 +09:00
1c4b985965 help -a: handle aliases with long names gracefully
We take pains to determine the longest command beforehand, so that we
can align the category column after printing the command names.

However, then we re-use that value when printing the aliases. If any
alias name is longer than the longest command name, we consequently try
to add a negative number of spaces (but `mput_char()` does not expect
any negative values and simply decrements until the value is 0, i.e.
it tries to add close to 2**31 spaces).

Let's fix this by adjusting the `longest` variable before printing the
aliases.

This fixes https://github.com/git-for-windows/git/issues/1975.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-12 17:18:38 +09:00
5acea87c3a help.h: fix coding style
We want a space after the `while` keyword.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-12 17:18:29 +09:00
e5a329a279 run-command: report exec failure
In 321fd823 ("run-command: mark path lookup errors with ENOENT",
2018-10-24), we rewrote the logic to execute a command by looking
in the directories on $PATH; as a side effect, a request to run a
command that is not found on $PATH is noticed even before a child
process is forked to execute it.

We however stopped to report an exec failure in such a case by
mistake.  Add a logic to report the error unless silent-exec-failure
is requested, to match the original code.

Reported-by: John Passaro <john.a.passaro@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-12 17:06:50 +09:00
5d826e9729 Git 2.20
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-09 13:16:21 +09:00
b5796d9a32 Merge tag 'l10n-2.20.0-rnd3' of https://github.com/git-l10n/git-po
l10n-2.20.0-rnd3

* tag 'l10n-2.20.0-rnd3' of https://github.com/git-l10n/git-po: (22 commits)
  l10n: de.po: fix two messages
  l10n: zh_CN: for git v2.20.0 l10n round 1 to 3
  l10n: update German translation
  l10n: bg.po: Updated Bulgarian translation (4187t)
  l10n: sv.po: Update Swedish translation (4187t0f0u)
  l10n: fr.po v2.20.0 round 3
  l10n: vi(4187t): Updated Vietnamese translation for v2.20.0 rd3
  l10n: es.po v2.20.0 round 3
  l10n: git.pot: v2.20.0 round 3 (5 new, 3 removed)
  l10n: vi(4185t): Updated Vietnamese translation for v2.20.0
  l10n: es.po v2.20.0 round 1
  l10n: bg.po: Updated Bulgarian translation (4185t)
  l10n: git.pot: v2.20.0 round 2 (2 new, 2 removed)
  l10n: bg.po: Updated Bulgarian translation (4185t)
  l10n: sv.po: Update Swedish translation (4185t0f0u)
  l10n: fr.po v2.20 rnd 1
  l10n: Update Catalan translation
  l10n: git.pot: v2.20.0 round 1 (254 new, 27 removed)
  l10n: Update Catalan translation
  l10n: vi.po: fix typo in pack-objects
  ...
2018-12-09 13:11:36 +09:00
0688c551a3 l10n: de.po: fix two messages
Reported-by: Phillip Szelat <phillip.szelat@gmail.com>
Signed-off-by: Ralf Thielow <ralf.thielow@gmail.com>
2018-12-07 19:43:07 +01:00
4c27fcb576 l10n: zh_CN: for git v2.20.0 l10n round 1 to 3
Translate 257 new messages (4187t0f0u) for git 2.20.0.

Reviewed-by: Zhou Fangyi <fangyi.zhou@yuriko.moe>
Reviewed-by: 依云 <lilydjwg@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2018-12-06 16:00:29 +08:00
cf4c0c2505 l10n: update German translation
Signed-off-by: Ralf Thielow <ralf.thielow@gmail.com>
2018-12-06 07:44:41 +01:00
965798d1f2 Merge branch 'es/format-patch-range-diff-fix-fix'
* es/format-patch-range-diff-fix-fix:
  range-diff: always pass at least minimal diff options
2018-12-04 12:49:50 +09:00
5335669531 Merge branch 'en/rebase-consistency'
* en/rebase-consistency:
  rebase docs: fix incorrect format of the section Behavioral Differences
2018-12-04 12:49:39 +09:00
6fcbad87d4 rebase docs: fix incorrect format of the section Behavioral Differences
The text body of section Behavioral Differences is typeset as code,
but should be regular text. Remove the indentation to achieve that.

While here, prettify the language:

- use "the x backend" instead of "x-based rebase";
- use present tense instead of future tense;

and use subsections instead of a list.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-04 11:50:52 +09:00
7e75a63d74 RelNotes 2.20: drop spurious double quote
We have three double-quote characters, which is one too many or too few.
Dropping the last one seems to match the original intention best.

Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-04 11:26:40 +09:00
7a49e44465 RelNotes 2.20: clarify sentence
I had to read this sentence a few times to understand it. Let's try to
clarify it.

Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-04 11:26:38 +09:00
00ac55c7bd RelNotes 2.20: move some items between sections
Some items that should be in "Performance, Internal Implementation,
Development Support etc." have ended up in "UI, Workflows & Features"
and "Fixes since v2.19". Move them, and do s/uses/use/ while at it.

Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-04 11:26:36 +09:00
ac0edf1f46 range-diff: always pass at least minimal diff options
Commit d8981c3f88 ("format-patch: do not let its diff-options affect
--range-diff", 2018-11-30) taught `show_range_diff()` to accept a
NULL-pointer as an indication that it should use its own "reasonable
default". That fixed a regression from a5170794 ("Merge branch
'ab/range-diff-no-patch'", 2018-11-18), but unfortunately it introduced
a regression of its own.

In particular, it means we forget the `file` member of the diff options,
so rather than placing a range-diff in the cover-letter, we write it to
stdout. In order to fix this, rewrite the two callers adjusted by
d8981c3f88 to instead create a "dummy" set of diff options where they
only fill in the fields we absolutely require, such as output file and
color.

Modify and extend the existing tests to try and verify that the right
contents end up in the right place.

Don't revert `show_range_diff()`, i.e., let it keep accepting NULL.
Rather than removing what is dead code and figuring out it isn't
actually dead and we've broken 2.20, just leave it for now.

[es: retain diff coloring when going to stdout]

Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-04 10:36:14 +09:00
609713eb66 Merge branch 'master' of git://github.com/alshopov/git-po 2018-12-03 12:49:45 +08:00
9290395fe9 l10n: bg.po: Updated Bulgarian translation (4187t)
Signed-off-by: Alexander Shopov <ash@kambanaria.org>
2018-12-02 16:09:21 +01:00
7345044379 l10n: sv.po: Update Swedish translation (4187t0f0u)
Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
2018-12-02 15:43:34 +01:00
a8f5329e04 Merge branch 'fr_2.20_round3' of git://github.com/jnavila/git 2018-12-02 22:36:36 +08:00
5386321271 l10n: fr.po v2.20.0 round 3
Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
2018-12-02 11:03:23 +01:00
cd1eab5882 Merge branch 'master' of https://github.com/vnwildman/git 2018-12-02 17:57:24 +08:00
8173e9c772 l10n: vi(4187t): Updated Vietnamese translation for v2.20.0 rd3
Signed-off-by: Tran Ngoc Quan <vnwildman@gmail.com>
2018-12-02 14:15:00 +07:00
7c6767be71 l10n: es.po v2.20.0 round 3
Signed-off-by: Christopher Diaz Riveros <chrisadr@gentoo.org>
2018-12-01 23:17:55 -05:00
578d8ea381 l10n: git.pot: v2.20.0 round 3 (5 new, 3 removed)
Generate po/git.pot from v2.20.0-rc2 for git v2.20.0 l10n round 3.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2018-12-02 10:56:26 +08:00
924a01f237 Merge branch 'master' of git://github.com/git-l10n/git-po 2018-12-02 10:55:14 +08:00
26a567e4c3 Merge branch 'master' of https://github.com/vnwildman/git 2018-12-02 10:25:09 +08:00
84368b62b0 l10n: vi(4185t): Updated Vietnamese translation for v2.20.0
Signed-off-by: Tran Ngoc Quan <vnwildman@gmail.com>
2018-12-02 09:00:09 +07:00
0960a4beff l10n: es.po v2.20.0 round 1
Signed-off-by: Christopher Diaz Riveros <chrisadr@gentoo.org>
2018-12-01 14:09:28 -05:00
d0a6644845 l10n: bg.po: Updated Bulgarian translation (4185t)
Signed-off-by: Alexander Shopov <ash@kambanaria.org>
2018-12-01 11:48:08 +01:00
36dd6933bd l10n: git.pot: v2.20.0 round 2 (2 new, 2 removed)
Generate po/git.pot from v2.20.0-rc1-10-g7068cbc4ab for git v2.20.0 l10n
round 2.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2018-12-01 16:15:51 +08:00
5d14a9d7c0 Merge branch 'master' of git://github.com/git-l10n/git-po 2018-12-01 16:14:16 +08:00
5362f4d97f Merge branch 'master' of git://github.com/alshopov/git-po 2018-12-01 16:13:31 +08:00
1a8e317e53 Merge branch 'master' of git://github.com/nafmo/git-l10n-sv 2018-12-01 16:11:45 +08:00
9cf1f0d08d Merge branch 'fr_2.20_rnd1' of git://github.com/jnavila/git 2018-12-01 15:36:53 +08:00
3154af4a0d l10n: bg.po: Updated Bulgarian translation (4185t)
Signed-off-by: Alexander Shopov <ash@kambanaria.org>
2018-11-30 23:17:50 +01:00
4a81e9cd6d l10n: sv.po: Update Swedish translation (4185t0f0u)
Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
2018-11-29 12:55:22 +01:00
6b822f7359 l10n: fr.po v2.20 rnd 1
Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
2018-11-28 22:16:56 +01:00
d9324e4ed0 l10n: Update Catalan translation
Signed-off-by: Jordi Mas <jmas@softcatala.org>
2018-11-25 14:53:41 +01:00
1f5a6ca2c7 Merge branch 'master' of git://github.com/git-l10n/git-po 2018-11-20 10:07:25 +08:00
1270554665 l10n: git.pot: v2.20.0 round 1 (254 new, 27 removed)
Generate po/git.pot from v2.20.0-rc0-23-gbb75be6cb9 for git v2.20.0 l10n
round 1.

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2018-11-20 10:06:16 +08:00
87af1bb0d8 Merge branch 'master' of https://github.com/Softcatala/git-po 2018-11-18 21:36:13 +08:00
f3131eecdb l10n: Update Catalan translation
Signed-off-by: Jordi Mas <jmas@softcatala.org>
2018-11-11 16:35:19 +01:00
232b63c440 l10n: vi.po: fix typo in pack-objects
Signed-off-by: Tran Ngoc Quan <vnwildman@gmail.com>
2018-11-01 13:26:08 +07:00
38bfde23f1 l10n: update German translation
Signed-off-by: Ralf Thielow <ralf.thielow@gmail.com>
2018-10-19 19:34:36 +02:00
e640d114ab l10n: zh_CN: review for git v2.19.0 l10n
Reviewed-by: 依云 <lilydjwg@gmail.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
2018-09-12 10:14:07 +08:00
70 changed files with 45785 additions and 29062 deletions

1
.gitattributes vendored
View File

@ -9,6 +9,7 @@
/command-list.txt eol=lf
/GIT-VERSION-GEN eol=lf
/mergetools/* eol=lf
/t/oid-info/* eol=lf
/Documentation/git-merge.txt conflict-marker-size=32
/Documentation/gitk.txt conflict-marker-size=32
/Documentation/user-manual.txt conflict-marker-size=32

View File

@ -0,0 +1,54 @@
Git v2.14.6 Release Notes
=========================
This release addresses the security issues CVE-2019-1348,
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387.
Fixes since v2.14.5
-------------------
* CVE-2019-1348:
The --export-marks option of git fast-import is exposed also via
the in-stream command feature export-marks=... and it allows
overwriting arbitrary paths.
* CVE-2019-1349:
When submodules are cloned recursively, under certain circumstances
Git could be fooled into using the same Git directory twice. We now
require the directory to be empty.
* CVE-2019-1350:
Incorrect quoting of command-line arguments allowed remote code
execution during a recursive clone in conjunction with SSH URLs.
* CVE-2019-1351:
While the only permitted drive letters for physical drives on
Windows are letters of the US-English alphabet, this restriction
does not apply to virtual drives assigned via subst <letter>:
<path>. Git mistook such paths for relative paths, allowing writing
outside of the worktree while cloning.
* CVE-2019-1352:
Git was unaware of NTFS Alternate Data Streams, allowing files
inside the .git/ directory to be overwritten during a clone.
* CVE-2019-1353:
When running Git in the Windows Subsystem for Linux (also known as
"WSL") while accessing a working directory on a regular Windows
drive, none of the NTFS protections were active.
* CVE-2019-1354:
Filenames on Linux/Unix can contain backslashes. On Windows,
backslashes are directory separators. Git did not use to refuse to
write out tracked files with such filenames.
* CVE-2019-1387:
Recursive clones are currently affected by a vulnerability that is
caused by too-lax validation of submodule names, allowing very
targeted attacks via remote code execution in recursive clones.
Credit for finding these vulnerabilities goes to Microsoft Security
Response Center, in particular to Nicolas Joly. The `fast-import`
fixes were provided by Jeff King, the other fixes by Johannes
Schindelin with help from Garima Singh.

View File

@ -0,0 +1,11 @@
Git v2.15.4 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6 to address
the security issues CVE-2019-1348, CVE-2019-1349, CVE-2019-1350,
CVE-2019-1351, CVE-2019-1352, CVE-2019-1353, CVE-2019-1354, and
CVE-2019-1387; see the release notes for that version for details.
In conjunction with a vulnerability that was fixed in v2.20.2,
`.gitmodules` is no longer allowed to contain entries of the form
`submodule.<name>.update=!command`.

View File

@ -0,0 +1,8 @@
Git v2.16.6 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6 and in
v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
CVE-2019-1354, and CVE-2019-1387; see the release notes for those
versions for details.

View File

@ -0,0 +1,12 @@
Git v2.17.3 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6 and in
v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
CVE-2019-1354, and CVE-2019-1387; see the release notes for those
versions for details.
In addition, `git fsck` was taught to identify `.gitmodules` entries
of the form `submodule.<name>.update=!command`, which have been
disallowed in v2.15.4.

View File

@ -0,0 +1,8 @@
Git v2.18.2 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6, v2.15.4
and in v2.17.3, addressing the security issues CVE-2019-1348,
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
for those versions for details.

View File

@ -0,0 +1,8 @@
Git v2.19.3 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6, v2.15.4
and in v2.17.3, addressing the security issues CVE-2019-1348,
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
for those versions for details.

View File

@ -137,11 +137,6 @@ UI, Workflows & Features
command line, or setting sendemail.suppresscc configuration
variable to "misc-by", can be used to disable this behaviour.
* Developer builds now uses -Wunused-function compilation option.
* One of our CI tests to run with "unusual/experimental/random"
settings now also uses commit-graph and midx.
* "git mergetool" learned to take the "--[no-]gui" option, just like
"git difftool" does.
@ -185,6 +180,11 @@ UI, Workflows & Features
Performance, Internal Implementation, Development Support etc.
* Developer builds now use -Wunused-function compilation option.
* One of our CI tests to run with "unusual/experimental/random"
settings now also uses commit-graph and midx.
* When there are too many packfiles in a repository (which is not
recommended), looking up an object in these would require
consulting many pack .idx files; a new mechanism to have a single
@ -305,7 +305,7 @@ Performance, Internal Implementation, Development Support etc.
* The overly large Documentation/config.txt file have been split into
million little pieces. This potentially allows each individual piece
included into the manual page of the command it affects more easily.
to be included into the manual page of the command it affects more easily.
* Replace three string-list instances used as look-up tables in "git
fetch" with hashmaps.
@ -387,6 +387,14 @@ Performance, Internal Implementation, Development Support etc.
two classes to ease code migration process has been proposed and
its support has been added to the Makefile.
* The "container" mode of TravisCI is going away. Our .travis.yml
file is getting prepared for the transition.
(merge 32ee384be8 ss/travis-ci-force-vm-mode later to maint).
* Our test scripts can now take the '-V' option as a synonym for the
'--verbose-log' option.
(merge a5f52c6dab sg/test-verbose-log later to maint).
Fixes since v2.19
-----------------
@ -544,14 +552,6 @@ Fixes since v2.19
didn't make much sense. This has been corrected.
(merge 669b1d2aae md/exclude-promisor-objects-fix later to maint).
* The "container" mode of TravisCI is going away. Our .travis.yml
file is getting prepared for the transition.
(merge 32ee384be8 ss/travis-ci-force-vm-mode later to maint).
* Our test scripts can now take the '-V' option as a synonym for the
'--verbose-log' option.
(merge a5f52c6dab sg/test-verbose-log later to maint).
* A regression in Git 2.12 era made "git fsck" fall into an infinite
loop while processing truncated loose objects.
(merge 18ad13e5b2 jk/detect-truncated-zlib-input later to maint).
@ -578,7 +578,7 @@ Fixes since v2.19
* "git rev-parse --exclude=* --branches --branches" (i.e. first
saying "add only things that do not match '*' out of all branches"
and then adding all branches, without any exclusion this time")
and then adding all branches, without any exclusion this time)
worked as expected, but "--exclude=* --all --all" did not work the
same way, which has been fixed.
(merge 5221048092 ag/rev-parse-all-exclude-fix later to maint).

View File

@ -0,0 +1,20 @@
Git v2.20.1 Release Notes
=========================
This release is primarily to fix brown-paper-bag breakages in the
2.20.0 release.
Fixes since v2.20
-----------------
* A few newly added tests were not portable and caused minority
platforms to report false breakages, which have been fixed.
* Portability fix for a recent update to parse-options API.
* "git help -a" did not work well when an overly long alias is
defined, which has been corrected.
* A recent update accidentally squelched an error message when the
run_command API failed to run a missing command, which has been
corrected.

View File

@ -0,0 +1,18 @@
Git v2.20.2 Release Notes
=========================
This release merges up the fixes that appear in v2.14.6, v2.15.4
and in v2.17.3, addressing the security issues CVE-2019-1348,
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
for those versions for details.
The change to disallow `submodule.<name>.update=!command` entries in
`.gitmodules` which was introduced v2.15.4 (and for which v2.17.3
added explicit fsck checks) fixes the vulnerability in v2.20.x where a
recursive clone followed by a submodule update could execute code
contained within the repository without the user explicitly having
asked for that (CVE-2019-19604).
Credit for finding this vulnerability goes to Joern Schneeweisz,
credit for the fixes goes to Jonathan Nieder.

View File

@ -50,6 +50,21 @@ OPTIONS
memory used by fast-import during this run. Showing this output
is currently the default, but can be disabled with --quiet.
--allow-unsafe-features::
Many command-line options can be provided as part of the
fast-import stream itself by using the `feature` or `option`
commands. However, some of these options are unsafe (e.g.,
allowing fast-import to access the filesystem outside of the
repository). These options are disabled by default, but can be
allowed by providing this option on the command line. This
currently impacts only the `export-marks`, `import-marks`, and
`import-marks-if-exists` feature commands.
+
Only enable this option if you trust the program generating the
fast-import stream! This option is enabled automatically for
remote-helpers that use the `import` capability, as they are
already trusted to run their own code.
Options for Frontends
~~~~~~~~~~~~~~~~~~~~~

View File

@ -550,24 +550,28 @@ Other incompatible flag pairs:
BEHAVIORAL DIFFERENCES
-----------------------
* empty commits:
There are some subtle differences how the backends behave.
am-based rebase will drop any "empty" commits, whether the
commit started empty (had no changes relative to its parent to
start with) or ended empty (all changes were already applied
upstream in other commits).
Empty commits
~~~~~~~~~~~~~
merge-based rebase does the same.
The am backend drops any "empty" commits, regardless of whether the
commit started empty (had no changes relative to its parent to
start with) or ended empty (all changes were already applied
upstream in other commits).
interactive-based rebase will by default drop commits that
started empty and halt if it hits a commit that ended up empty.
The `--keep-empty` option exists for interactive rebases to allow
it to keep commits that started empty.
The merge backend does the same.
* directory rename detection:
The interactive backend drops commits by default that
started empty and halts if it hits a commit that ended up empty.
The `--keep-empty` option exists for the interactive backend to allow
it to keep commits that started empty.
merge-based and interactive-based rebases work fine with
directory rename detection. am-based rebases sometimes do not.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
The merge and interactive backends work fine with
directory rename detection. The am backend sometimes does not.
include::merge-strategies.txt[]

View File

@ -44,9 +44,8 @@ submodule.<name>.update::
submodule init` to initialize the configuration variable of
the same name. Allowed values here are 'checkout', 'rebase',
'merge' or 'none'. See description of 'update' command in
linkgit:git-submodule[1] for their meaning. Note that the
'!command' form is intentionally ignored here for security
reasons.
linkgit:git-submodule[1] for their meaning. For security
reasons, the '!command' form is not accepted here.
submodule.<name>.branch::
A remote branch name for tracking updates in the upstream submodule.

View File

@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v2.20.0-rc2
DEF_VER=v2.20.2
LF='
'

View File

@ -1 +1 @@
Documentation/RelNotes/2.20.0.txt
Documentation/RelNotes/2.20.2.txt

View File

@ -850,6 +850,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
case PARSE_OPT_DONE:
if (ctx.argv[0])
dashdash_pos = ctx.cpidx;

View File

@ -768,7 +768,7 @@ static int checkout(int submodule_progress)
if (!err && (option_recurse_submodules.nr > 0)) {
struct argv_array args = ARGV_ARRAY_INIT;
argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
if (option_shallow_submodules == 1)
argv_array_push(&args, "--depth=1");

View File

@ -1094,9 +1094,18 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
}
if (rev->rdiff1) {
/*
* Pass minimum required diff-options to range-diff; others
* can be added later if deemed desirable.
*/
struct diff_options opts;
diff_setup(&opts);
opts.file = rev->diffopt.file;
opts.use_color = rev->diffopt.use_color;
diff_setup_done(&opts);
fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
show_range_diff(rev->rdiff1, rev->rdiff2,
rev->creation_factor, 1, NULL);
rev->creation_factor, 1, &opts);
}
}

View File

@ -287,6 +287,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
case PARSE_OPT_DONE:
goto parse_done;
}

View File

@ -18,6 +18,7 @@
#include "diffcore.h"
#include "diff.h"
#include "object-store.h"
#include "dir.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@ -1354,7 +1355,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
char *p, *path = NULL, *sm_gitdir;
struct strbuf sb = STRBUF_INIT;
struct string_list reference = STRING_LIST_INIT_NODUP;
int dissociate = 0;
int dissociate = 0, require_init = 0;
char *sm_alternate = NULL, *error_strategy = NULL;
struct option module_clone_options[] = {
@ -1381,6 +1382,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
OPT_BOOL(0, "progress", &progress,
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &require_init,
N_("disallow cloning into non-empty directory")),
OPT_END()
};
@ -1408,6 +1411,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
} else
path = xstrdup(path);
if (validate_submodule_git_dir(sm_gitdir, name) < 0)
die(_("refusing to create/use '%s' in another submodule's "
"git dir"), sm_gitdir);
if (!file_exists(sm_gitdir)) {
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
@ -1419,6 +1426,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
} else {
if (require_init && !access(path, X_OK) && !is_empty_dir(path))
die(_("directory not empty: '%s'"), path);
if (safe_create_leading_directories_const(path) < 0)
die(_("could not create directory '%s'"), path);
strbuf_addf(&sb, "%s/index", sm_gitdir);
@ -1473,6 +1482,8 @@ static void determine_submodule_update_strategy(struct repository *r,
die(_("Invalid update mode '%s' configured for submodule path '%s'"),
val, path);
} else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
if (sub->update_strategy.type == SM_UPDATE_COMMAND)
BUG("how did we read update = !command from .gitmodules?");
out->type = sub->update_strategy.type;
out->command = sub->update_strategy.command;
} else
@ -1531,6 +1542,7 @@ struct submodule_update_clone {
int recommend_shallow;
struct string_list references;
int dissociate;
unsigned require_init;
const char *depth;
const char *recursive_prefix;
const char *prefix;
@ -1549,7 +1561,7 @@ struct submodule_update_clone {
int max_jobs;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
NULL, NULL, NULL, \
NULL, 0, 0, 0, NULL, 0, 0, 0}
@ -1676,6 +1688,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
if (suc->recommend_shallow && sub->recommend_shallow == 1)
argv_array_push(&child->args, "--depth=1");
if (suc->require_init)
argv_array_push(&child->args, "--require-init");
argv_array_pushl(&child->args, "--path", sub->path, NULL);
argv_array_pushl(&child->args, "--name", sub->name, NULL);
argv_array_pushl(&child->args, "--url", url, NULL);
@ -1866,6 +1880,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
OPT_BOOL(0, "progress", &suc.progress,
N_("force cloning progress")),
OPT_BOOL(0, "require-init", &suc.require_init,
N_("disallow cloning into non-empty directory")),
OPT_END()
};

View File

@ -1086,6 +1086,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
{

View File

@ -389,6 +389,12 @@ int mingw_mkdir(const char *path, int mode)
{
int ret;
wchar_t wpath[MAX_PATH];
if (!is_valid_win32_path(path)) {
errno = EINVAL;
return -1;
}
if (xutftowcs_path(wpath, path) < 0)
return -1;
ret = _wmkdir(wpath);
@ -462,7 +468,7 @@ int mingw_open (const char *filename, int oflags, ...)
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
va_list args;
unsigned mode;
int fd;
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
wchar_t wfilename[MAX_PATH];
open_fn_t open_fn;
@ -470,6 +476,11 @@ int mingw_open (const char *filename, int oflags, ...)
mode = va_arg(args, int);
va_end(args);
if (!is_valid_win32_path(filename)) {
errno = create ? EINVAL : ENOENT;
return -1;
}
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
@ -536,6 +547,11 @@ FILE *mingw_fopen (const char *filename, const char *otype)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (!is_valid_win32_path(filename)) {
int create = otype && strchr(otype, 'w');
errno = create ? EINVAL : ENOENT;
return NULL;
}
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@ -558,6 +574,11 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (!is_valid_win32_path(filename)) {
int create = otype && strchr(otype, 'w');
errno = create ? EINVAL : ENOENT;
return NULL;
}
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@ -1051,7 +1072,7 @@ static const char *quote_arg(const char *arg)
p++;
len++;
}
if (*p == '"')
if (*p == '"' || !*p)
n += count*2 + 1;
continue;
}
@ -1073,16 +1094,19 @@ static const char *quote_arg(const char *arg)
count++;
*d++ = *arg++;
}
if (*arg == '"') {
if (*arg == '"' || !*arg) {
while (count-- > 0)
*d++ = '\\';
/* don't escape the surrounding end quote */
if (!*arg)
break;
*d++ = '\\';
}
}
*d++ = *arg++;
}
*d++ = '"';
*d++ = 0;
*d++ = '\0';
return q;
}
@ -2275,6 +2299,30 @@ pid_t waitpid(pid_t pid, int *status, int options)
return -1;
}
int mingw_has_dos_drive_prefix(const char *path)
{
int i;
/*
* Does it start with an ASCII letter (i.e. highest bit not set),
* followed by a colon?
*/
if (!(0x80 & (unsigned char)*path))
return *path && path[1] == ':' ? 2 : 0;
/*
* While drive letters must be letters of the English alphabet, it is
* possible to assign virtually _any_ Unicode character via `subst` as
* a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
* like this:
*
* subst ֍: %USERPROFILE%\Desktop
*/
for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
; /* skip first UTF-8 character */
return path[i] == ':' ? i + 1 : 0;
}
int mingw_skip_dos_drive_prefix(char **path)
{
int ret = has_dos_drive_prefix(*path);
@ -2416,6 +2464,50 @@ static void setup_windows_environment(void)
setenv("TERM", "cygwin", 1);
}
int is_valid_win32_path(const char *path)
{
int preceding_space_or_period = 0, i = 0, periods = 0;
if (!protect_ntfs)
return 1;
skip_dos_drive_prefix((char **)&path);
for (;;) {
char c = *(path++);
switch (c) {
case '\0':
case '/': case '\\':
/* cannot end in ` ` or `.`, except for `.` and `..` */
if (preceding_space_or_period &&
(i != periods || periods > 2))
return 0;
if (!c)
return 1;
i = periods = preceding_space_or_period = 0;
continue;
case '.':
periods++;
/* fallthru */
case ' ':
preceding_space_or_period = 1;
i++;
continue;
case ':': /* DOS drive prefix was already skipped */
case '<': case '>': case '"': case '|': case '?': case '*':
/* illegal character */
return 0;
default:
if (c > '\0' && c < '\x20')
/* illegal character */
return 0;
}
preceding_space_or_period = 0;
i++;
}
}
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
* mingw startup code, see init.c in mingw runtime).

View File

@ -443,8 +443,8 @@ HANDLE winansi_get_osfhandle(int fd);
* git specific compatibility
*/
#define has_dos_drive_prefix(path) \
(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
int mingw_has_dos_drive_prefix(const char *path);
#define has_dos_drive_prefix mingw_has_dos_drive_prefix
int mingw_skip_dos_drive_prefix(char **path);
#define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
static inline int mingw_is_dir_sep(int c)
@ -479,6 +479,20 @@ extern char *mingw_query_user_email(void);
#include <inttypes.h>
#endif
/**
* Verifies that the given path is a valid one on Windows.
*
* In particular, path segments are disallowed which
*
* - end in a period or a space (except the special directories `.` and `..`).
*
* - contain any of the reserved characters, e.g. `:`, `;`, `*`, etc
*
* Returns 1 upon success, otherwise 0.
*/
int is_valid_win32_path(const char *path);
#define is_valid_path(path) is_valid_win32_path(path)
/**
* Converts UTF-8 encoded string to UTF-16LE.
*

View File

@ -396,7 +396,6 @@ ifeq ($(uname_S),Windows)
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
PTHREAD_LIBS =
lib =
BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
ifndef DEBUG
BASIC_CFLAGS += -GL -Os -MD
BASIC_LDFLAGS += -LTCG
@ -529,7 +528,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/dirent.o
BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
BASIC_CFLAGS += -DWIN32
EXTLIBS += -lws2_32
GITLIBS += git.res
PTHREAD_LIBS =

View File

@ -514,7 +514,7 @@ int url_is_local_not_ssh(const char *url)
const char *colon = strchr(url, ':');
const char *slash = strchr(url, '/');
return !colon || (slash && slash < colon) ||
has_dos_drive_prefix(url);
(has_dos_drive_prefix(url) && is_valid_path(url));
}
static const char *prot_name(enum protocol protocol)

View File

@ -80,7 +80,7 @@ enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
int protect_hfs = PROTECT_HFS_DEFAULT;
#ifndef PROTECT_NTFS_DEFAULT
#define PROTECT_NTFS_DEFAULT 0
#define PROTECT_NTFS_DEFAULT 1
#endif
int protect_ntfs = PROTECT_NTFS_DEFAULT;
const char *core_fsmonitor;

View File

@ -364,6 +364,7 @@ static uintmax_t next_mark;
static struct strbuf new_data = STRBUF_INIT;
static int seen_data_command;
static int require_explicit_termination;
static int allow_unsafe_features;
/* Signal handling */
static volatile sig_atomic_t checkpoint_requested;
@ -1826,6 +1827,12 @@ static void dump_marks(void)
if (!export_marks_file || (import_marks_file && !import_marks_file_done))
return;
if (safe_create_leading_directories_const(export_marks_file)) {
failure |= error_errno("unable to create leading directories of %s",
export_marks_file);
return;
}
if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
failure |= error_errno("Unable to write marks file %s",
export_marks_file);
@ -3198,7 +3205,6 @@ static void option_import_marks(const char *marks,
}
import_marks_file = make_fast_import_path(marks);
safe_create_leading_directories_const(import_marks_file);
import_marks_file_from_stream = from_stream;
import_marks_file_ignore_missing = ignore_missing;
}
@ -3239,7 +3245,6 @@ static void option_active_branches(const char *branches)
static void option_export_marks(const char *marks)
{
export_marks_file = make_fast_import_path(marks);
safe_create_leading_directories_const(export_marks_file);
}
static void option_cat_blob_fd(const char *fd)
@ -3282,10 +3287,12 @@ static int parse_one_option(const char *option)
option_active_branches(option);
} else if (skip_prefix(option, "export-pack-edges=", &option)) {
option_export_pack_edges(option);
} else if (starts_with(option, "quiet")) {
} else if (!strcmp(option, "quiet")) {
show_stats = 0;
} else if (starts_with(option, "stats")) {
} else if (!strcmp(option, "stats")) {
show_stats = 1;
} else if (!strcmp(option, "allow-unsafe-features")) {
; /* already handled during early option parsing */
} else {
return 0;
}
@ -3293,6 +3300,13 @@ static int parse_one_option(const char *option)
return 1;
}
static void check_unsafe_feature(const char *feature, int from_stream)
{
if (from_stream && !allow_unsafe_features)
die(_("feature '%s' forbidden in input without --allow-unsafe-features"),
feature);
}
static int parse_one_feature(const char *feature, int from_stream)
{
const char *arg;
@ -3300,10 +3314,13 @@ static int parse_one_feature(const char *feature, int from_stream)
if (skip_prefix(feature, "date-format=", &arg)) {
option_date_format(arg);
} else if (skip_prefix(feature, "import-marks=", &arg)) {
check_unsafe_feature("import-marks", from_stream);
option_import_marks(arg, from_stream, 0);
} else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
check_unsafe_feature("import-marks-if-exists", from_stream);
option_import_marks(arg, from_stream, 1);
} else if (skip_prefix(feature, "export-marks=", &arg)) {
check_unsafe_feature(feature, from_stream);
option_export_marks(arg);
} else if (!strcmp(feature, "get-mark")) {
; /* Don't die - this feature is supported */
@ -3430,6 +3447,20 @@ int cmd_main(int argc, const char **argv)
avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
/*
* We don't parse most options until after we've seen the set of
* "feature" lines at the start of the stream (which allows the command
* line to override stream data). But we must do an early parse of any
* command-line options that impact how we interpret the feature lines.
*/
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg != '-' || !strcmp(arg, "--"))
break;
if (!strcmp(arg, "--allow-unsafe-features"))
allow_unsafe_features = 1;
}
global_argc = argc;
global_argv = argv;

25
fsck.c
View File

@ -68,6 +68,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(GITMODULES_SYMLINK, ERROR) \
FUNC(GITMODULES_URL, ERROR) \
FUNC(GITMODULES_PATH, ERROR) \
FUNC(GITMODULES_UPDATE, ERROR) \
/* warnings */ \
FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
@ -605,7 +606,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
while (desc.size) {
unsigned mode;
const char *name;
const char *name, *backslash;
const struct object_id *oid;
oid = tree_entry_extract(&desc, &name, &mode);
@ -627,6 +628,22 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
".gitmodules is a symbolic link");
}
if ((backslash = strchr(name, '\\'))) {
while (backslash) {
backslash++;
has_dotgit |= is_ntfs_dotgit(backslash);
if (is_ntfs_dotgitmodules(backslash)) {
if (!S_ISLNK(mode))
oidset_insert(&gitmodules_found, oid);
else
retval += report(options, &item->object,
FSCK_MSG_GITMODULES_SYMLINK,
".gitmodules is a symbolic link");
}
backslash = strchr(backslash, '\\');
}
}
if (update_tree_entry_gently(&desc)) {
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
break;
@ -1000,6 +1017,12 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
FSCK_MSG_GITMODULES_PATH,
"disallowed submodule path: %s",
value);
if (!strcmp(key, "update") && value &&
parse_submodule_update_type(value) == SM_UPDATE_COMMAND)
data->ret |= report(data->options, data->obj,
FSCK_MSG_GITMODULES_UPDATE,
"disallowed submodule update setting: %s",
value);
free(name);
return 0;

View File

@ -385,6 +385,10 @@ static inline int git_offset_1st_component(const char *path)
#define offset_1st_component git_offset_1st_component
#endif
#ifndef is_valid_path
#define is_valid_path(path) 1
#endif
#ifndef find_last_dir_sep
static inline char *git_find_last_dir_sep(const char *path)
{

View File

@ -34,6 +34,7 @@ reference=
cached=
recursive=
init=
require_init=
files=
remote=
nofetch=
@ -457,6 +458,10 @@ cmd_update()
-i|--init)
init=1
;;
--require-init)
init=1
require_init=1
;;
--remote)
remote=1
;;
@ -539,6 +544,7 @@ cmd_update()
${reference:+"$reference"} \
${dissociate:+"--dissociate"} \
${depth:+--depth "$depth"} \
${require_init:+--require-init} \
$recommend_shallow \
$jobs \
"$@" || echo "#unmatched" $?

10
help.c
View File

@ -83,8 +83,9 @@ static void print_command_list(const struct cmdname_help *cmds,
for (i = 0; cmds[i].name; i++) {
if (cmds[i].category & mask) {
size_t len = strlen(cmds[i].name);
printf(" %s ", cmds[i].name);
mput_char(' ', longest - strlen(cmds[i].name));
mput_char(' ', longest > len ? longest - len : 1);
puts(_(cmds[i].help));
}
}
@ -526,6 +527,13 @@ void list_all_cmds_help(void)
git_config(get_alias, &alias_list);
string_list_sort(&alias_list);
for (i = 0; i < alias_list.nr; i++) {
size_t len = strlen(alias_list.items[i].string);
if (longest < len)
longest = len;
}
if (alias_list.nr) {
printf("\n%s\n", _("Command aliases"));
ALLOC_ARRAY(aliases, alias_list.nr + 1);

2
help.h
View File

@ -15,7 +15,7 @@ struct cmdnames {
static inline void mput_char(char c, unsigned int num)
{
while(num--)
while (num--)
putchar(c);
}

View File

@ -755,14 +755,23 @@ void show_log(struct rev_info *opt)
if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
struct diff_queue_struct dq;
struct diff_options opts;
memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
DIFF_QUEUE_CLEAR(&diff_queued_diff);
next_commentary_block(opt, NULL);
fprintf_ln(opt->diffopt.file, "%s", opt->rdiff_title);
/*
* Pass minimum required diff-options to range-diff; others
* can be added later if deemed desirable.
*/
diff_setup(&opts);
opts.file = opt->diffopt.file;
opts.use_color = opt->diffopt.use_color;
diff_setup_done(&opts);
show_range_diff(opt->rdiff1, opt->rdiff2,
opt->creation_factor, 1, NULL);
opt->creation_factor, 1, &opts);
memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
}

View File

@ -516,7 +516,7 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
show_negated_gitcomp(original_opts, -1);
show_negated_gitcomp(original_opts, nr_noopts);
fputc('\n', stdout);
exit(0);
return PARSE_OPT_COMPLETE;
}
static int usage_with_options_internal(struct parse_opt_ctx_t *,
@ -638,6 +638,8 @@ int parse_options(int argc, const char **argv, const char *prefix,
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
case PARSE_OPT_COMPLETE:
exit(0);
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
break;

View File

@ -208,6 +208,7 @@ extern int opterror(const struct option *opt, const char *reason, int flags);
/*----- incremental advanced APIs -----*/
enum {
PARSE_OPT_COMPLETE = -2,
PARSE_OPT_HELP = -1,
PARSE_OPT_DONE,
PARSE_OPT_NON_OPTION,

98
path.c
View File

@ -1292,37 +1292,77 @@ int daemon_avoid_alias(const char *p)
}
}
static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
{
if (len < skip)
return 0;
len -= skip;
path += skip;
while (len-- > 0) {
char c = *(path++);
if (c != ' ' && c != '.')
return 0;
}
return 1;
}
/*
* On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
* directory:
*
* - For historical reasons, file names that end in spaces or periods are
* automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
* to `.git/`.
*
* - For other historical reasons, file names that do not conform to the 8.3
* format (up to eight characters for the basename, three for the file
* extension, certain characters not allowed such as `+`, etc) are associated
* with a so-called "short name", at least on the `C:` drive by default.
* Which means that `git~1/` is a valid way to refer to `.git/`.
*
* Note: Technically, `.git/` could receive the short name `git~2` if the
* short name `git~1` were already used. In Git, however, we guarantee that
* `.git` is the first item in a directory, therefore it will be associated
* with the short name `git~1` (unless short names are disabled).
*
* - For yet other historical reasons, NTFS supports so-called "Alternate Data
* Streams", i.e. metadata associated with a given file, referred to via
* `<filename>:<stream-name>:<stream-type>`. There exists a default stream
* type for directories, allowing `.git/` to be accessed via
* `.git::$INDEX_ALLOCATION/`.
*
* When this function returns 1, it indicates that the specified file/directory
* name refers to a `.git` file or directory, or to any of these synonyms, and
* Git should therefore not track it.
*
* For performance reasons, _all_ Alternate Data Streams of `.git/` are
* forbidden, not just `::$INDEX_ALLOCATION`.
*
* This function is intended to be used by `git fsck` even on platforms where
* the backslash is a regular filename character, therefore it needs to handle
* backlash characters in the provided `name` specially: they are interpreted
* as directory separators.
*/
int is_ntfs_dotgit(const char *name)
{
size_t len;
char c;
for (len = 0; ; len++)
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
if (only_spaces_and_periods(name, len, 4) &&
!strncasecmp(name, ".git", 4))
return 1;
if (only_spaces_and_periods(name, len, 5) &&
!strncasecmp(name, "git~1", 5))
return 1;
if (name[len] != '\\')
return 0;
name += len + 1;
len = -1;
}
/*
* Note that when we don't find `.git` or `git~1` we end up with `name`
* advanced partway through the string. That's okay, though, as we
* return immediately in those cases, without looking at `name` any
* further.
*/
c = *(name++);
if (c == '.') {
/* .git */
if (((c = *(name++)) != 'g' && c != 'G') ||
((c = *(name++)) != 'i' && c != 'I') ||
((c = *(name++)) != 't' && c != 'T'))
return 0;
} else if (c == 'g' || c == 'G') {
/* git ~1 */
if (((c = *(name++)) != 'i' && c != 'I') ||
((c = *(name++)) != 't' && c != 'T') ||
*(name++) != '~' ||
*(name++) != '1')
return 0;
} else
return 0;
for (;;) {
c = *(name++);
if (!c || c == '\\' || c == '/' || c == ':')
return 1;
if (c != '.' && c != ' ')
return 0;
}
}
static int is_ntfs_dot_generic(const char *name,
@ -1338,7 +1378,7 @@ static int is_ntfs_dot_generic(const char *name,
only_spaces_and_periods:
for (;;) {
char c = name[i++];
if (!c)
if (!c || c == ':')
return 1;
if (c != ' ' && c != '.')
return 0;

7372
po/bg.po

File diff suppressed because it is too large Load Diff

11507
po/ca.po

File diff suppressed because it is too large Load Diff

10520
po/de.po

File diff suppressed because it is too large Load Diff

7608
po/es.po

File diff suppressed because it is too large Load Diff

7314
po/fr.po

File diff suppressed because it is too large Load Diff

7343
po/git.pot

File diff suppressed because it is too large Load Diff

7292
po/sv.po

File diff suppressed because it is too large Load Diff

7327
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -955,6 +955,9 @@ int verify_path(const char *path, unsigned mode)
if (has_dos_drive_prefix(path))
return 0;
if (!is_valid_path(path))
return 0;
goto inside;
for (;;) {
if (!c)
@ -982,7 +985,15 @@ inside:
if ((c == '.' && !verify_dotfile(path, mode)) ||
is_dir_sep(c) || c == '\0')
return 0;
} else if (c == '\\' && protect_ntfs) {
if (is_ntfs_dotgit(path))
return 0;
if (S_ISLNK(mode)) {
if (is_ntfs_dotgitmodules(path))
return 0;
}
}
c = *path++;
}
}

View File

@ -728,6 +728,8 @@ fail_pipe:
if (prepare_cmd(&argv, cmd) < 0) {
failed_errno = errno;
cmd->pid = -1;
if (!cmd->silent_exec_failure)
error_errno("cannot run %s", cmd->argv[0]);
goto end_of_spawn;
}

View File

@ -398,6 +398,13 @@ struct parse_config_parameter {
int overwrite;
};
/*
* Parse a config item from .gitmodules.
*
* This does not handle submodule-related configuration from the main
* config store (.git/config, etc). Callers are responsible for
* checking for overrides in the main config store when appropriate.
*/
static int parse_config(const char *var, const char *value, void *data)
{
struct parse_config_parameter *me = data;
@ -475,8 +482,9 @@ static int parse_config(const char *var, const char *value, void *data)
warn_multiple_config(me->treeish_name, submodule->name,
"update");
else if (parse_submodule_update_strategy(value,
&submodule->update_strategy) < 0)
die(_("invalid value for %s"), var);
&submodule->update_strategy) < 0 ||
submodule->update_strategy.type == SM_UPDATE_COMMAND)
die(_("invalid value for %s"), var);
} else if (!strcmp(item.buf, "shallow")) {
if (!me->overwrite && submodule->recommend_shallow != -1)
warn_multiple_config(me->treeish_name, submodule->name,

View File

@ -1732,6 +1732,47 @@ out:
return ret;
}
int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
{
size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
char *p;
int ret = 0;
if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
strcmp(p, submodule_name))
BUG("submodule name '%s' not a suffix of git dir '%s'",
submodule_name, git_dir);
/*
* We prevent the contents of sibling submodules' git directories to
* clash.
*
* Example: having a submodule named `hippo` and another one named
* `hippo/hooks` would result in the git directories
* `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
* but the latter directory is already designated to contain the hooks
* of the former.
*/
for (; *p; p++) {
if (is_dir_sep(*p)) {
char c = *p;
*p = '\0';
if (is_git_directory(git_dir))
ret = -1;
*p = c;
if (ret < 0)
return error(_("submodule git dir '%s' is "
"inside git dir '%.*s'"),
git_dir,
(int)(p - git_dir), git_dir);
}
}
return 0;
}
/*
* Embeds a single submodules git directory into the superprojects git dir,
* non recursively.
@ -1740,7 +1781,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
const char *path)
{
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
const char *new_git_dir;
char *new_git_dir;
const struct submodule *sub;
if (submodule_uses_worktrees(path))
@ -1758,10 +1799,14 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
new_git_dir = git_path("modules/%s", sub->name);
new_git_dir = git_pathdup("modules/%s", sub->name);
if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
die(_("refusing to move '%s' into an existing git dir"),
real_old_git_dir);
if (safe_create_leading_directories_const(new_git_dir) < 0)
die(_("could not create directory '%s'"), new_git_dir);
real_new_git_dir = real_pathdup(new_git_dir, 1);
free(new_git_dir);
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
get_super_prefix_or_empty(), path,

View File

@ -124,6 +124,11 @@ int push_unpushed_submodules(struct repository *r,
*/
int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
/*
* Make sure that no submodule's git dir is nested in a sibling submodule's.
*/
int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
int submodule_move_head(const char *path,

1
t/.gitattributes vendored
View File

@ -16,6 +16,7 @@ t[0-9][0-9][0-9][0-9]/* -whitespace
/t4135/* eol=lf
/t4211/* eol=lf
/t4252/* eol=lf
/t4256/1/* eol=lf
/t5100/* eol=lf
/t5515/* eol=lf
/t556x_common eol=lf

View File

@ -8,18 +8,21 @@ static int cmd_sync(void)
{
char Buffer[MAX_PATH];
DWORD dwRet;
char szVolumeAccessPath[] = "\\\\.\\X:";
char szVolumeAccessPath[] = "\\\\.\\XXXX:";
HANDLE hVolWrite;
int success = 0;
int success = 0, dos_drive_prefix;
dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
if ((0 == dwRet) || (dwRet > MAX_PATH))
return error("Error getting current directory");
if (!has_dos_drive_prefix(Buffer))
dos_drive_prefix = has_dos_drive_prefix(Buffer);
if (!dos_drive_prefix)
return error("'%s': invalid drive letter", Buffer);
szVolumeAccessPath[4] = Buffer[0];
memcpy(szVolumeAccessPath, Buffer, dos_drive_prefix);
szVolumeAccessPath[dos_drive_prefix] = '\0';
hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == hVolWrite)

View File

@ -177,6 +177,99 @@ static int is_dotgitmodules(const char *path)
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
}
/*
* A very simple, reproducible pseudo-random generator. Copied from
* `test-genrandom.c`.
*/
static uint64_t my_random_value = 1234;
static uint64_t my_random(void)
{
my_random_value = my_random_value * 1103515245 + 12345;
return my_random_value;
}
/*
* A fast approximation of the square root, without requiring math.h.
*
* It uses Newton's method to approximate the solution of 0 = x^2 - value.
*/
static double my_sqrt(double value)
{
const double epsilon = 1e-6;
double x = value;
if (value == 0)
return 0;
for (;;) {
double delta = (value / x - x) / 2;
if (delta < epsilon && delta > -epsilon)
return x + delta;
x += delta;
}
}
static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
{
size_t i, j, nr, min_len = 3, max_len = 20;
char **names;
int repetitions = 15, file_mode = 0100644;
uint64_t begin, end;
double m[3][2], v[3][2];
uint64_t cumul;
double cumul2;
if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) {
file_mode = 0120000;
argc--;
argv++;
}
nr = argc > 1 ? strtoul(argv[1], NULL, 0) : 1000000;
ALLOC_ARRAY(names, nr);
if (argc > 2) {
min_len = strtoul(argv[2], NULL, 0);
if (argc > 3)
max_len = strtoul(argv[3], NULL, 0);
if (min_len > max_len)
die("min_len > max_len");
}
for (i = 0; i < nr; i++) {
size_t len = min_len + (my_random() % (max_len + 1 - min_len));
names[i] = xmallocz(len);
while (len > 0)
names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' ')));
}
for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) {
cumul = 0;
cumul2 = 0;
for (i = 0; i < repetitions; i++) {
begin = getnanotime();
for (j = 0; j < nr; j++)
verify_path(names[j], file_mode);
end = getnanotime();
printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6);
cumul += end - begin;
cumul2 += (end - begin) * (end - begin);
}
m[protect_ntfs][protect_hfs] = cumul / (double)repetitions;
v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]);
printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6);
}
for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
for (protect_hfs = 0; protect_hfs < 2; protect_hfs++)
printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]);
return 0;
}
int cmd__path_utils(int argc, const char **argv)
{
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@ -291,6 +384,26 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
if (argc > 1 && !strcmp(argv[1], "protect_ntfs_hfs"))
return !!protect_ntfs_hfs_benchmark(argc - 1, argv + 1);
if (argc > 1 && !strcmp(argv[1], "is_valid_path")) {
int res = 0, expect = 1, i;
for (i = 2; i < argc; i++)
if (!strcmp("--not", argv[i]))
expect = 0;
else if (expect != is_valid_path(argv[i]))
res = error("'%s' is%s a valid path",
argv[i], expect ? " not" : "");
else
fprintf(stderr,
"'%s' is%s a valid path\n",
argv[i], expect ? "" : " not");
return !!res;
}
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;

View File

@ -13,8 +13,8 @@
#include "run-command.h"
#include "argv-array.h"
#include "strbuf.h"
#include <string.h>
#include <errno.h>
#include "gettext.h"
#include "parse-options.h"
static int number_callbacks;
static int parallel_next(struct child_process *cp,
@ -50,11 +50,145 @@ static int task_finished(int result,
return 1;
}
static uint64_t my_random_next = 1234;
static uint64_t my_random(void)
{
uint64_t res = my_random_next;
my_random_next = my_random_next * 1103515245 + 12345;
return res;
}
static int quote_stress_test(int argc, const char **argv)
{
/*
* We are running a quote-stress test.
* spawn a subprocess that runs quote-stress with a
* special option that echoes back the arguments that
* were passed in.
*/
char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
int i, j, k, trials = 100, skip = 0, msys2 = 0;
struct strbuf out = STRBUF_INIT;
struct argv_array args = ARGV_ARRAY_INIT;
struct option options[] = {
OPT_INTEGER('n', "trials", &trials, "Number of trials"),
OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
OPT_END()
};
const char * const usage[] = {
"test-tool run-command quote-stress-test <options>",
NULL
};
argc = parse_options(argc, argv, NULL, options, usage, 0);
setenv("MSYS_NO_PATHCONV", "1", 0);
for (i = 0; i < trials; i++) {
struct child_process cp = CHILD_PROCESS_INIT;
size_t arg_count, arg_offset;
int ret = 0;
argv_array_clear(&args);
if (msys2)
argv_array_pushl(&args, "sh", "-c",
"printf %s\\\\0 \"$@\"", "skip", NULL);
else
argv_array_pushl(&args, "test-tool", "run-command",
"quote-echo", NULL);
arg_offset = args.argc;
if (argc > 0) {
trials = 1;
arg_count = argc;
for (j = 0; j < arg_count; j++)
argv_array_push(&args, argv[j]);
} else {
arg_count = 1 + (my_random() % 5);
for (j = 0; j < arg_count; j++) {
char buf[20];
size_t min_len = 1;
size_t arg_len = min_len +
(my_random() % (ARRAY_SIZE(buf) - min_len));
for (k = 0; k < arg_len; k++)
buf[k] = special[my_random() %
ARRAY_SIZE(special)];
buf[arg_len] = '\0';
argv_array_push(&args, buf);
}
}
if (i < skip)
continue;
cp.argv = args.argv;
strbuf_reset(&out);
if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
return error("Failed to spawn child process");
for (j = 0, k = 0; j < arg_count; j++) {
const char *arg = args.argv[j + arg_offset];
if (strcmp(arg, out.buf + k))
ret = error("incorrectly quoted arg: '%s', "
"echoed back as '%s'",
arg, out.buf + k);
k += strlen(out.buf + k) + 1;
}
if (k != out.len)
ret = error("got %d bytes, but consumed only %d",
(int)out.len, (int)k);
if (ret) {
fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
for (j = 0; j < arg_count; j++)
fprintf(stderr, "arg #%d: '%s'\n",
(int)j, args.argv[j + arg_offset]);
strbuf_release(&out);
argv_array_clear(&args);
return ret;
}
if (i && (i % 100) == 0)
fprintf(stderr, "Trials completed: %d\n", (int)i);
}
strbuf_release(&out);
argv_array_clear(&args);
return 0;
}
static int quote_echo(int argc, const char **argv)
{
while (argc > 1) {
fwrite(argv[1], strlen(argv[1]), 1, stdout);
fputc('\0', stdout);
argv++;
argc--;
}
return 0;
}
int cmd__run_command(int argc, const char **argv)
{
struct child_process proc = CHILD_PROCESS_INIT;
int jobs;
if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
return !!quote_stress_test(argc - 1, argv + 1);
if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
return !!quote_echo(argc - 1, argv + 1);
if (argc < 3)
return 1;
while (!strcmp(argv[1], "env")) {

View File

@ -165,6 +165,15 @@ test_expect_success 'absolute path rejects the empty string' '
test_must_fail test-tool path-utils absolute_path ""
'
test_expect_success MINGW '<drive-letter>:\\abc is an absolute path' '
for letter in : \" C Z 1 ä
do
path=$letter:\\abc &&
absolute="$(test-tool path-utils absolute_path "$path")" &&
test "$path" = "$absolute" || return 1
done
'
test_expect_success 'real path rejects the empty string' '
test_must_fail test-tool path-utils real_path ""
'
@ -423,6 +432,9 @@ test_expect_success 'match .gitmodules' '
~1000000 \
~9999999 \
\
.gitmodules:\$DATA \
"gitmod~4 . :\$DATA" \
\
--not \
".gitmodules x" \
".gitmodules .x" \
@ -447,7 +459,25 @@ test_expect_success 'match .gitmodules' '
\
GI7EB~1 \
GI7EB~01 \
GI7EB~1X
GI7EB~1X \
\
.gitmodules,:\$DATA
'
test_expect_success MINGW 'is_valid_path() on Windows' '
test-tool path-utils is_valid_path \
win32 \
"win32 x" \
../hello.txt \
C:\\git \
\
--not \
"win32 " \
"win32 /x " \
"win32." \
"win32 . ." \
.../hello.txt \
colon:test
'
test_done

View File

@ -13,11 +13,13 @@ cat >hello-script <<-EOF
EOF
test_expect_success 'start_command reports ENOENT (slash)' '
test-tool run-command start-command-ENOENT ./does-not-exist
test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
test_i18ngrep "\./does-not-exist" err
'
test_expect_success 'start_command reports ENOENT (no slash)' '
test-tool run-command start-command-ENOENT does-not-exist
test-tool run-command start-command-ENOENT does-not-exist 2>err &&
test_i18ngrep "does-not-exist" err
'
test_expect_success 'run_command can run a command' '
@ -33,7 +35,8 @@ test_expect_success 'run_command is restricted to PATH' '
write_script should-not-run <<-\EOF &&
echo yikes
EOF
test_must_fail test-tool run-command run-command should-not-run
test_must_fail test-tool run-command run-command should-not-run 2>err &&
test_i18ngrep "should-not-run" err
'
test_expect_success !MINGW 'run_command can run a script without a #! line' '

View File

@ -49,6 +49,7 @@ git~1
.git.SPACE .git.{space}
.\\\\.GIT\\\\foobar backslashes
.git\\\\foobar backslashes2
.git...:alternate-stream
EOF
test_expect_success 'utf-8 paths allowed with core.protectHFS off' '

View File

@ -453,6 +453,7 @@ while read name path pretty; do
(
git init $name-$type &&
cd $name-$type &&
git config core.protectNTFS false &&
echo content >file &&
git add file &&
git commit -m base &&

View File

@ -248,18 +248,24 @@ test_expect_success 'dual-coloring' '
for prev in topic master..topic
do
test_expect_success "format-patch --range-diff=$prev" '
git format-patch --stdout --cover-letter --range-diff=$prev \
git format-patch --cover-letter --range-diff=$prev \
master..unmodified >actual &&
grep "= 1: .* s/5/A" actual &&
grep "= 2: .* s/4/A" actual &&
grep "= 3: .* s/11/B" actual &&
grep "= 4: .* s/12/B" actual
test_when_finished "rm 000?-*" &&
test_line_count = 5 actual &&
test_i18ngrep "^Range-diff:$" 0000-* &&
grep "= 1: .* s/5/A" 0000-* &&
grep "= 2: .* s/4/A" 0000-* &&
grep "= 3: .* s/11/B" 0000-* &&
grep "= 4: .* s/12/B" 0000-*
'
done
test_expect_success 'format-patch --range-diff as commentary' '
git format-patch --stdout --range-diff=HEAD~1 HEAD~1 >actual &&
test_i18ngrep "^Range-diff:$" actual
git format-patch --range-diff=HEAD~1 HEAD~1 >actual &&
test_when_finished "rm 0001-*" &&
test_line_count = 1 actual &&
test_i18ngrep "^Range-diff:$" 0001-* &&
grep "> 1: .* new message" 0001-*
'
test_done

View File

@ -10,6 +10,7 @@ test_expect_success 'create commits with glob characters' '
# the name "f*" in the worktree, because it is not allowed
# on Windows (the tests below do not depend on the presence
# of the file in the worktree)
git config core.protectNTFS false &&
git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" &&
test_tick &&
git commit -m star &&

View File

@ -407,12 +407,26 @@ test_expect_success 'submodule update - command in .git/config' '
)
'
test_expect_success 'submodule update - command in .gitmodules is ignored' '
test_expect_success 'submodule update - command in .gitmodules is rejected' '
test_when_finished "git -C super reset --hard HEAD^" &&
git -C super config -f .gitmodules submodule.submodule.update "!false" &&
git -C super commit -a -m "add command to .gitmodules file" &&
git -C super/submodule reset --hard $submodulesha1^ &&
git -C super submodule update submodule
test_must_fail git -C super submodule update submodule
'
test_expect_success 'fsck detects command in .gitmodules' '
git init command-in-gitmodules &&
(
cd command-in-gitmodules &&
git submodule add ../submodule submodule &&
test_commit adding-submodule &&
git config -f .gitmodules submodule.submodule.update "!false" &&
git add .gitmodules &&
test_commit configuring-update &&
test_must_fail git fsck
)
'
cat << EOF >expect
@ -481,6 +495,9 @@ test_expect_success 'recursive submodule update - command in .git/config catches
'
test_expect_success 'submodule init does not copy command into .git/config' '
test_when_finished "git -C super update-index --force-remove submodule1" &&
test_when_finished git config -f super/.gitmodules \
--remove-section submodule.submodule1 &&
(cd super &&
git ls-files -s submodule >out &&
H=$(cut -d" " -f2 out) &&
@ -489,10 +506,9 @@ test_expect_success 'submodule init does not copy command into .git/config' '
git config -f .gitmodules submodule.submodule1.path submodule1 &&
git config -f .gitmodules submodule.submodule1.url ../submodule &&
git config -f .gitmodules submodule.submodule1.update !false &&
git submodule init submodule1 &&
echo "none" >expect &&
git config submodule.submodule1.update >actual &&
test_cmp expect actual
test_must_fail git submodule init submodule1 &&
test_expect_code 1 git config submodule.submodule1.update >actual &&
test_must_be_empty actual
)
'

View File

@ -191,4 +191,60 @@ test_expect_success 'fsck detects corrupt .gitmodules' '
)
'
test_expect_success MINGW 'prevent git~1 squatting on Windows' '
git init squatting &&
(
cd squatting &&
mkdir a &&
touch a/..git &&
git add a/..git &&
test_tick &&
git commit -m initial &&
modules="$(test_write_lines \
"[submodule \"b.\"]" "url = ." "path = c" \
"[submodule \"b\"]" "url = ." "path = d\\\\a" |
git hash-object -w --stdin)" &&
rev="$(git rev-parse --verify HEAD)" &&
hash="$(echo x | git hash-object -w --stdin)" &&
git -c core.protectNTFS=false update-index --add \
--cacheinfo 100644,$modules,.gitmodules \
--cacheinfo 160000,$rev,c \
--cacheinfo 160000,$rev,d\\a \
--cacheinfo 100644,$hash,d./a/x \
--cacheinfo 100644,$hash,d./a/..git &&
test_tick &&
git -c core.protectNTFS=false commit -m "module" &&
test_must_fail git show HEAD: 2>err &&
test_i18ngrep backslash err
) &&
test_must_fail git -c core.protectNTFS=false \
clone --recurse-submodules squatting squatting-clone 2>err &&
test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
! grep gitdir squatting-clone/d/a/git~2
'
test_expect_success 'git dirs of sibling submodules must not be nested' '
git init nested &&
test_commit -C nested nested &&
(
cd nested &&
cat >.gitmodules <<-EOF &&
[submodule "hippo"]
url = .
path = thing1
[submodule "hippo/hooks"]
url = .
path = thing2
EOF
git clone . thing1 &&
git clone . thing2 &&
git add .gitmodules thing1 thing2 &&
test_tick &&
git commit -m nested
) &&
test_must_fail git clone --recurse-submodules nested clone 2>err &&
test_i18ngrep -E "(is inside git dir|hippo already exists|not a git repository: .*/hippo)" err
'
test_done

View File

@ -46,4 +46,18 @@ test_expect_success 'fsck rejects unprotected dash' '
grep gitmodulesUrl err
'
test_expect_success 'trailing backslash is handled correctly' '
git init testmodule &&
test_commit -C testmodule c &&
git submodule add ./testmodule &&
: ensure that the name ends in a double backslash &&
sed -e "s|\\(submodule \"testmodule\\)\"|\\1\\\\\\\\\"|" \
-e "s|url = .*|url = \" --should-not-be-an-option\"|" \
<.gitmodules >.new &&
mv .new .gitmodules &&
git commit -am "Add testmodule" &&
test_must_fail git clone --verbose --recurse-submodules . dolly 2>err &&
test_i18ngrep ! "unknown option" err
'
test_done

View File

@ -25,4 +25,21 @@ test_expect_success 'fsck rejects unprotected dash' '
grep gitmodulesPath err
'
test_expect_success MINGW 'submodule paths disallows trailing spaces' '
git init super &&
test_must_fail git -C super submodule add ../upstream "sub " &&
: add "sub", then rename "sub" to "sub ", the hard way &&
git -C super submodule add ../upstream sub &&
tree=$(git -C super write-tree) &&
git -C super ls-tree $tree >tree &&
sed "s/sub/sub /" <tree >tree.new &&
tree=$(git -C super mktree <tree.new) &&
commit=$(echo with space | git -C super commit-tree $tree) &&
git -C super update-ref refs/heads/master $commit &&
test_must_fail git clone --recurse-submodules super dst 2>err &&
test_i18ngrep "sub " err
'
test_done

View File

@ -2106,12 +2106,27 @@ test_expect_success 'R: abort on receiving feature after data command' '
test_must_fail git fast-import <input
'
test_expect_success 'R: import-marks features forbidden by default' '
>git.marks &&
echo "feature import-marks=git.marks" >input &&
test_must_fail git fast-import <input &&
echo "feature import-marks-if-exists=git.marks" >input &&
test_must_fail git fast-import <input
'
test_expect_success 'R: only one import-marks feature allowed per stream' '
>git.marks &&
>git2.marks &&
cat >input <<-EOF &&
feature import-marks=git.marks
feature import-marks=git2.marks
EOF
test_must_fail git fast-import --allow-unsafe-features <input
'
test_expect_success 'R: export-marks feature forbidden by default' '
echo "feature export-marks=git.marks" >input &&
test_must_fail git fast-import <input
'
@ -2125,19 +2140,29 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
EOF
cat input | git fast-import &&
git fast-import --allow-unsafe-features <input &&
grep :1 git.marks
'
test_expect_success 'R: export-marks options can be overridden by commandline options' '
cat input | git fast-import --export-marks=other.marks &&
grep :1 other.marks
cat >input <<-\EOF &&
feature export-marks=feature-sub/git.marks
blob
mark :1
data 3
hi
EOF
git fast-import --allow-unsafe-features \
--export-marks=cmdline-sub/other.marks <input &&
grep :1 cmdline-sub/other.marks &&
test_path_is_missing feature-sub
'
test_expect_success 'R: catch typo in marks file name' '
test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
echo "feature import-marks=nonexistent.marks" |
test_must_fail git fast-import
test_must_fail git fast-import --allow-unsafe-features
'
test_expect_success 'R: import and output marks can be the same file' '
@ -2192,7 +2217,8 @@ test_expect_success 'R: --import-marks-if-exists' '
test_expect_success 'R: feature import-marks-if-exists' '
rm -f io.marks &&
git fast-import --export-marks=io.marks <<-\EOF &&
git fast-import --export-marks=io.marks \
--allow-unsafe-features <<-\EOF &&
feature import-marks-if-exists=not_io.marks
EOF
test_must_be_empty io.marks &&
@ -2203,7 +2229,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
echo ":1 $blob" >expect &&
echo ":2 $blob" >>expect &&
git fast-import --export-marks=io.marks <<-\EOF &&
git fast-import --export-marks=io.marks \
--allow-unsafe-features <<-\EOF &&
feature import-marks-if-exists=io.marks
blob
mark :2
@ -2216,7 +2243,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
echo ":3 $blob" >>expect &&
git fast-import --import-marks=io.marks \
--export-marks=io.marks <<-\EOF &&
--export-marks=io.marks \
--allow-unsafe-features <<-\EOF &&
feature import-marks-if-exists=not_io.marks
blob
mark :3
@ -2227,7 +2255,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
test_cmp expect io.marks &&
git fast-import --import-marks-if-exists=not_io.marks \
--export-marks=io.marks <<-\EOF &&
--export-marks=io.marks \
--allow-unsafe-features <<-\EOF &&
feature import-marks-if-exists=io.marks
EOF
test_must_be_empty io.marks
@ -2239,7 +2268,7 @@ test_expect_success 'R: import to output marks works without any content' '
feature export-marks=marks.new
EOF
cat input | git fast-import &&
git fast-import --allow-unsafe-features <input &&
test_cmp marks.out marks.new
'
@ -2249,7 +2278,7 @@ test_expect_success 'R: import marks prefers commandline marks file over the str
feature export-marks=marks.new
EOF
cat input | git fast-import --import-marks=marks.out &&
git fast-import --import-marks=marks.out --allow-unsafe-features <input &&
test_cmp marks.out marks.new
'
@ -2262,7 +2291,8 @@ test_expect_success 'R: multiple --import-marks= should be honoured' '
head -n2 marks.out > one.marks &&
tail -n +3 marks.out > two.marks &&
git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
git fast-import --import-marks=one.marks --import-marks=two.marks \
--allow-unsafe-features <input &&
test_cmp marks.out combined.marks
'
@ -2275,7 +2305,7 @@ test_expect_success 'R: feature relative-marks should be honoured' '
mkdir -p .git/info/fast-import/ &&
cp marks.new .git/info/fast-import/relative.in &&
git fast-import <input &&
git fast-import --allow-unsafe-features <input &&
test_cmp marks.new .git/info/fast-import/relative.out
'
@ -2287,7 +2317,7 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
feature export-marks=non-relative.out
EOF
git fast-import <input &&
git fast-import --allow-unsafe-features <input &&
test_cmp marks.new non-relative.out
'
@ -2557,7 +2587,7 @@ test_expect_success 'R: quiet option results in no stats being output' '
EOF
cat input | git fast-import 2> output &&
git fast-import 2>output <input &&
test_must_be_empty output
'

View File

@ -418,9 +418,10 @@ test_expect_success 'directory becomes symlink' '
test_expect_success 'fast-export quotes pathnames' '
git init crazy-paths &&
test_config -C crazy-paths core.protectNTFS false &&
(cd crazy-paths &&
blob=$(echo foo | git hash-object -w --stdin) &&
git update-index --add \
git -c core.protectNTFS=false update-index --add \
--cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
--cacheinfo 100644 $blob "path with \"quote\"" \
--cacheinfo 100644 $blob "path with \\backslash" \

View File

@ -1539,7 +1539,7 @@ test_expect_success 'complete tree filename with metacharacters' '
EOF
'
test_expect_success 'send-email' '
test_expect_success PERL 'send-email' '
test_completion "git send-email --cov" "--cover-letter " &&
test_completion "git send-email ma" "master "
'

View File

@ -423,6 +423,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti
child_process_init(fastimport);
fastimport->in = helper->out;
argv_array_push(&fastimport->args, "fast-import");
argv_array_push(&fastimport->args, "--allow-unsafe-features");
argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
if (data->bidi_import) {

View File

@ -43,6 +43,12 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
strbuf_addstr(err, _("empty filename in tree entry"));
return -1;
}
#ifdef GIT_WINDOWS_NATIVE
if (protect_ntfs && strchr(path, '\\')) {
strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
return -1;
}
#endif
len = strlen(path) + 1;
/* Initialize the descriptor entry */

View File

@ -2073,7 +2073,8 @@ static int merged_entry(const struct cache_entry *ce,
invalidate_ce_path(old, o);
}
do_add_entry(o, merge, update, CE_STAGEMASK);
if (do_add_entry(o, merge, update, CE_STAGEMASK) < 0)
return -1;
return 1;
}