Skip to content

Commit 8e44997

Browse files
committed
Merge branch 'master' into stable-7.5
* master: [ignore patterns] Backslash is a general escape Do not always refresh packed-refs during ref updates Change-Id: If0293dd52bb72670adf08b5454388c7ccf7352fe
2 parents ffebbe7 + 34f169f commit 8e44997

File tree

10 files changed

+231
-180
lines changed

10 files changed

+231
-180
lines changed

Documentation/config-options.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ For details on native git options see also the official [git config documentatio
5757
| `core.symlinks` | Auto detect if filesystem supports symlinks| ✅ | If false, symbolic links are checked out as small plain files that contain the link text. |
5858
| ~~`core.trustFolderStat`~~ | `true` | ⃞ | __Deprecated__, use `core.trustStat` instead. If set to `true` translated to `core.trustStat=always`, if `false` translated to `core.trustStat=never`, see below. If both `core.trustFolderStat` and `core.trustStat` are configured then `trustStat` takes precedence and `trustFolderStat` is ignored. |
5959
| `core.trustLooseRefStat` | `inherit` | ⃞ | Whether to trust the file attributes of loose refs and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `trustStat`. |
60-
| `core.trustPackedRefsStat` | `inherit` | ⃞ | Whether to trust the file attributes of the packed-refs file. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
60+
| `core.trustPackedRefsStat` | `inherit` | ⃞ | Whether to trust the file attributes of the packed-refs file. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. __Note:__ since 6.10.2, this setting applies during both ref reads and ref updates, but previously only applied during reads.|
6161
| `core.trustTablesListStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `tables.list` file used by the reftable ref storage backend to store the list of reftable filenames. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. The reftable backend is used if `extensions.refStorage = reftable`. |
6262
| `core.trustLooseObjectStat` | `inherit` | ⃞ | Whether to trust the file attributes of the loose object file and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
6363
| `core.trustPackStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `objects/pack` directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |

org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/BasicRuleTest.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> and others
2+
* Copyright (C) 2014, 2025 Andrey Loskutov <loskutov@gmx.de> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -58,14 +58,4 @@ public void testStripTrailingChar() {
5858
assertEquals("a", Strings.stripTrailing("a///", '/'));
5959
assertEquals("a/ ", Strings.stripTrailing("a/ ", '/'));
6060
}
61-
62-
@Test
63-
public void testStripTrailingWhitespace() {
64-
assertEquals("", Strings.stripTrailingWhitespace(""));
65-
assertEquals("", Strings.stripTrailingWhitespace(" "));
66-
assertEquals("a", Strings.stripTrailingWhitespace("a"));
67-
assertEquals("a", Strings.stripTrailingWhitespace("a "));
68-
assertEquals("a", Strings.stripTrailingWhitespace("a "));
69-
assertEquals("a", Strings.stripTrailingWhitespace("a \t"));
70-
}
7161
}

org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> and others
2+
* Copyright (C) 2017, 2025 Thomas Wolf <twolf@apache.org> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -15,14 +15,14 @@
1515
import static org.junit.Assert.assertFalse;
1616
import static org.junit.Assert.assertTrue;
1717

18-
import java.io.BufferedInputStream;
19-
import java.io.BufferedReader;
2018
import java.io.ByteArrayInputStream;
2119
import java.io.File;
2220
import java.io.IOException;
23-
import java.io.InputStreamReader;
21+
import java.io.InputStream;
2422
import java.nio.file.Files;
23+
import java.util.ArrayList;
2524
import java.util.LinkedHashSet;
25+
import java.util.List;
2626
import java.util.Set;
2727

2828
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -72,36 +72,48 @@ private String toString(TemporaryBuffer b) throws IOException {
7272
private String[] cgitIgnored() throws Exception {
7373
FS fs = db.getFS();
7474
ProcessBuilder builder = fs.runInShell("git", new String[] { "ls-files",
75-
"--ignored", "--exclude-standard", "-o" });
75+
"--ignored", "--exclude-standard", "-o", "-z" });
7676
builder.directory(db.getWorkTree());
7777
builder.environment().put("HOME", fs.userHome().getAbsolutePath());
7878
ExecutionResult result = fs.execute(builder,
7979
new ByteArrayInputStream(new byte[0]));
8080
String errorOut = toString(result.getStderr());
8181
assertEquals("External git failed", "exit 0\n",
8282
"exit " + result.getRc() + '\n' + errorOut);
83-
try (BufferedReader r = new BufferedReader(new InputStreamReader(
84-
new BufferedInputStream(result.getStdout().openInputStream()),
85-
UTF_8))) {
86-
return r.lines().toArray(String[]::new);
87-
}
83+
return readLines(result.getStdout());
8884
}
8985

9086
private String[] cgitUntracked() throws Exception {
9187
FS fs = db.getFS();
9288
ProcessBuilder builder = fs.runInShell("git",
93-
new String[] { "ls-files", "--exclude-standard", "-o" });
89+
new String[] { "ls-files", "--exclude-standard", "-o", "-z" });
9490
builder.directory(db.getWorkTree());
9591
builder.environment().put("HOME", fs.userHome().getAbsolutePath());
9692
ExecutionResult result = fs.execute(builder,
9793
new ByteArrayInputStream(new byte[0]));
9894
String errorOut = toString(result.getStderr());
9995
assertEquals("External git failed", "exit 0\n",
10096
"exit " + result.getRc() + '\n' + errorOut);
101-
try (BufferedReader r = new BufferedReader(new InputStreamReader(
102-
new BufferedInputStream(result.getStdout().openInputStream()),
103-
UTF_8))) {
104-
return r.lines().toArray(String[]::new);
97+
return readLines(result.getStdout());
98+
}
99+
100+
private String[] readLines(TemporaryBuffer buf) throws IOException {
101+
try (InputStream in = buf.openInputStreamWithAutoDestroy()) {
102+
byte[] data = in.readAllBytes();
103+
int from = 0;
104+
int to = 0;
105+
List<String> items = new ArrayList<>();
106+
while (to < data.length) {
107+
if (data[to++] == 0) {
108+
items.add(new String(data, from, to - from - 1, UTF_8));
109+
from = to;
110+
}
111+
}
112+
if (from < data.length) {
113+
// Last item not terminated by NUL
114+
items.add(new String(data, from, to - from, UTF_8));
115+
}
116+
return items.toArray(new String[0]);
105117
}
106118
}
107119

@@ -119,7 +131,7 @@ private void jgitIgnoredAndUntracked(LinkedHashSet<String> ignored,
119131
ignored.add(walk.getPathString());
120132
} else {
121133
// tests of this class won't add any files to the index,
122-
// hence everything what is not ignored is untracked
134+
// hence everything not ignored is untracked
123135
untracked.add(walk.getPathString());
124136
}
125137
}
@@ -301,6 +313,48 @@ public void testSimpleRootGitIgnoreGlobalNegation1() throws Exception {
301313
assertSameAsCGit();
302314
}
303315

316+
@Test
317+
public void testSingleBackslash() throws Exception {
318+
createFiles("foo\\", "foo/foo\\", "bar");
319+
writeTrashFile(".gitignore", "foo\\");
320+
assertSameAsCGit("foo\\", "foo/foo\\", "bar");
321+
}
322+
323+
@Test
324+
public void testDoubleBackslash() throws Exception {
325+
createFiles("foo\\", "foo/foo\\", "bar");
326+
writeTrashFile(".gitignore", "foo\\\\");
327+
assertSameAsCGit("bar");
328+
}
329+
330+
@Test
331+
public void testSingleBackslashRegexp() throws Exception {
332+
createFiles("foobar\\", "foo/foobar\\", "bar");
333+
writeTrashFile(".gitignore", "fo*r\\");
334+
assertSameAsCGit("foobar\\", "foo/foobar\\", "bar");
335+
}
336+
337+
@Test
338+
public void testDoubleBackslashRegexp() throws Exception {
339+
createFiles("foobar\\", "foo/foobar\\", "bar");
340+
writeTrashFile(".gitignore", "fo*r\\\\");
341+
assertSameAsCGit("bar");
342+
}
343+
344+
@Test
345+
public void testSingleBackslashRegexpInside() throws Exception {
346+
createFiles("fo\\obar", "foo/fo\\obar", "bar", "foobar");
347+
writeTrashFile(".gitignore", "fo\\o*r");
348+
assertSameAsCGit("fo\\obar", "foo/fo\\obar", "bar");
349+
}
350+
351+
@Test
352+
public void testDoubleBackslashRegexpInside() throws Exception {
353+
createFiles("fo\\obar", "foo/fo\\obar", "bar", "foobar");
354+
writeTrashFile(".gitignore", "fo\\\\o*r");
355+
assertSameAsCGit("bar", "foobar");
356+
}
357+
304358
@Test
305359
public void testRepeatedNegationInDifferentFiles5() throws Exception {
306360
// see IgnoreNodeTest.testRepeatedNegationInDifferentFiles5

org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2008, Florian Köberle <florianskarten@web.de> and others
2+
* Copyright (C) 2008, 2025 Florian Köberle <florianskarten@web.de> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -10,25 +10,13 @@
1010
package org.eclipse.jgit.ignore;
1111

1212
import static org.junit.Assert.assertEquals;
13-
import static org.junit.Assume.assumeTrue;
1413

1514
import org.junit.Test;
1615

1716
@SuppressWarnings({ "boxing" })
1817
public class IgnoreRuleSpecialCasesTest {
1918

2019
private void assertMatch(final String pattern, final String input,
21-
final boolean matchExpected, Boolean... assume) {
22-
boolean assumeDir = input.endsWith("/");
23-
FastIgnoreRule matcher = new FastIgnoreRule(pattern);
24-
if (assume.length == 0 || !assume[0].booleanValue()) {
25-
assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
26-
} else {
27-
assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
28-
}
29-
}
30-
31-
private void assertFileNameMatch(final String pattern, final String input,
3220
final boolean matchExpected) {
3321
boolean assumeDir = input.endsWith("/");
3422
FastIgnoreRule matcher = new FastIgnoreRule(pattern);
@@ -739,7 +727,7 @@ public void testSpecialGroupCase9() throws Exception {
739727

740728
@Test
741729
public void testSpecialGroupCase10() throws Exception {
742-
// Second bracket is threated literally, so both [ and : should match
730+
// Second bracket is treated literally, so both [ and : should match
743731
assertMatch("[[:]", ":", true);
744732
assertMatch("[[:]", "[", true);
745733
}
@@ -835,6 +823,8 @@ public void testNotEscapingBackslash() throws Exception {
835823
assertMatch("c:\\/", "a/c:/", true);
836824
assertMatch("c:\\tmp", "c:tmp", true);
837825
assertMatch("c:\\tmp", "a/c:tmp", true);
826+
assertMatch("foo\\/bar", "foo/bar", true);
827+
assertMatch("foo\\/bar\\/", "foo/bar/", true);
838828
}
839829

840830
@Test
@@ -844,20 +834,48 @@ public void testMultipleEscapedCharacters1() throws Exception {
844834

845835
@Test
846836
public void testBackslash() throws Exception {
847-
assertMatch("a\\", "a", true);
837+
assertMatch("a\\", "a", false);
848838
assertMatch("\\a", "a", true);
849-
assertMatch("a/\\", "a/", true);
850-
assertMatch("a/b\\", "a/b", true);
839+
assertMatch("a/\\", "a/", false);
840+
assertMatch("a/b\\", "a/b", false);
851841
assertMatch("\\a/b", "a/b", true);
852842
assertMatch("/\\a", "/a", true);
853-
assertMatch("\\a\\b\\c\\", "abc", true);
854-
assertMatch("/\\a/\\b/\\c\\", "a/b/c", true);
843+
assertMatch("\\a\\b\\c\\", "abc", false);
844+
assertMatch("\\a\\b\\c", "abc", true);
845+
assertMatch("/\\a/\\b/\\c\\", "a/b/c", false);
846+
assertMatch("/\\a/\\b/\\c", "a/b/c", true);
847+
assertMatch("/\\a\\/\\b\\/\\c", "a/b/c", true);
848+
849+
assertMatch("foo\\", "foo", false);
850+
assertMatch("foo\\", "foo\\", false);
851+
assertMatch("foo\\\\", "foo\\", true);
855852

856853
// empty path segment doesn't match
857854
assertMatch("\\/a", "/a", false);
858855
assertMatch("\\/a", "a", false);
859856
}
860857

858+
@Test
859+
public void testBackslashInRegexp() throws Exception {
860+
assertMatch("f*[\\x]y", "f\\y", false);
861+
assertMatch("f*[\\x]y", "fxy", true);
862+
assertMatch("f*[\\\\x]y", "f\\y", true);
863+
assertMatch("f*[\\\\x]y", "fxy", true);
864+
assertMatch("f*[\\x]y", "foo\\y", false);
865+
assertMatch("f*[\\x]y", "fooxy", true);
866+
assertMatch("f*[\\\\x]y", "foo\\y", true);
867+
assertMatch("f*[\\\\x]y", "fooxy", true);
868+
assertMatch("f*a\\y", "fa\\y", false);
869+
assertMatch("f*a\\y", "fay", true);
870+
assertMatch("f*a\\y", "fooa\\y", false);
871+
assertMatch("f*a\\y", "fooay", true);
872+
assertMatch("f*a\\\\y", "fa\\y", true);
873+
assertMatch("f*a\\\\y", "fooa\\y", true);
874+
assertMatch("f\\*y", "fooy", false);
875+
assertMatch("f\\*y", "f*y", true);
876+
assertMatch("/\\a*A\\/\\b*B\\/\\c*C", "afooA/bfooB/cC", true);
877+
}
878+
861879
@Test
862880
public void testDollar() throws Exception {
863881
assertMatch("$", "$", true);
@@ -991,27 +1009,27 @@ public void testBracketsRandom() throws Exception {
9911009

9921010
@Test
9931011
public void testFilePathSimpleCase() throws Exception {
994-
assertFileNameMatch("a/b", "a/b", true);
1012+
assertMatch("a/b", "a/b", true);
9951013
}
9961014

9971015
@Test
9981016
public void testFilePathCase0() throws Exception {
999-
assertFileNameMatch("a*b", "a/b", false);
1017+
assertMatch("a*b", "a/b", false);
10001018
}
10011019

10021020
@Test
10031021
public void testFilePathCase1() throws Exception {
1004-
assertFileNameMatch("a?b", "a/b", false);
1022+
assertMatch("a?b", "a/b", false);
10051023
}
10061024

10071025
@Test
10081026
public void testFilePathCase2() throws Exception {
1009-
assertFileNameMatch("a*b", "a\\b", true);
1027+
assertMatch("a*b", "a\\b", true);
10101028
}
10111029

10121030
@Test
10131031
public void testFilePathCase3() throws Exception {
1014-
assertFileNameMatch("a?b", "a\\b", true);
1032+
assertMatch("a?b", "a\\b", true);
10151033
}
10161034

10171035
}

org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2014, 2021 Andrey Loskutov <loskutov@gmx.de> and others
2+
* Copyright (C) 2014, 2025 Andrey Loskutov <loskutov@gmx.de> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,7 +12,6 @@
1212
import static org.eclipse.jgit.ignore.IMatcher.NO_MATCH;
1313
import static org.eclipse.jgit.ignore.internal.Strings.isDirectoryPattern;
1414
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing;
15-
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailingWhitespace;
1615

1716
import java.text.MessageFormat;
1817

@@ -72,7 +71,7 @@ void parse(String pattern) throws InvalidPatternException {
7271
if (pattern == null) {
7372
throw new IllegalArgumentException("Pattern must not be null!"); //$NON-NLS-1$
7473
}
75-
if (pattern.length() == 0) {
74+
if (pattern.isEmpty() || pattern.charAt(0) == '#') {
7675
dirOnly = false;
7776
inverse = false;
7877
this.matcher = NO_MATCH;
@@ -81,29 +80,19 @@ void parse(String pattern) throws InvalidPatternException {
8180
inverse = pattern.charAt(0) == '!';
8281
if (inverse) {
8382
pattern = pattern.substring(1);
84-
if (pattern.length() == 0) {
83+
if (pattern.isEmpty()
84+
|| (pattern.length() == 1 && pattern.charAt(0) == '\\')) {
8585
dirOnly = false;
8686
this.matcher = NO_MATCH;
8787
return;
8888
}
8989
}
90-
if (pattern.charAt(0) == '#') {
91-
this.matcher = NO_MATCH;
92-
dirOnly = false;
93-
return;
94-
}
95-
if (pattern.charAt(0) == '\\' && pattern.length() > 1) {
96-
char next = pattern.charAt(1);
97-
if (next == '!' || next == '#') {
98-
// remove backslash escaping first special characters
99-
pattern = pattern.substring(1);
100-
}
101-
}
10290
dirOnly = isDirectoryPattern(pattern);
10391
if (dirOnly) {
104-
pattern = stripTrailingWhitespace(pattern);
92+
pattern = pattern.stripTrailing();
10593
pattern = stripTrailing(pattern, PATH_SEPARATOR);
106-
if (pattern.length() == 0) {
94+
if (pattern.isEmpty()
95+
|| (pattern.length() == 1 && pattern.charAt(0) == '\\')) {
10796
this.matcher = NO_MATCH;
10897
return;
10998
}

org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> and others
2+
* Copyright (C) 2014, 2025 Andrey Loskutov <loskutov@gmx.de> and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -28,7 +28,7 @@ public class NameMatcher extends AbstractMatcher {
2828
super(pattern, dirOnly);
2929
slash = getPathSeparator(pathSeparator);
3030
if (deleteBackslash) {
31-
pattern = Strings.deleteBackslash(pattern);
31+
pattern = Strings.unescape(pattern);
3232
}
3333
beginning = pattern.length() == 0 ? false : pattern.charAt(0) == slash;
3434
if (!beginning) {

0 commit comments

Comments
 (0)