threads / patch / 19231

patch, 2 partsAdd support for boolean config values "yes", "no"

Subject: [JGIT PATCH 1/2] Add support for boolean config values "yes", "no"

## tl;dr

18 messages between May 7, 2009 and May 8, 2009. Diffs are folded; open one to read it.

replies: 17people: 7as markdown or json

Shawn O. Pearce· May 7, 2009, 15:05 UTC · lore

In 8f8c6fafd92f (shipped in 1.6.3) Linus taught C Git how to read boolean configuration values set to "yes" as true and "no" as false. Add support for these values, and some test cases.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 This little 2 patch series is to adjust some cases in JGit to
 match Git 1.6.3 semantics.
 .../org/spearce/jgit/lib/RepositoryConfigTest.java |   70 ++++++++++++++++++--
 .../src/org/spearce/jgit/lib/RepositoryConfig.java |   13 +++-
 2 files changed, 74 insertions(+), 9 deletions(-)
Show changes to 2 files +74 −9

org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java, org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
index 4b5314c..ed573e1 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
@@ -57,9 +57,7 @@
 	 * @throws IOException
 	 */
 	public void test001_ReadBareKey() throws IOException {
-		final File path = writeTrashFile("config_001", "[foo]\nbar\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
-		System.out.println(repositoryConfig.getString("foo", null, "bar"));
+		final RepositoryConfig repositoryConfig = read("[foo]\nbar\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", null, "bar", false));
 		assertEquals("", repositoryConfig.getString("foo", null, "bar"));
 	}
@@ -70,8 +68,7 @@ public void test001_ReadBareKey() throws IOException {
 	 * @throws IOException
 	 */
 	public void test002_ReadWithSubsection() throws IOException {
-		final File path = writeTrashFile("config_002", "[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
+		final RepositoryConfig repositoryConfig = read("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", "zip", "bar", false));
 		assertEquals("", repositoryConfig.getString("foo","zip", "bar"));
 		assertEquals(false, repositoryConfig.getBoolean("foo", "zap", "bar", true));
@@ -113,8 +110,7 @@ assertTrue(Arrays.equals(values.toArray(), repositoryConfig
 	}
 
 	public void test006_readCaseInsensitive() throws IOException {
-		final File path = writeTrashFile("config_001", "[Foo]\nBar\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
+		final RepositoryConfig repositoryConfig = read("[Foo]\nBar\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", null, "bar", false));
 		assertEquals("", repositoryConfig.getString("foo", null, "bar"));
 	}
@@ -182,4 +178,64 @@ public void test007_readUserInfos() throws IOException {
 		assertEquals("local username", authorName);
 		assertEquals("author@localemail", authorEmail);
 	}
+
+	public void testReadBoolean_TrueFalse1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = true\nb = false\n");
+		assertEquals("true", c.getString("s", null, "a"));
+		assertEquals("false", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_TrueFalse2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = TrUe\nb = fAlSe\n");
+		assertEquals("TrUe", c.getString("s", null, "a"));
+		assertEquals("fAlSe", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_YesNo1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = yes\nb = no\n");
+		assertEquals("yes", c.getString("s", null, "a"));
+		assertEquals("no", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_YesNo2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = yEs\nb = NO\n");
+		assertEquals("yEs", c.getString("s", null, "a"));
+		assertEquals("NO", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_OnOff1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = on\nb = off\n");
+		assertEquals("on", c.getString("s", null, "a"));
+		assertEquals("off", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_OnOff2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = ON\nb = OFF\n");
+		assertEquals("ON", c.getString("s", null, "a"));
+		assertEquals("OFF", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	private RepositoryConfig read(final String content) throws IOException {
+		final File p = writeTrashFile(getName() + ".config", content);
+		final RepositoryConfig c = new RepositoryConfig(null, p);
+		return c;
+	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
index cb287ee..e3a303f 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
@@ -254,10 +254,19 @@ public boolean getBoolean(final String section, String subsection,
 			return defaultValue;
 
 		n = n.toLowerCase();
-		if (MAGIC_EMPTY_VALUE.equals(n) || "yes".equalsIgnoreCase(n) || "true".equalsIgnoreCase(n) || "1".equals(n)) {
+		if (MAGIC_EMPTY_VALUE.equals(n)
+				|| "yes".equalsIgnoreCase(n)
+				|| "true".equalsIgnoreCase(n)
+				|| "1".equals(n)
+				|| "on".equals(n)) {
 			return true;
-		} else if ("no".equalsIgnoreCase(n) || "false".equalsIgnoreCase(n) || "0".equalsIgnoreCase(n)) {
+
+		} else if ("no".equalsIgnoreCase(n)
+				|| "false".equalsIgnoreCase(n)
+				|| "0".equalsIgnoreCase(n)
+				|| "off".equalsIgnoreCase(n)) {
 			return false;
+
 		} else {
 			throw new IllegalArgumentException("Invalid boolean value: "
 					+ section + "." + name + "=" + n);
-- 
1.6.3.195.gad81
Shawn O. Pearce· May 7, 2009, 15:05 UTC · re: Shawn O. Pearce · lore

[JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

In 3e262b95c509 I taught C Git to disallow refs whose names end in ".lock". This suffix is used by the atomic update mechanism as a signal that the ref is being modified. When reading a loose ref directory both JGit and C Git skip over any files whose names end with this suffix, as the file is assumed to be one of these magic locking files from another concurrent process. Consequently, any ref that ends with this name will become invisible once created.

We also add a suite of tests for the isValidRefName function, fix its formatting to better conform to current style conventions, and correct the result for "master"; this is not a valid ref name as it has only 1 path component. At least 2 path components is required.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../tst/org/spearce/jgit/lib/ValidRefNameTest.java |  168 ++++++++++++++++++++
 .../src/org/spearce/jgit/lib/Repository.java       |   28 ++--
 2 files changed, 181 insertions(+), 15 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
Show changes to 2 files +181 −15

org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java, org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
new file mode 100644
index 0000000..5cca682
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import junit.framework.TestCase;
+
+public class ValidRefNameTest extends TestCase {
+	private static void assertValid(final boolean exp, final String name) {
+		assertEquals("\"" + name + "\"", exp, Repository.isValidRefName(name));
+	}
+
+	public void testEmptyString() {
+		assertValid(false, "");
+		assertValid(false, "/");
+	}
+
+	public void testMustHaveTwoComponents() {
+		assertValid(false, "master");
+		assertValid(true, "heads/master");
+	}
+
+	public void testValidHead() {
+		assertValid(true, "refs/heads/master");
+		assertValid(true, "refs/heads/pu");
+		assertValid(true, "refs/heads/z");
+		assertValid(true, "refs/heads/FoO");
+	}
+
+	public void testValidTag() {
+		assertValid(true, "refs/tags/v1.0");
+	}
+
+	public void testNoLockSuffix() {
+		assertValid(false, "refs/heads/master.lock");
+	}
+
+	public void testNoDirectorySuffix() {
+		assertValid(false, "refs/heads/master/");
+	}
+
+	public void testNoSpace() {
+		assertValid(false, "refs/heads/i haz space");
+	}
+
+	public void testNoAsciiControlCharacters() {
+		for (char c = '\0'; c < ' '; c++)
+			assertValid(false, "refs/heads/mast" + c + "er");
+	}
+
+	public void testNoBareDot() {
+		assertValid(false, "refs/heads/.");
+		assertValid(false, "refs/heads/..");
+		assertValid(false, "refs/heads/./master");
+		assertValid(false, "refs/heads/../master");
+	}
+
+	public void testNoLeadingDot() {
+		assertValid(false, ".");
+		assertValid(false, "refs/heads/.bar");
+		assertValid(false, "refs/heads/..bar");
+	}
+
+	public void testContainsDot() {
+		assertValid(true, "refs/heads/m.a.s.t.e.r");
+		assertValid(false, "refs/heads/master..pu");
+	}
+
+	public void testNoMagicRefCharacters() {
+		assertValid(false, "refs/heads/master^");
+		assertValid(false, "refs/heads/^master");
+		assertValid(false, "^refs/heads/master");
+
+		assertValid(false, "refs/heads/master~");
+		assertValid(false, "refs/heads/~master");
+		assertValid(false, "~refs/heads/master");
+
+		assertValid(false, "refs/heads/master:");
+		assertValid(false, "refs/heads/:master");
+		assertValid(false, ":refs/heads/master");
+	}
+
+	public void testShellGlob() {
+		assertValid(false, "refs/heads/master?");
+		assertValid(false, "refs/heads/?master");
+		assertValid(false, "?refs/heads/master");
+
+		assertValid(false, "refs/heads/master[");
+		assertValid(false, "refs/heads/[master");
+		assertValid(false, "[refs/heads/master");
+
+		assertValid(false, "refs/heads/master*");
+		assertValid(false, "refs/heads/*master");
+		assertValid(false, "*refs/heads/master");
+	}
+
+	public void testValidSpecialCharacters() {
+		assertValid(true, "refs/heads/!");
+		assertValid(true, "refs/heads/\"");
+		assertValid(true, "refs/heads/#");
+		assertValid(true, "refs/heads/$");
+		assertValid(true, "refs/heads/%");
+		assertValid(true, "refs/heads/&");
+		assertValid(true, "refs/heads/'");
+		assertValid(true, "refs/heads/(");
+		assertValid(true, "refs/heads/)");
+		assertValid(true, "refs/heads/+");
+		assertValid(true, "refs/heads/,");
+		assertValid(true, "refs/heads/-");
+		assertValid(true, "refs/heads/;");
+		assertValid(true, "refs/heads/<");
+		assertValid(true, "refs/heads/=");
+		assertValid(true, "refs/heads/>");
+		assertValid(true, "refs/heads/@");
+		assertValid(true, "refs/heads/]");
+		assertValid(true, "refs/heads/_");
+		assertValid(true, "refs/heads/`");
+		assertValid(true, "refs/heads/{");
+		assertValid(true, "refs/heads/|");
+		assertValid(true, "refs/heads/}");
+
+		// This is valid on UNIX, but not on Windows.
+		//
+		assertValid(true, "refs/heads/\\");
+	}
+
+	public void testRefLogQueryIsValidRef() {
+		// Yes, these are actually ambiguous. You can create a ref
+		// that also looks like a reflog query.
+		//
+		assertValid(true, "refs/heads/master@{1}");
+		assertValid(true, "refs/heads/master@{1.hour.ago}");
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index 5baa7a0..66dcd46 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -939,36 +939,34 @@ public static boolean isValidRefName(final String refName) {
 		final int len = refName.length();
 		if (len == 0)
 			return false;
+		if (refName.endsWith(".lock"))
+			return false;
 
+		int components = 1;
 		char p = '\0';
-		for (int i=0; i<len; ++i) {
-			char c = refName.charAt(i);
+		for (int i = 0; i < len; i++) {
+			final char c = refName.charAt(i);
 			if (c <= ' ')
 				return false;
-			switch(c) {
+			switch (c) {
 			case '.':
-				if (i == 0)
-					return false;
-				if (p == '/')
-					return false;
-				if (p == '.')
+				switch (p) {
+				case '\0': case '/': case '.':
 					return false;
+				}
 				break;
 			case '/':
-				if (i == 0)
-					return false;
-				if (i == len -1)
+				if (i == 0 || i == len - 1)
 					return false;
+				components++;
 				break;
 			case '~': case '^': case ':':
-			case '?': case '[':
-				return false;
-			case '*':
+			case '?': case '[': case '*':
 				return false;
 			}
 			p = c;
 		}
-		return true;
+		return components > 1;
 	}
 
 	/**
-- 
1.6.3.195.gad81
Robin Rosenberg· May 7, 2009, 23:02 UTC · re: Shawn O. Pearce · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

NAK, ammended patch follows.

Disallows @{, names that end with '.', '\' (that's the "almost" part) plus explicitly allows unicode names.

-- robin
>From 0854d96e2774bd084cc673ab16cf61c489f0562e Mon Sep 17 00:00:00 2001
From: Shawn O. Pearce <spearce@spearce.org>
Date: Thu, 7 May 2009 08:05:14 -0700
Subject: [EGIT PATCH] Make Repository.isValidRefName almost compatible with Git 1.6.3

In 3e262b95c509 I taught C Git to disallow refs whose names end in ".lock". This suffix is used by the atomic update mechanism as a signal that the ref is being modified. When reading a loose ref directory both JGit and C Git skip over any files whose names end with this suffix, as the file is assumed to be one of these magic locking files from another concurrent process. Consequently, any ref that ends with this name will become invisible once created.

In cbdffe4093be Junio disallows names that looks like reflog queries as well as names that ends with periods.

We also add a suite of tests for the isValidRefName function, fix its formatting to better conform to current style conventions, and correct the result for "master"; this is not a valid ref name as it has only 1 path component. At least 2 path components is required.

In addition to 1.6.3 we disallow names with a '\' (backslash) since that is a directory separator in Windows. Allowing JGit users to create such names is just asking for trouble. This does not necessarily prevent JGit from working with such refs in other situations.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
 .../tst/org/spearce/jgit/lib/ValidRefNameTest.java |  171 ++++++++++++++++++++
 .../src/org/spearce/jgit/lib/Repository.java       |   33 +++--
 2 files changed, 191 insertions(+), 13 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
Show changes to 2 files +191 −13

org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java, org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
new file mode 100644
index 0000000..cb6b9a7
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ValidRefNameTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2009, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.lib;
+
+import junit.framework.TestCase;
+
+public class ValidRefNameTest extends TestCase {
+	private static void assertValid(final boolean exp, final String name) {
+		assertEquals("\"" + name + "\"", exp, Repository.isValidRefName(name));
+	}
+
+	public void testEmptyString() {
+		assertValid(false, "");
+		assertValid(false, "/");
+	}
+
+	public void testMustHaveTwoComponents() {
+		assertValid(false, "master");
+		assertValid(true, "heads/master");
+	}
+
+	public void testValidHead() {
+		assertValid(true, "refs/heads/master");
+		assertValid(true, "refs/heads/pu");
+		assertValid(true, "refs/heads/z");
+		assertValid(true, "refs/heads/FoO");
+	}
+
+	public void testValidTag() {
+		assertValid(true, "refs/tags/v1.0");
+	}
+
+	public void testNoLockSuffix() {
+		assertValid(false, "refs/heads/master.lock");
+	}
+
+	public void testNoDirectorySuffix() {
+		assertValid(false, "refs/heads/master/");
+	}
+
+	public void testNoSpace() {
+		assertValid(false, "refs/heads/i haz space");
+	}
+
+	public void testNoAsciiControlCharacters() {
+		for (char c = '\0'; c < ' '; c++)
+			assertValid(false, "refs/heads/mast" + c + "er");
+	}
+
+	public void testNoBareDot() {
+		assertValid(false, "refs/heads/.");
+		assertValid(false, "refs/heads/..");
+		assertValid(false, "refs/heads/./master");
+		assertValid(false, "refs/heads/../master");
+	}
+
+	public void testNoLeadingOrTrailingDot() {
+		assertValid(false, ".");
+		assertValid(false, "refs/heads/.bar");
+		assertValid(false, "refs/heads/..bar");
+		assertValid(false, "refs/heads/bar.");
+	}
+
+	public void testContainsDot() {
+		assertValid(true, "refs/heads/m.a.s.t.e.r");
+		assertValid(false, "refs/heads/master..pu");
+	}
+
+	public void testNoMagicRefCharacters() {
+		assertValid(false, "refs/heads/master^");
+		assertValid(false, "refs/heads/^master");
+		assertValid(false, "^refs/heads/master");
+
+		assertValid(false, "refs/heads/master~");
+		assertValid(false, "refs/heads/~master");
+		assertValid(false, "~refs/heads/master");
+
+		assertValid(false, "refs/heads/master:");
+		assertValid(false, "refs/heads/:master");
+		assertValid(false, ":refs/heads/master");
+	}
+
+	public void testShellGlob() {
+		assertValid(false, "refs/heads/master?");
+		assertValid(false, "refs/heads/?master");
+		assertValid(false, "?refs/heads/master");
+
+		assertValid(false, "refs/heads/master[");
+		assertValid(false, "refs/heads/[master");
+		assertValid(false, "[refs/heads/master");
+
+		assertValid(false, "refs/heads/master*");
+		assertValid(false, "refs/heads/*master");
+		assertValid(false, "*refs/heads/master");
+	}
+
+	public void testValidSpecialCharacters() {
+		assertValid(true, "refs/heads/!");
+		assertValid(true, "refs/heads/\"");
+		assertValid(true, "refs/heads/#");
+		assertValid(true, "refs/heads/$");
+		assertValid(true, "refs/heads/%");
+		assertValid(true, "refs/heads/&");
+		assertValid(true, "refs/heads/'");
+		assertValid(true, "refs/heads/(");
+		assertValid(true, "refs/heads/)");
+		assertValid(true, "refs/heads/+");
+		assertValid(true, "refs/heads/,");
+		assertValid(true, "refs/heads/-");
+		assertValid(true, "refs/heads/;");
+		assertValid(true, "refs/heads/<");
+		assertValid(true, "refs/heads/=");
+		assertValid(true, "refs/heads/>");
+		assertValid(true, "refs/heads/@");
+		assertValid(true, "refs/heads/]");
+		assertValid(true, "refs/heads/_");
+		assertValid(true, "refs/heads/`");
+		assertValid(true, "refs/heads/{");
+		assertValid(true, "refs/heads/|");
+		assertValid(true, "refs/heads/}");
+
+		// This is valid on UNIX, but not on Windows
+		// hence we make in invalid due to non-portability
+		//
+		assertValid(false, "refs/heads/\\");
+	}
+
+	public void testUnicodeNames() {
+		assertValid(true, "refs/heads/\u00e5ngstr\u00f6m");
+	}
+
+	public void testRefLogQueryIsValidRef() {
+		assertValid(false, "refs/heads/master@{1}");
+		assertValid(false, "refs/heads/master@{1.hour.ago}");
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index 5baa7a0..a47207d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -931,6 +931,8 @@ public RepositoryState getRepositoryState() {
 	 * a special meaning in a Git object reference expression. Some other
 	 * dangerous characters are also excluded.
 	 *
+	 * For portability reasons '\' is excluded
+	 *
 	 * @param refName
 	 *
 	 * @return true if refName is a valid ref name
@@ -939,36 +941,41 @@ public static boolean isValidRefName(final String refName) {
 		final int len = refName.length();
 		if (len == 0)
 			return false;
+		if (refName.endsWith(".lock"))
+			return false;
 
+		int components = 1;
 		char p = '\0';
-		for (int i=0; i<len; ++i) {
-			char c = refName.charAt(i);
+		for (int i = 0; i < len; i++) {
+			final char c = refName.charAt(i);
 			if (c <= ' ')
 				return false;
-			switch(c) {
+			switch (c) {
 			case '.':
-				if (i == 0)
-					return false;
-				if (p == '/')
+				switch (p) {
+				case '\0': case '/': case '.':
 					return false;
-				if (p == '.')
+				}
+				if (i == len -1)
 					return false;
 				break;
 			case '/':
-				if (i == 0)
+				if (i == 0 || i == len - 1)
 					return false;
-				if (i == len -1)
+				components++;
+				break;
+			case '@':
+				if (p == '{')
 					return false;
 				break;
 			case '~': case '^': case ':':
-			case '?': case '[':
-				return false;
-			case '*':
+			case '?': case '[': case '*':
+			case '\\':
 				return false;
 			}
 			p = c;
 		}
-		return true;
+		return components > 1;
 	}
 
 	/**
-- 
1.6.3
Linus Torvalds· May 7, 2009, 23:29 UTC · re: Robin Rosenberg · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

On Fri, 8 May 2009, Robin Rosenberg wrote:
> 
> In 3e262b95c509 I taught C Git to disallow refs whose names end in
> ".lock".

Btw, I think we should revert that, and instead change our naming for lock-files.

It's a silly limitation, and for a stupid reason, I think. I can well imagine people using a branch naming policy of using '.' instead of spaces, and then It makes perfect sense to call a branch "fix.vm.lock" when you have a VM locking issue you want to fix.

So I think we should remove that stupid "*.lock" rule from the C version (and then obviously from the JGIT one too).

Now, that leaves what we _should_ call the lock-files, and the only sane thing to do is to pick a sequence that is already invalid for other reasons. We could make the lockfile end in "..lock", for example (".." is not legal in names due to the confusion with the "a..b" range notation), or just end or begin with a "." (same issue, except "..." now).

Or just make it end in '~' which is also invalid for similar reasons.
		Linus
Junio C Hamano· May 8, 2009, 00:32 UTC · re: Linus Torvalds · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Linus Torvalds <torvalds@linux-foundation.org> writes:
Show 23 quoted lines
> On Fri, 8 May 2009, Robin Rosenberg wrote:
>> 
>> In 3e262b95c509 I taught C Git to disallow refs whose names end in
>> ".lock".
>
> Btw, I think we should revert that, and instead change our naming for 
> lock-files.
>
> It's a silly limitation, and for a stupid reason, I think. I can well 
> imagine people using a branch naming policy of using '.' instead of 
> spaces, and then It makes perfect sense to call a branch "fix.vm.lock" 
> when you have a VM locking issue you want to fix.
>
> So I think we should remove that stupid "*.lock" rule from the C version 
> (and then obviously from the JGIT one too).
>
> Now, that leaves what we _should_ call the lock-files, and the only sane 
> thing to do is to pick a sequence that is already invalid for other 
> reasons. We could make the lockfile end in "..lock", for example (".." is 
> not legal in names due to the confusion with the "a..b" range notation), 
> or just end or begin with a "." (same issue, except "..." now).
>
> Or just make it end in '~' which is also invalid for similar reasons.
I was tempted to go for the tilde, but then realized that
	.git/config~

is a common thing to have in a repository owned by an Emacs user, and we do create with O_CREAT, so it is not such a good idea.

'..lck' may be a good name to use here.
Shawn O. Pearce· May 8, 2009, 00:47 UTC · re: Junio C Hamano · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Junio C Hamano <gitster@pobox.com> wrote:
Show 9 quoted lines
> Linus Torvalds <torvalds@linux-foundation.org> writes:
> >> 
> >> In 3e262b95c509 I taught C Git to disallow refs whose names end in
> >> ".lock".
> >
> > Btw, I think we should revert that, and instead change our naming for 
> > lock-files.
> 
> '..lck' may be a good name to use here.
I agree.  So much so that I wrote a patch for you.

--8<-- Change .lock suffix to ..lck

In 3e262b95c509 I taught Git to deny creation of refs whose name ends in ".lock", as the loose ref scanner skips over these under the assumption that they are refs being modified by a concurrent process operating on the same repository.

Linus pointed out that the name "fix.vm.lock" is an otherwise perfectly valid branch name, except the ".lock" suffix conflicts with the internal implementation detail of how Git manages doing an atomic update.

Instead, change the name to "..lck", as suggested by Junio.

This uses the same storage space in memory as ".lock", so we can do a fairly dumb search-replace to make the change, but the ".." prefix has been forbidden in a ref name for ages, to prevent "a..b" from being ambiguous as a single ref name, or as the pair "^a b".

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 Documentation/git-check-ref-format.txt   |    2 --
 Documentation/technical/api-lockfile.txt |    4 ++--
 builtin-reflog.c                         |    2 +-
 config.c                                 |    2 +-
 lockfile.c                               |   10 +++++-----
 refs.c                                   |   11 ++++-------
 t/t3505-cherry-pick-empty.sh             |    2 +-
 t/t3600-rm.sh                            |    4 ++--
 t/t4123-apply-shrink.sh                  |    2 +-
 t/t7001-mv.sh                            |    4 ++--
 t/t7502-commit.sh                        |    2 +-
 t/test-lib.sh                            |    6 +++---
 12 files changed, 23 insertions(+), 28 deletions(-)
Show changes to 12 files +23 −28

Documentation/git-check-ref-format.txt, Documentation/technical/api-lockfile.txt, builtin-reflog.c, config.c, lockfile.c, refs.c, t/t3505-cherry-pick-empty.sh, t/t3600-rm.sh, t/t4123-apply-shrink.sh, t/t7001-mv.sh, t/t7502-commit.sh, t/test-lib.sh

diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index c1ce268..7c0f0ea 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -34,8 +34,6 @@ imposes the following rules on how references are named:
 
 . They cannot end with a slash `/` nor a dot `.`.
 
-. They cannot end with the sequence `.lock`.
-
 . They cannot contain a sequence `@{`.
 
 These rules make it easy for shell script based tools to parse
diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt
index dd89404..982984f 100644
--- a/Documentation/technical/api-lockfile.txt
+++ b/Documentation/technical/api-lockfile.txt
@@ -4,9 +4,9 @@ lockfile API
 The lockfile API serves two purposes:
 
 * Mutual exclusion.  When we write out a new index file, first
-  we create a new file `$GIT_DIR/index.lock`, write the new
+  we create a new file `$GIT_DIR/index..lck`, write the new
   contents into it, and rename it to the final destination
-  `$GIT_DIR/index`.  We try to create the `$GIT_DIR/index.lock`
+  `$GIT_DIR/index`.  We try to create the `$GIT_DIR/index..lck`
   file with O_EXCL so that we can notice and fail when somebody
   else is already trying to update the index file.
 
diff --git a/builtin-reflog.c b/builtin-reflog.c
index ddfdf5a..19baf07 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -341,7 +341,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
 	if (!file_exists(log_file))
 		goto finish;
 	if (!cmd->dry_run) {
-		newlog_path = git_pathdup("logs/%s.lock", ref);
+		newlog_path = git_pathdup("logs/%s..lck", ref);
 		cb.newlog = fopen(newlog_path, "w");
 	}
 
diff --git a/config.c b/config.c
index 1682273..50ecc1e 100644
--- a/config.c
+++ b/config.c
@@ -925,7 +925,7 @@ int git_config_set(const char *key, const char *value)
  *
  * This function does this:
  *
- * - it locks the config file by creating ".git/config.lock"
+ * - it locks the config file by creating ".git/config..lck"
  *
  * - it then parses the config using store_aux() as validator to find
  *   the position on the key/value pair to replace. If it is to be unset,
diff --git a/lockfile.c b/lockfile.c
index 828d19f..61ff5cb 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -129,11 +129,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
 	strcpy(lk->filename, path);
 	/*
 	 * subtract 5 from size to make sure there's room for adding
-	 * ".lock" for the lock file name
+	 * "..lck" for the lock file name
 	 */
 	if (!(flags & LOCK_NODEREF))
 		resolve_symlink(lk->filename, sizeof(lk->filename)-5);
-	strcat(lk->filename, ".lock");
+	strcat(lk->filename, "..lck");
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
 		if (!lock_file_list) {
@@ -159,13 +159,13 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
 NORETURN void unable_to_lock_index_die(const char *path, int err)
 {
 	if (err == EEXIST) {
-		die("Unable to create '%s.lock': %s.\n\n"
+		die("Unable to create '%s..lck': %s.\n\n"
 		    "If no other git process is currently running, this probably means a\n"
 		    "git process crashed in this repository earlier. Make sure no other git\n"
 		    "process is running and remove the file manually to continue.",
 		    path, strerror(err));
 	} else {
-		die("Unable to create '%s.lock': %s", path, strerror(err));
+		die("Unable to create '%s..lck': %s", path, strerror(err));
 	}
 }
 
@@ -219,7 +219,7 @@ int commit_lock_file(struct lock_file *lk)
 	if (lk->fd >= 0 && close_lock_file(lk))
 		return -1;
 	strcpy(result_file, lk->filename);
-	i = strlen(result_file) - 5; /* .lock */
+	i = strlen(result_file) - 5; /* ..lck */
 	result_file[i] = 0;
 	if (rename(lk->filename, result_file))
 		return -1;
diff --git a/refs.c b/refs.c
index e65a3b4..b4ca305 100644
--- a/refs.c
+++ b/refs.c
@@ -266,7 +266,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
 			namelen = strlen(de->d_name);
 			if (namelen > 255)
 				continue;
-			if (has_extension(de->d_name, ".lock"))
+			if (has_extension(de->d_name, "..lck"))
 				continue;
 			memcpy(ref + baselen, de->d_name, namelen+1);
 			if (stat(git_path("%s", ref), &st) < 0)
@@ -681,7 +681,6 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
  * - it ends with a "/".
- * - it ends with ".lock"
  */
 
 static inline int bad_ref_char(int ch)
@@ -743,8 +742,6 @@ int check_ref_format(const char *ref)
 				return CHECK_REF_FORMAT_ERROR;
 			if (level < 2)
 				return CHECK_REF_FORMAT_ONELEVEL;
-			if (has_extension(ref, ".lock"))
-				return CHECK_REF_FORMAT_ERROR;
 			return ret;
 		}
 	}
@@ -996,7 +993,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 		const char *path;
 
 		if (!(delopt & REF_NODEREF)) {
-			i = strlen(lock->lk->filename) - 5; /* .lock */
+			i = strlen(lock->lk->filename) - 5; /* ..lck */
 			lock->lk->filename[i] = 0;
 			path = lock->lk->filename;
 		} else {
@@ -1363,7 +1360,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
 		error("refname too long: %s", refs_heads_master);
 		goto error_free_return;
 	}
-	lockpath = mkpath("%s.lock", git_HEAD);
+	lockpath = mkpath("%s..lck", git_HEAD);
 	fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
 	if (fd < 0) {
 		error("Unable to open %s for writing", lockpath);
@@ -1593,7 +1590,7 @@ static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
 			namelen = strlen(de->d_name);
 			if (namelen > 255)
 				continue;
-			if (has_extension(de->d_name, ".lock"))
+			if (has_extension(de->d_name, "..lck"))
 				continue;
 			memcpy(log + baselen, de->d_name, namelen+1);
 			if (stat(git_path("logs/%s", log), &st) < 0)
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 9aaeabd..3b7aa6d 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -26,7 +26,7 @@ test_expect_code 1 'cherry-pick an empty commit' '
 
 test_expect_success 'index lockfile was removed' '
 
-	test ! -f .git/index.lock
+	test ! -f .git/index..lck
 
 '
 
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 76b1bb4..e437cf7 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -264,9 +264,9 @@ test_expect_success 'choking "git rm" should not let it die with cruft' '
 	    i=$(( $i + 1 ))
 	done | git update-index --index-info &&
 	git rm -n "some-file-*" | :;
-	test -f .git/index.lock
+	test -f .git/index..lck
 	status=$?
-	rm -f .git/index.lock
+	rm -f .git/index..lck
 	git reset -q --hard
 	test "$status" != 0
 '
diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh
index 984157f..fcd6a67 100755
--- a/t/t4123-apply-shrink.sh
+++ b/t/t4123-apply-shrink.sh
@@ -47,7 +47,7 @@ test_expect_success 'apply should fail gracefully' '
 	else
 		status=$?
 		echo "Status was $status"
-		if test -f .git/index.lock
+		if test -f .git/index..lck
 		then
 			echo Oops, should not have crashed
 			false
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 10b8f8c..afe8240 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -62,14 +62,14 @@ test_expect_success \
     'checking -f on untracked file with existing target' \
     'touch path0/untracked1 &&
      git mv -f untracked1 path0
-     test ! -f .git/index.lock &&
+     test ! -f .git/index..lck &&
      test -f untracked1 &&
      test -f path0/untracked1'
 
 # clean up the mess in case bad things happen
 rm -f idontexist untracked1 untracked2 \
      path0/idontexist path0/untracked1 path0/untracked2 \
-     .git/index.lock
+     .git/index..lck
 
 test_expect_success \
     'adding another file' \
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 56cd866..100da27 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -241,7 +241,7 @@ test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
 	  GIT_EDITOR=.git/FAKE_EDITOR
 	  export GIT_EDITOR
 	  exec git commit -a'\'' &&
-	test ! -f .git/index.lock
+	test ! -f .git/index..lck
 '
 
 rm -f .git/MERGE_MSG .git/COMMIT_EDITMSG
diff --git a/t/test-lib.sh b/t/test-lib.sh
index dad1437..ecce338 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -558,13 +558,13 @@ else
 		test -h "$2" &&
 		test "$1" = "$(readlink "$2")" || {
 			# be super paranoid
-			if mkdir "$2".lock
+			if mkdir "$2"..lck
 			then
 				rm -f "$2" &&
 				ln -s "$1" "$2" &&
-				rm -r "$2".lock
+				rm -r "$2"..lck
 			else
-				while test -d "$2".lock
+				while test -d "$2"..lck
 				do
 					say "Waiting for lock on $2."
 					sleep 1
-- 
1.6.3.195.gad81


-- 
Shawn.
Alex Riesen· May 8, 2009, 07:24 UTC · re: Shawn O. Pearce · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

2009/5/8 Shawn O. Pearce <spearce@spearce.org>:
Show 14 quoted lines
> --- a/lockfile.c
> +++ b/lockfile.c
> @@ -129,11 +129,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
>        strcpy(lk->filename, path);
>        /*
>         * subtract 5 from size to make sure there's room for adding
> -        * ".lock" for the lock file name
> +        * "..lck" for the lock file name
>         */
>        if (!(flags & LOCK_NODEREF))
>                resolve_symlink(lk->filename, sizeof(lk->filename)-5);
> -       strcat(lk->filename, ".lock");
> +       strcat(lk->filename, "..lck");
>        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);

There must be at least some deprecation period during which also the old .lock lockfiles are considered and created. Just imagine someone modifying the ref with an older version, which knows nothing about ..lck.

Johannes Schindelin· May 8, 2009, 08:04 UTC · re: Alex Riesen · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Hi,
On Fri, 8 May 2009, Alex Riesen wrote:
Show 20 quoted lines
> 2009/5/8 Shawn O. Pearce <spearce@spearce.org>:
> > --- a/lockfile.c
> > +++ b/lockfile.c
> > @@ -129,11 +129,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
> >        strcpy(lk->filename, path);
> >        /*
> >         * subtract 5 from size to make sure there's room for adding
> > -        * ".lock" for the lock file name
> > +        * "..lck" for the lock file name
> >         */
> >        if (!(flags & LOCK_NODEREF))
> >                resolve_symlink(lk->filename, sizeof(lk->filename)-5);
> > -       strcat(lk->filename, ".lock");
> > +       strcat(lk->filename, "..lck");
> >        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
> 
> There must be at least some deprecation period during which
> also the old .lock lockfiles are considered and created.
> Just imagine someone modifying the ref with an older version,
> which knows nothing about ..lck.

As lock files are only supposed to be created by Git itself, and have a maximum lifetime until the end of the process, I think we do not need a grace period at all.

Ciao, Dscho

Junio C Hamano· May 8, 2009, 08:43 UTC · re: Johannes Schindelin · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 8 quoted lines
>> There must be at least some deprecation period during which
>> also the old .lock lockfiles are considered and created.
>> Just imagine someone modifying the ref with an older version,
>> which knows nothing about ..lck.
>
> As lock files are only supposed to be created by Git itself, and have a 
> maximum lifetime until the end of the process, I think we do not need a 
> grace period at all.

Could there be people with slightly older git and shiny new jgit (or the other combination) working on the same repository?

Johannes Schindelin· May 8, 2009, 09:54 UTC · re: Junio C Hamano · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Hi,
On Fri, 8 May 2009, Junio C Hamano wrote:
Show 13 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> There must be at least some deprecation period during which also the 
> >> old .lock lockfiles are considered and created. Just imagine someone 
> >> modifying the ref with an older version, which knows nothing about 
> >> ..lck.
> >
> > As lock files are only supposed to be created by Git itself, and have 
> > a maximum lifetime until the end of the process, I think we do not 
> > need a grace period at all.
> 
> Could there be people with slightly older git and shiny new jgit (or the 
> other combination) working on the same repository?

You mean concurrently? Sure, but do we have to care? People doing this certainly know what they are doing, and live happily even with a 0.5" hole in their foot.

Ciao, Dscho

Alex Riesen· May 8, 2009, 11:45 UTC · re: Johannes Schindelin · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

2009/5/8 Johannes Schindelin <Johannes.Schindelin@gmx.de>:
Show 18 quoted lines
> On Fri, 8 May 2009, Junio C Hamano wrote:
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>>
>> >> There must be at least some deprecation period during which also the
>> >> old .lock lockfiles are considered and created. Just imagine someone
>> >> modifying the ref with an older version, which knows nothing about
>> >> ..lck.
>> >
>> > As lock files are only supposed to be created by Git itself, and have
>> > a maximum lifetime until the end of the process, I think we do not
>> > need a grace period at all.
>>
>> Could there be people with slightly older git and shiny new jgit (or the
>> other combination) working on the same repository?
>
> You mean concurrently?  Sure, but do we have to care?  People doing this
> certainly know what they are doing, and live happily even with a 0.5"
> hole in their foot.
I certainly hope that'll be you, one day.

Of course people run git concurrently on the same repo. Even from different machines.

That's _why_ we have the locking in the first place.

And of course there will be different versions of git installed on these machines. And most certainly the people will not know what they were doing, blame the hole on Git (and maybe even shoot you in return, after they'll find that stupid message of yours in archives).

Shawn O. Pearce· May 8, 2009, 14:34 UTC · re: Alex Riesen · lore

Re: [JGIT PATCH 2/2] Make Repository.isValidRefName compatible with Git 1.6.3

Alex Riesen <raa.lkml@gmail.com> wrote:
Show 5 quoted lines
> 2009/5/8 Johannes Schindelin <Johannes.Schindelin@gmx.de>:
> > On Fri, 8 May 2009, Junio C Hamano wrote:
> >>
> >> Could there be people with slightly older git and shiny new jgit (or the
> >> other combination) working on the same repository?

Yes, Gerrit Code Review runs a JGit daemon 24/7. I have a lot of folks running Gerrit servers inside their company networks, and two running them on the public internet. Gerrit runs bleeding edge JGit, and would most certainly pick up a "..lck" patch there before they upgrade their git-core package.

This is one of those cases where the locking is really important, because you should be able to concurrently access the same git repository from the command line with git-core utilities while Gerrit's daemon process is servicing network based user requests.

> > You mean concurrently? ??Sure, but do we have to care? ??People doing this
> > certainly know what they are doing, and live happily even with a 0.5"
> > hole in their foot.
Yes, dammit, we should care.
> Of course people run git concurrently on the same repo. Even from different
> machines.

Even ignoring JGit and Gerrit's daemon process entirely as "useless stupid projects that never should have happened", _this_ is enough of a reason to care. People use Git on NFS all of the time. With different machines. Accessing the same repo. Those different machines most likely have different versions of Git installed.

> That's _why_ we have the locking in the first place.
Yes.

I've actually thought about this "fix.vm.lock" being invalid for quite a while now, and wanted to change lockfile.c to use another pattern that was already an invalid ref name, like Junio's "..lck" proposal does. But I've never been able to come up with a sane way to deal with the transition. So I never brought the topic up before.

I still don't have a sane way to deal with the transition.
The only thing I can think of is:
  - grab ".lock"
  - grab "..lck"
  - do stuff
  - commit by link("..lck", orig)
  - ulink ".lock"
And maybe in a year we stop writing ".lock".

But, here's news for you, *#@!#@@!$@!*&@! distributions are still shipping 1.5.0-1.5.0.3. In particular I've spent the better part of this past two weeks telling new Git users to upgrade off of these versions due to the pread() bug introduced in [1] and fixed in [2].

I've seen WAAAAY too many "fatal: cannot pread pack file:" errors. Enough for a lifetime.

[1] http://repo.or.cz/w/git.git?a=commit;h=6d2fa7f1b489c65e677c18eda5c144dbc5d614ab [2] http://repo.or.cz/w/git.git?a=commit;h=a91d49cd369ac5fc8e1a17357a975d09cf6c8cb3

-- 
Shawn.
Brandon Casey· May 7, 2009, 17:56 UTC · re: Shawn O. Pearce · lore

Re: [JGIT PATCH 1/2] Add support for boolean config values "yes", "no"

Shawn O. Pearce wrote:
> In 8f8c6fafd92f (shipped in 1.6.3) Linus taught C Git how to read
> boolean configuration values set to "yes" as true and "no" as false.
> Add support for these values, and some test cases.

Linus added support for "on" and "off" in that commit as it appears your commit is also doing. :)

I was about to point out that you are not ignoring case for "on", but using equalsIgnoreCase() appears to be unnecessary since above that you are already doing n = n.toLowerCase().

-brandon
Show 26 quoted lines
> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
> index cb287ee..e3a303f 100644
> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
> @@ -254,10 +254,19 @@ public boolean getBoolean(final String section, String subsection,
>  			return defaultValue;
>  
>  		n = n.toLowerCase();
> -		if (MAGIC_EMPTY_VALUE.equals(n) || "yes".equalsIgnoreCase(n) || "true".equalsIgnoreCase(n) || "1".equals(n)) {
> +		if (MAGIC_EMPTY_VALUE.equals(n)
> +				|| "yes".equalsIgnoreCase(n)
> +				|| "true".equalsIgnoreCase(n)
> +				|| "1".equals(n)
> +				|| "on".equals(n)) {
>  			return true;
> -		} else if ("no".equalsIgnoreCase(n) || "false".equalsIgnoreCase(n) || "0".equalsIgnoreCase(n)) {
> +
> +		} else if ("no".equalsIgnoreCase(n)
> +				|| "false".equalsIgnoreCase(n)
> +				|| "0".equalsIgnoreCase(n)
> +				|| "off".equalsIgnoreCase(n)) {
>  			return false;
> +
>  		} else {
>  			throw new IllegalArgumentException("Invalid boolean value: "
>  					+ section + "." + name + "=" + n);
Shawn O. Pearce· May 7, 2009, 18:01 UTC · re: Brandon Casey · lore

[JGIT PATCH v2 1/2] Add support for boolean config values "on", "off"

In 8f8c6fafd92f (shipped in 1.6.3) Linus taught C Git how to read boolean configuration values set to "on" as true and "off" as false. Add support for these values, and some test cases.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
  Brandon Casey <casey@nrlssc.navy.mil> wrote:
  > Shawn O. Pearce wrote:
  > > In 8f8c6fafd92f (shipped in 1.6.3) Linus taught C Git how to read
  > > boolean configuration values set to "yes" as true and "no" as false.
  > > Add support for these values, and some test cases.
  > 
  > Linus added support for "on" and "off" in that commit as it appears
  > your commit is also doing. :)
  Whoops, good catch, thanks.
  > I was about to point out that you are not ignoring case for "on", but
  > using equalsIgnoreCase() appears to be unnecessary since above that
  > you are already doing n = n.toLowerCase().
  I dropped the toLowerCase and used equalsIgnoreCase instead.
  There is no reason to be allocating a copy of the string when
  equalsIgnorCase is sufficient.
  
 .../org/spearce/jgit/lib/RepositoryConfigTest.java |   70 ++++++++++++++++++--
 .../src/org/spearce/jgit/lib/RepositoryConfig.java |   14 +++-
 2 files changed, 74 insertions(+), 10 deletions(-)
Show changes to 2 files +74 −10

org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java, org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java

diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
index 4b5314c..ed573e1 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java
@@ -57,9 +57,7 @@
 	 * @throws IOException
 	 */
 	public void test001_ReadBareKey() throws IOException {
-		final File path = writeTrashFile("config_001", "[foo]\nbar\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
-		System.out.println(repositoryConfig.getString("foo", null, "bar"));
+		final RepositoryConfig repositoryConfig = read("[foo]\nbar\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", null, "bar", false));
 		assertEquals("", repositoryConfig.getString("foo", null, "bar"));
 	}
@@ -70,8 +68,7 @@ public void test001_ReadBareKey() throws IOException {
 	 * @throws IOException
 	 */
 	public void test002_ReadWithSubsection() throws IOException {
-		final File path = writeTrashFile("config_002", "[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
+		final RepositoryConfig repositoryConfig = read("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", "zip", "bar", false));
 		assertEquals("", repositoryConfig.getString("foo","zip", "bar"));
 		assertEquals(false, repositoryConfig.getBoolean("foo", "zap", "bar", true));
@@ -113,8 +110,7 @@ assertTrue(Arrays.equals(values.toArray(), repositoryConfig
 	}
 
 	public void test006_readCaseInsensitive() throws IOException {
-		final File path = writeTrashFile("config_001", "[Foo]\nBar\n");
-		RepositoryConfig repositoryConfig = new RepositoryConfig(null, path);
+		final RepositoryConfig repositoryConfig = read("[Foo]\nBar\n");
 		assertEquals(true, repositoryConfig.getBoolean("foo", null, "bar", false));
 		assertEquals("", repositoryConfig.getString("foo", null, "bar"));
 	}
@@ -182,4 +178,64 @@ public void test007_readUserInfos() throws IOException {
 		assertEquals("local username", authorName);
 		assertEquals("author@localemail", authorEmail);
 	}
+
+	public void testReadBoolean_TrueFalse1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = true\nb = false\n");
+		assertEquals("true", c.getString("s", null, "a"));
+		assertEquals("false", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_TrueFalse2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = TrUe\nb = fAlSe\n");
+		assertEquals("TrUe", c.getString("s", null, "a"));
+		assertEquals("fAlSe", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_YesNo1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = yes\nb = no\n");
+		assertEquals("yes", c.getString("s", null, "a"));
+		assertEquals("no", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_YesNo2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = yEs\nb = NO\n");
+		assertEquals("yEs", c.getString("s", null, "a"));
+		assertEquals("NO", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_OnOff1() throws IOException {
+		final RepositoryConfig c = read("[s]\na = on\nb = off\n");
+		assertEquals("on", c.getString("s", null, "a"));
+		assertEquals("off", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	public void testReadBoolean_OnOff2() throws IOException {
+		final RepositoryConfig c = read("[s]\na = ON\nb = OFF\n");
+		assertEquals("ON", c.getString("s", null, "a"));
+		assertEquals("OFF", c.getString("s", null, "b"));
+
+		assertTrue(c.getBoolean("s", "a", false));
+		assertFalse(c.getBoolean("s", "b", true));
+	}
+
+	private RepositoryConfig read(final String content) throws IOException {
+		final File p = writeTrashFile(getName() + ".config", content);
+		final RepositoryConfig c = new RepositoryConfig(null, p);
+		return c;
+	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
index cb287ee..b816604 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java
@@ -253,11 +253,19 @@ public boolean getBoolean(final String section, String subsection,
 		if (n == null)
 			return defaultValue;
 
-		n = n.toLowerCase();
-		if (MAGIC_EMPTY_VALUE.equals(n) || "yes".equalsIgnoreCase(n) || "true".equalsIgnoreCase(n) || "1".equals(n)) {
+		if (MAGIC_EMPTY_VALUE.equals(n)
+				|| "yes".equalsIgnoreCase(n)
+				|| "true".equalsIgnoreCase(n)
+				|| "1".equals(n)
+				|| "on".equalsIgnoreCase(n)) {
 			return true;
-		} else if ("no".equalsIgnoreCase(n) || "false".equalsIgnoreCase(n) || "0".equalsIgnoreCase(n)) {
+
+		} else if ("no".equalsIgnoreCase(n)
+				|| "false".equalsIgnoreCase(n)
+				|| "0".equals(n)
+				|| "off".equalsIgnoreCase(n)) {
 			return false;
+
 		} else {
 			throw new IllegalArgumentException("Invalid boolean value: "
 					+ section + "." + name + "=" + n);
-- 
1.6.3.195.gad81
Shawn O. Pearce· May 8, 2009, 00:54 UTC · lore

Re: [PATCH] Allow branch names that end with ".lock"

Junio C Hamano <gitster@pobox.com> wrote:
> A project policy for naming branches could be to use a dot as a word
> separator (instead of '-' which is often done by existing projects), and
> "fix.vm.lock" could be a very valid name for a branch to address a VM
> locking issues.
I think we sent the same patch... except...
 
>  git-gui/lib/index.tcl                    |    2 +-
Don't do that.  I'll patch git-gui and send you a pull request.
-- 
Shawn.
Junio C Hamano· May 8, 2009, 00:57 UTC · re: Shawn O. Pearce · lore

Re: [PATCH] Allow branch names that end with ".lock"

"Shawn O. Pearce" <spearce@spearce.org> writes:
Show 11 quoted lines
> Junio C Hamano <gitster@pobox.com> wrote:
>> A project policy for naming branches could be to use a dot as a word
>> separator (instead of '-' which is often done by existing projects), and
>> "fix.vm.lock" could be a very valid name for a branch to address a VM
>> locking issues.
>
> I think we sent the same patch... except...
>
>>  git-gui/lib/index.tcl                    |    2 +-
>
> Don't do that.  I'll patch git-gui and send you a pull request.

Why not? I never commit my "how about this" weatherbaloon patches directly ;-)

If you found that ours match identically except for that one line, that is a very good indication. I didn't check.

Shawn O. Pearce· May 8, 2009, 01:01 UTC · re: Junio C Hamano · lore

Re: [PATCH] Allow branch names that end with ".lock"

Junio C Hamano <gitster@pobox.com> wrote:
Show 16 quoted lines
> "Shawn O. Pearce" <spearce@spearce.org> writes:
> 
> > Junio C Hamano <gitster@pobox.com> wrote:
> >> A project policy for naming branches could be to use a dot as a word
> >> separator (instead of '-' which is often done by existing projects), and
> >> "fix.vm.lock" could be a very valid name for a branch to address a VM
> >> locking issues.
> >
> > I think we sent the same patch... except...
> >
> >>  git-gui/lib/index.tcl                    |    2 +-
> >
> > Don't do that.  I'll patch git-gui and send you a pull request.
> 
> Why not?  I never commit my "how about this" weatherbaloon patches
> directly ;-)

I know. I was saying, "don't patch git-gui in the same commit as git.git". I would have spun that as two different commits.

> If you found that ours match identically except for that one line, that is
> a very good indication.  I didn't check.
Here's `git diff yours mine`, we were really close:
Show changes to 2 files +1 −3

Documentation/technical/api-lockfile.txt, refs.c

diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt
index 31e1141..982984f 100644
--- a/Documentation/technical/api-lockfile.txt
+++ b/Documentation/technical/api-lockfile.txt
@@ -4,7 +4,7 @@ lockfile API
 The lockfile API serves two purposes:
 
 * Mutual exclusion.  When we write out a new index file, first
-  we create a new "lock" file `$GIT_DIR/index..lck`, write the new
+  we create a new file `$GIT_DIR/index..lck`, write the new
   contents into it, and rename it to the final destination
   `$GIT_DIR/index`.  We try to create the `$GIT_DIR/index..lck`
   file with O_EXCL so that we can notice and fail when somebody

ACK, I like your version better than mine.

diff --git a/refs.c b/refs.c
index 03aded9..b4ca305 100644
--- a/refs.c
+++ b/refs.c
@@ -742,8 +742,6 @@ int check_ref_format(const char *ref)
 				return CHECK_REF_FORMAT_ERROR;
 			if (level < 2)
 				return CHECK_REF_FORMAT_ONELEVEL;
-			if (has_extension(ref, "..lck"))
-				return CHECK_REF_FORMAT_ERROR;
 			return ret;
 		}
 	}

NAK, I like how I removed this block.  It can't happen anymore,
the no ".." in name earlier should have caught the condition.
-- 
Shawn.
Junio C Hamano· May 8, 2009, 01:25 UTC · re: Shawn O. Pearce · lore

Re: [PATCH] Allow branch names that end with ".lock"

"Shawn O. Pearce" <spearce@spearce.org> writes:
Show 16 quoted lines
> diff --git a/refs.c b/refs.c
> index 03aded9..b4ca305 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -742,8 +742,6 @@ int check_ref_format(const char *ref)
>  				return CHECK_REF_FORMAT_ERROR;
>  			if (level < 2)
>  				return CHECK_REF_FORMAT_ONELEVEL;
> -			if (has_extension(ref, "..lck"))
> -				return CHECK_REF_FORMAT_ERROR;
>  			return ret;
>  		}
>  	}
>
> NAK, I like how I removed this block.  It can't happen anymore,
> the no ".." in name earlier should have caught the condition.
Very true.  Thanks.

← back to recent threads