{"thread":{"id":"16566","subject":"[JGIT PATCH 2/2] Improve closing of files in error situations.","startedAt":"2008-12-02T21:20:15Z","lastAt":"2025-02-27T01:58:09Z","messageCount":98,"participants":["Robin Rosenberg","Shawn O. Pearce","y@vger.kernel.org","struggleyb.nku@gmail.com","Junio C Hamano","Nazri Ramliy","Bo Yang","pclouds@gmail.com","Nguyen Thai Ngoc Duy","Michał Pomorski","Andrew Garber","Phil Hord","Pavel Volek","NGUYEN Kim Thuat","Matthieu Moy","nguyenki","Kim Thuat NGUYEN","Max Horn","kim-thuat.nguyen@ensimag.imag.fr","Eric Blake","Jeff King","Hilco Wijbenga","y@ensimag.imag.fr","Elia Pinto","Rohit Mani","TamerTas","Stefan Beller","Eric Sunshine","Tamer TAS","Movchan Pavel","Lars Vogel","Takahito Ogawa","小川恭史","Brandon Richardson","Martin Ågren","Jiang Xin"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"96984","messageId":"1228252816-5987-1-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"y","subject":"[JGIT PATCH 1/2] Close a forgotten reference to the HEAD ref.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2008-12-02T21:20:15Z","receivedAt":"2008-12-02T21:20:15Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/Repository.java       |   12 ++++++++++--\n 1 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex c953531..b54afd5 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -989,9 +989,10 @@ public Ref peel(final Ref ref) {\n \t * @return true if HEAD points to a StGit patch.\n \t */\n \tpublic boolean isStGitMode() {\n+\t\tFile file = new File(getDirectory(), \"HEAD\");\n+\t\tBufferedReader reader = null;\n \t\ttry {\n-\t\t\tFile file = new File(getDirectory(), \"HEAD\");\n-\t\t\tBufferedReader reader = new BufferedReader(new FileReader(file));\n+\t\t\treader = new BufferedReader(new FileReader(file));\n \t\t\tString string = reader.readLine();\n \t\t\tif (!string.startsWith(\"ref: refs/heads/\"))\n \t\t\t\treturn false;\n@@ -1007,6 +1008,13 @@ public boolean isStGitMode() {\n \t\t} catch (IOException e) {\n \t\t\te.printStackTrace();\n \t\t\treturn false;\n+\t\t} finally {\n+\t\t\ttry {\n+\t\t\t\tif (reader != null)\n+\t\t\t\t\treader.close();\n+\t\t\t} catch (IOException e1) {\n+\t\t\t\t// nothing to do here\n+\t\t\t}\n \t\t}\n \t}\n \n-- \n1.6.0.3.640.g6331a\n"},{"id":"96983","messageId":"1228252816-5987-2-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1228252816-5987-1-git-send-email-robin.rosenberg@dewire.com","subject":"[JGIT PATCH 2/2] Improve closing of files in error situations.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2008-12-02T21:20:16Z","receivedAt":"2008-12-02T21:20:16Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../exttst/org/spearce/jgit/lib/SpeedTestBase.java |   12 +++++--\n .../src/org/spearce/jgit/lib/Repository.java       |   31 +++++++++++++-------\n 2 files changed, 28 insertions(+), 15 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java b/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\nindex 11f7439..36a5e0e 100644\n--- a/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\n+++ b/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\n@@ -72,10 +72,14 @@\n \tprotected void prepare(String[] refcmd) throws Exception {\n \t\ttry {\n \t\t\tBufferedReader bufferedReader = new BufferedReader(new FileReader(\"kernel.ref\"));\n-\t\t\tkernelrepo = bufferedReader.readLine();\n-\t\t\tbufferedReader.close();\n-\t\t\ttimeNativeGit(kernelrepo, refcmd);\n-\t\t\tnativeTime = timeNativeGit(kernelrepo, refcmd);\n+\t\t\ttry {\n+\t\t\t\tkernelrepo = bufferedReader.readLine();\n+\t\t\t\tbufferedReader.close();\n+\t\t\t\ttimeNativeGit(kernelrepo, refcmd);\n+\t\t\t\tnativeTime = timeNativeGit(kernelrepo, refcmd);\n+\t\t\t} finally {\n+\t\t\t\tbufferedReader.close();\n+\t\t\t}\n \t\t} catch (Exception e) {\n \t\t\tSystem.out.println(\"Create a file named kernel.ref and put the path to the Linux kernels repository there\");\n \t\t\tthrow e;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex b54afd5..da1494f 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -144,10 +144,13 @@ public Repository(final File d) throws IOException {\n \t\tfinal File altFile = FS.resolve(objectsDir, \"info/alternates\");\n \t\tif (altFile.exists()) {\n \t\t\tBufferedReader ar = new BufferedReader(new FileReader(altFile));\n-\t\t\tfor (String alt=ar.readLine(); alt!=null; alt=ar.readLine()) {\n-\t\t\t\treadObjectsDirs(FS.resolve(objectsDir, alt), ret);\n+\t\t\ttry {\n+\t\t\t\tfor (String alt=ar.readLine(); alt!=null; alt=ar.readLine()) {\n+\t\t\t\t\treadObjectsDirs(FS.resolve(objectsDir, alt), ret);\n+\t\t\t\t}\n+\t\t\t} catch (Exception e) {\n+\t\t\t\tar.close();\n \t\t\t}\n-\t\t\tar.close();\n \t\t}\n \t\treturn ret;\n \t}\n@@ -1027,15 +1030,21 @@ public boolean isStGitMode() {\n \t\tif (isStGitMode()) {\n \t\t\tFile patchDir = new File(new File(getDirectory(),\"patches\"),getBranch());\n \t\t\tBufferedReader apr = new BufferedReader(new FileReader(new File(patchDir,\"applied\")));\n-\t\t\tfor (String patchName=apr.readLine(); patchName!=null; patchName=apr.readLine()) {\n-\t\t\t\tFile topFile = new File(new File(new File(patchDir,\"patches\"), patchName), \"top\");\n-\t\t\t\tBufferedReader tfr = new BufferedReader(new FileReader(topFile));\n-\t\t\t\tString objectId = tfr.readLine();\n-\t\t\t\tObjectId id = ObjectId.fromString(objectId);\n-\t\t\t\tret.put(id, new StGitPatch(patchName, id));\n-\t\t\t\ttfr.close();\n+\t\t\ttry {\n+\t\t\t\tfor (String patchName=apr.readLine(); patchName!=null; patchName=apr.readLine()) {\n+\t\t\t\t\tFile topFile = new File(new File(new File(patchDir,\"patches\"), patchName), \"top\");\n+\t\t\t\t\tBufferedReader tfr = new BufferedReader(new FileReader(topFile));\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tString objectId = tfr.readLine();\n+\t\t\t\t\t\tObjectId id = ObjectId.fromString(objectId);\n+\t\t\t\t\t\tret.put(id, new StGitPatch(patchName, id));\n+\t\t\t\t\t} finally {\n+\t\t\t\t\t\ttfr.close();\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t} finally {\n+\t\t\t\tapr.close();\n \t\t\t}\n-\t\t\tapr.close();\n \t\t}\n \t\treturn ret;\n \t}\n-- \n1.6.0.3.640.g6331a\n"},{"id":"96987","messageId":"1228254088-8490-1-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1228252816-5987-2-git-send-email-robin.rosenberg@dewire.com","subject":"[JGIT PATCH 2/2 v2] Improve closing of files in error situations.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2008-12-02T21:41:28Z","receivedAt":"2008-12-02T21:41:28Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../exttst/org/spearce/jgit/lib/SpeedTestBase.java |   12 +++++--\n .../dircache/DirCacheCGitCompatabilityTest.java    |   24 ++++++++++-----\n .../org/spearce/jgit/lib/RepositoryTestCase.java   |   27 +++++++++++------\n .../src/org/spearce/jgit/lib/GitIndex.java         |    2 +-\n .../src/org/spearce/jgit/lib/Repository.java       |   31 +++++++++++++-------\n 5 files changed, 63 insertions(+), 33 deletions(-)\n\nI was a bit quick there. This is an extended version of patch 2/2.\n\n-- robin\n\ndiff --git a/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java b/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\nindex 11f7439..36a5e0e 100644\n--- a/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\n+++ b/org.spearce.jgit.test/exttst/org/spearce/jgit/lib/SpeedTestBase.java\n@@ -72,10 +72,14 @@\n \tprotected void prepare(String[] refcmd) throws Exception {\n \t\ttry {\n \t\t\tBufferedReader bufferedReader = new BufferedReader(new FileReader(\"kernel.ref\"));\n-\t\t\tkernelrepo = bufferedReader.readLine();\n-\t\t\tbufferedReader.close();\n-\t\t\ttimeNativeGit(kernelrepo, refcmd);\n-\t\t\tnativeTime = timeNativeGit(kernelrepo, refcmd);\n+\t\t\ttry {\n+\t\t\t\tkernelrepo = bufferedReader.readLine();\n+\t\t\t\tbufferedReader.close();\n+\t\t\t\ttimeNativeGit(kernelrepo, refcmd);\n+\t\t\t\tnativeTime = timeNativeGit(kernelrepo, refcmd);\n+\t\t\t} finally {\n+\t\t\t\tbufferedReader.close();\n+\t\t\t}\n \t\t} catch (Exception e) {\n \t\t\tSystem.out.println(\"Create a file named kernel.ref and put the path to the Linux kernels repository there\");\n \t\t\tthrow e;\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheCGitCompatabilityTest.java\nindex b052686..42832fe 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheCGitCompatabilityTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheCGitCompatabilityTest.java\n@@ -146,10 +146,14 @@ private File pathOf(final String name) {\n \t\tfinal LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<String, CGitIndexRecord>();\n \t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n \t\t\t\tnew FileInputStream(pathOf(\"gitgit.lsfiles\")), \"UTF-8\"));\n-\t\tString line;\n-\t\twhile ((line = br.readLine()) != null) {\n-\t\t\tfinal CGitIndexRecord cr = new CGitIndexRecord(line);\n-\t\t\tr.put(cr.path, cr);\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal CGitIndexRecord cr = new CGitIndexRecord(line);\n+\t\t\t\tr.put(cr.path, cr);\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n \t\t}\n \t\treturn r;\n \t}\n@@ -158,10 +162,14 @@ private File pathOf(final String name) {\n \t\tfinal LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<String, CGitLsTreeRecord>();\n \t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n \t\t\t\tnew FileInputStream(pathOf(\"gitgit.lstree\")), \"UTF-8\"));\n-\t\tString line;\n-\t\twhile ((line = br.readLine()) != null) {\n-\t\t\tfinal CGitLsTreeRecord cr = new CGitLsTreeRecord(line);\n-\t\t\tr.put(cr.path, cr);\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal CGitLsTreeRecord cr = new CGitLsTreeRecord(line);\n+\t\t\t\tr.put(cr.path, cr);\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n \t\t}\n \t\treturn r;\n \t}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\nindex 22bf395..8937145 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n@@ -152,14 +152,20 @@ private static void reportDeleteFailure(final String name,\n \tprotected static void copyFile(final File src, final File dst)\n \t\t\tthrows IOException {\n \t\tfinal FileInputStream fis = new FileInputStream(src);\n-\t\tfinal FileOutputStream fos = new FileOutputStream(dst);\n-\t\tfinal byte[] buf = new byte[4096];\n-\t\tint r;\n-\t\twhile ((r = fis.read(buf)) > 0) {\n-\t\t\tfos.write(buf, 0, r);\n+\t\ttry {\n+\t\t\tfinal FileOutputStream fos = new FileOutputStream(dst);\n+\t\t\ttry {\n+\t\t\t\tfinal byte[] buf = new byte[4096];\n+\t\t\t\tint r;\n+\t\t\t\twhile ((r = fis.read(buf)) > 0) {\n+\t\t\t\t\tfos.write(buf, 0, r);\n+\t\t\t\t}\n+\t\t\t} finally {\n+\t\t\t\tfos.close();\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tfis.close();\n \t\t}\n-\t\tfis.close();\n-\t\tfos.close();\n \t}\n \n \tprotected File writeTrashFile(final String name, final String data)\n@@ -170,8 +176,11 @@ protected File writeTrashFile(final String name, final String data)\n \t\t\tthrow new Error(\"Could not create directory \" + tf.getParentFile());\n \t\tfinal OutputStreamWriter fw = new OutputStreamWriter(\n \t\t\t\tnew FileOutputStream(tf), \"UTF-8\");\n-\t\tfw.write(data);\n-\t\tfw.close();\n+\t\ttry {\n+\t\t\tfw.write(data);\n+\t\t} finally {\n+\t\t\tfw.close();\n+\t\t}\n \t\treturn tf;\n \t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\nindex bafddef..7ff6754 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n@@ -547,8 +547,8 @@ public boolean isModified(File wd, boolean forceContentCheck) {\n \n \t\t\t\ttry {\n \t\t\t\t\tInputStream is = new FileInputStream(file);\n-\t\t\t\t\tObjectWriter objectWriter = new ObjectWriter(db);\n \t\t\t\t\ttry {\n+\t\t\t\t\t\tObjectWriter objectWriter = new ObjectWriter(db);\n \t\t\t\t\t\tObjectId newId = objectWriter.computeBlobSha1(file\n \t\t\t\t\t\t\t\t.length(), is);\n \t\t\t\t\t\tboolean ret = !newId.equals(sha1);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex b54afd5..da1494f 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -144,10 +144,13 @@ public Repository(final File d) throws IOException {\n \t\tfinal File altFile = FS.resolve(objectsDir, \"info/alternates\");\n \t\tif (altFile.exists()) {\n \t\t\tBufferedReader ar = new BufferedReader(new FileReader(altFile));\n-\t\t\tfor (String alt=ar.readLine(); alt!=null; alt=ar.readLine()) {\n-\t\t\t\treadObjectsDirs(FS.resolve(objectsDir, alt), ret);\n+\t\t\ttry {\n+\t\t\t\tfor (String alt=ar.readLine(); alt!=null; alt=ar.readLine()) {\n+\t\t\t\t\treadObjectsDirs(FS.resolve(objectsDir, alt), ret);\n+\t\t\t\t}\n+\t\t\t} catch (Exception e) {\n+\t\t\t\tar.close();\n \t\t\t}\n-\t\t\tar.close();\n \t\t}\n \t\treturn ret;\n \t}\n@@ -1027,15 +1030,21 @@ public boolean isStGitMode() {\n \t\tif (isStGitMode()) {\n \t\t\tFile patchDir = new File(new File(getDirectory(),\"patches\"),getBranch());\n \t\t\tBufferedReader apr = new BufferedReader(new FileReader(new File(patchDir,\"applied\")));\n-\t\t\tfor (String patchName=apr.readLine(); patchName!=null; patchName=apr.readLine()) {\n-\t\t\t\tFile topFile = new File(new File(new File(patchDir,\"patches\"), patchName), \"top\");\n-\t\t\t\tBufferedReader tfr = new BufferedReader(new FileReader(topFile));\n-\t\t\t\tString objectId = tfr.readLine();\n-\t\t\t\tObjectId id = ObjectId.fromString(objectId);\n-\t\t\t\tret.put(id, new StGitPatch(patchName, id));\n-\t\t\t\ttfr.close();\n+\t\t\ttry {\n+\t\t\t\tfor (String patchName=apr.readLine(); patchName!=null; patchName=apr.readLine()) {\n+\t\t\t\t\tFile topFile = new File(new File(new File(patchDir,\"patches\"), patchName), \"top\");\n+\t\t\t\t\tBufferedReader tfr = new BufferedReader(new FileReader(topFile));\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tString objectId = tfr.readLine();\n+\t\t\t\t\t\tObjectId id = ObjectId.fromString(objectId);\n+\t\t\t\t\t\tret.put(id, new StGitPatch(patchName, id));\n+\t\t\t\t\t} finally {\n+\t\t\t\t\t\ttfr.close();\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t} finally {\n+\t\t\t\tapr.close();\n \t\t\t}\n-\t\t\tapr.close();\n \t\t}\n \t\treturn ret;\n \t}\n-- \n1.6.0.3.640.g6331a\n"},{"id":"115728","messageId":"1244405951-21808-1-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"y","subject":"[EGIT PATCH 0/3] Ref log reader","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-07T20:19:08Z","receivedAt":"2009-06-07T20:19:08Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Initially this is needed to the next revised set of rename tests. I have\nnot optimized much for performance.\n\n-- robin\n\nRobin Rosenberg (3):\n  Assert the name and origName properties of Ref objects\n  Add methods to RawParseUtils for scanning backwards.\n  Add a ref log reader class\n\n .../tst/org/spearce/jgit/lib/RefTest.java          |   11 ++\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  153 ++++++++++++++++\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  186 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |    9 +\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   84 +++++++++-\n 5 files changed, 442 insertions(+), 1 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n"},{"id":"115730","messageId":"1244405951-21808-2-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1244405951-21808-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 1/3] Assert the name and origName properties of Ref objects","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-07T20:19:09Z","receivedAt":"2009-06-07T20:19:09Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefTest.java          |   11 +++++++++++\n 1 files changed, 11 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\nindex 440686d..fabbe7e 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n@@ -144,4 +144,15 @@ public void testReadSimplePackedRefSameRepo() throws IOException {\n \t\tassertEquals(Storage.LOOSE_PACKED, ref.getStorage());\n \t}\n \n+\tpublic void testOrigResolvedNamesBranch() throws IOException {\n+\t\tRef ref = db.getRef(\"a\");\n+\t\tassertEquals(\"refs/heads/a\", ref.getName());\n+\t\tassertEquals(\"refs/heads/a\", ref.getOrigName());\n+\t}\n+\n+\tpublic void testOrigResolvedNamesSymRef() throws IOException {\n+\t\tRef ref = db.getRef(\"HEAD\");\n+\t\tassertEquals(\"refs/heads/master\", ref.getName());\n+\t\tassertEquals(\"HEAD\", ref.getOrigName());\n+\t}\n }\n-- \n1.6.3.2.199.g7340d\n"},{"id":"115729","messageId":"1244405951-21808-3-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1244405951-21808-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 2/3] Add methods to RawParseUtils for scanning backwards.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-07T20:19:10Z","receivedAt":"2009-06-07T20:19:10Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   84 +++++++++++++++++++-\n 1 files changed, 83 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\nindex 79ebe41..bdd6a11 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n@@ -321,6 +321,67 @@ public static final int nextLF(final byte[] b, int ptr, final char chrA) {\n \t}\n \n \t/**\n+\t * Locate the first position before a given character.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before chrA, -1 for not found\n+\t */\n+\tpublic static final int prev(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tif (b[ptr--] == chrA)\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n+\t * Locate the first position before the previous LF.\n+\t * <p>\n+\t * This method stops on the first '\\n' it finds.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for LF at.\n+\t * @return new position just before the first LF found, -1 for not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr) {\n+\t\treturn prev(b, ptr, '\\n');\n+\t}\n+\n+\t/**\n+\t * Locate the previous position before either the given character or LF.\n+\t * <p>\n+\t * This method stops on the first match it finds from either chrA or '\\n'.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA or LF at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before the first chrA or LF to be found, -1 for\n+\t *         not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tfinal byte c = b[ptr--];\n+\t\t\tif (c == chrA || c == '\\n')\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n \t * Index the region between <code>[ptr, end)</code> to find line starts.\n \t * <p>\n \t * The returned list is 1 indexed. Index 0 contains\n@@ -519,7 +580,28 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t *         after decoding the region through the specified character set.\n \t */\n \tpublic static String decode(final byte[] buffer) {\n-\t\treturn decode(Constants.CHARSET, buffer, 0, buffer.length);\n+\t\treturn decode(buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n+\t * Decode a buffer under UTF-8, if possible.\n+\t * \n+\t * If the byte stream cannot be decoded that way, the platform default is\n+\t * tried and if that too fails, the fail-safe ISO-8859-1 encoding is tried.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @param start\n+\t *            start position in buffer\n+\t * @param end\n+\t *            one position past the last location within the buffer to take\n+\t *            data from.\n+\t * @return a string representation of the range <code>[start,end)</code>,\n+\t *         after decoding the region through the specified character set.\n+\t */\n+\tpublic static String decode(final byte[] buffer, final int start,\n+\t\t\tfinal int end) {\n+\t\treturn decode(Constants.CHARSET, buffer, start, end);\n \t}\n \n \t/**\n-- \n1.6.3.2.199.g7340d\n"},{"id":"115727","messageId":"1244405951-21808-4-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1244405951-21808-3-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 3/3] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-07T20:19:11Z","receivedAt":"2009-06-07T20:19:11Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  153 ++++++++++++++++\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  186 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |    9 +\n 3 files changed, 348 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\nnew file mode 100644\nindex 0000000..1919477\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n@@ -0,0 +1,153 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileNotFoundException;\n+import java.io.FileOutputStream;\n+import java.io.IOException;\n+import java.text.SimpleDateFormat;\n+import java.util.Date;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.ReflogReader.Entry;\n+\n+public class ReflogReaderTest extends RepositoryTestCase {\n+\n+\tstatic byte[] oneLine = \"da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c Robin Rosenberg <robin.rosenberg@dewire.com> 1243028200 +0200\\tcommit: Add a toString for debugging to RemoteRefUpdate\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLine = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] aLine = \"1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to a\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] masterLine = \"2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to master\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] headLine = \"3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to HEAD\\n\"\n+\t\t\t.getBytes();\n+\n+\tpublic void testReadOneLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", oneLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tEntry e = reader.getLastEntry();\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"da85355dfc525c9f6f3927b876f379f46ccf826e\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"3e7549db262d1e836d9bf0af7e22355468f1717c\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Robin Rosenberg\", e.getWho().getName());\n+\t\tassertEquals(\"robin.rosenberg@dewire.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(120, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:40\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"commit\", e.getAction());\n+\t\tassertEquals(\"Add a toString for debugging to RemoteRefUpdate\", e\n+\t\t\t\t.getComment());\n+\t}\n+\n+\tprivate String iso(Date d) {\n+\t\treturn new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\").format(d);\n+\t}\n+\n+\tpublic void testReadTwoLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:42\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"rebase finished\", e.getAction());\n+\t\tassertEquals(\n+\t\t\t\t\"refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\n+\t\te = reverseEntries.get(1);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"0000000000000000000000000000000000000000\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"thor@committer.au\", e.getWho().getEmailAddress());\n+\t\tassertEquals(-60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:41\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"branch\", e.getAction());\n+\t\tassertEquals(\"Created from rr/renamebranchv4\", e.getComment());\n+\t}\n+\n+\tpublic void testReadRightLog() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/a\", aLine);\n+\t\tsetupReflog(\"logs/refs/heads/master\", masterLine);\n+\t\tsetupReflog(\"logs/HEAD\", headLine);\n+\t\tassertEquals(\"change to master\", db.getReflogReader(\"master\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"change to a\", db.getReflogReader(\"a\").getLastEntry()\n+\t\t\t\t.getComment());\n+\t\tassertEquals(\"change to HEAD\", db.getReflogReader(\"HEAD\").getLastEntry()\n+\t\t\t\t.getComment());\n+\t}\n+\n+\tpublic void testNoLog() throws Exception {\n+\t\tassertEquals(0, db.getReflogReader(\"master\").getReverseEntries().size());\n+\t\tassertNull(db.getReflogReader(\"master\").getLastEntry());\n+\t}\n+\n+\tprivate void setupReflog(String logName, byte[] data)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\tFile logfile = new File(db.getDirectory(), logName);\n+\t\tlogfile.getParentFile().mkdirs();\n+\t\tFileOutputStream fileOutputStream = new FileOutputStream(logfile);\n+\t\tfileOutputStream.write(data);\n+\t\tfileOutputStream.close();\n+\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\nnew file mode 100644\nindex 0000000..15591af\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n@@ -0,0 +1,186 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/**\n+ * Utility for reading reflog entries\n+ */\n+public class ReflogReader {\n+\t/**\n+\t * Parsed reflog entry\n+\t */\n+\tstatic public class Entry {\n+\t\tEntry(byte[] raw, int pos) {\n+\t\t\toldId = ObjectId.fromString(raw, pos);\n+\t\t\tif (raw[pos + Constants.OBJECT_ID_LENGTH * 2] != ' ')\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tnewId = ObjectId.fromString(raw, pos + Constants.OBJECT_ID_LENGTH\n+\t\t\t\t\t* 2 + 1);\n+\t\t\tif (raw[pos + Constants.OBJECT_ID_LENGTH * 2 * 2 + 1] != ' ')\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\twho = RawParseUtils.parsePersonIdent(raw, pos\n+\t\t\t\t\t+ Constants.OBJECT_ID_LENGTH * 2 * 2 + 2);\n+\t\t\tint p0 = RawParseUtils.next(raw, pos + Constants.OBJECT_ID_LENGTH\n+\t\t\t\t\t* 2 * 2 + 2, '\\t');\n+\t\t\tif (p0 == -1)\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tint p1 = RawParseUtils.next(raw, p0 + 1, ':');\n+\t\t\tif (p1 == -1)\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\taction = RawParseUtils.decode(raw, p0, p1-1).trim();\n+\t\t\tint p2 = RawParseUtils.nextLF(raw, p1 + 1);\n+\t\t\tif (p2 == -1)\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tcomment = RawParseUtils.decode(raw, p1, p2).trim();\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id before the change\n+\t\t */\n+\t\tpublic ObjectId getOldId() {\n+\t\t\treturn oldId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id after the change\n+\t\t */\n+\t\tpublic ObjectId getNewId() {\n+\t\t\treturn newId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return user performin the change\n+\t\t */\n+\t\tpublic PersonIdent getWho() {\n+\t\t\treturn who;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return some kind of textual indication of the change made\n+\t\t */\n+\t\tpublic String getAction() {\n+\t\t\treturn action;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return textual description of the change\n+\t\t */\n+\t\tpublic String getComment() {\n+\t\t\treturn comment;\n+\t\t}\n+\n+\t\tprivate ObjectId oldId;\n+\n+\t\tprivate ObjectId newId;\n+\n+\t\tprivate PersonIdent who;\n+\n+\t\tprivate String action;\n+\n+\t\tprivate String comment;\n+\t}\n+\n+\tprivate File logName;\n+\n+\tReflogReader(Repository db, String refname) {\n+\t\tlogName = new File(db.getDirectory(), \"logs/\" + refname);\n+\t}\n+\n+\t/**\n+\t * Get the last entry in the reflog\n+\t * \n+\t * @return the latest reflog entry, or null if no log\n+\t * @throws IOException\n+\t */\n+\tpublic Entry getLastEntry() throws IOException {\n+\t\tList<Entry> entries = getReverseEntries(1);\n+\t\treturn entries.size() > 0 ? entries.get(0) : null;\n+\t}\n+\n+\t/**\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries() throws IOException {\n+\t\treturn getReverseEntries(Integer.MAX_VALUE);\n+\t}\n+\n+\t/**\n+\t * @param max\n+\t *            max numer of entries to read\n+\t * @return all reglog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries(int max) throws IOException {\n+\t\tFileInputStream fileInputStream;\n+\t\ttry {\n+\t\t\tfileInputStream = new FileInputStream(logName);\n+\t\t} catch (FileNotFoundException e) {\n+\t\t\treturn Collections.emptyList();\n+\t\t}\n+\t\tif (logName.length() > Integer.MAX_VALUE)\n+\t\t\t// implementation limit, will suck with smaller files too\n+\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n+\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n+\t\tbyte[] log = new byte[(int) logName.length()];\n+\t\tNB.readFully(fileInputStream, log, 0, log.length);\n+\t\tint rs = log.length - 2; // skip terminating \\n\n+\t\tList<Entry> ret = new ArrayList<Entry>();\n+\t\twhile (rs >= 0 && max-- > 0) {\n+\t\t\trs = RawParseUtils.prevLF(log, rs);\n+\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n+\t\t\tret.add(entry);\n+\t\t}\n+\t\treturn ret;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 5def5d3..89dc044 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -1089,4 +1089,13 @@ public String shortenRefName(String refName) {\n \t\t\treturn refName.substring(Constants.R_REMOTES.length());\n \t\treturn refName;\n \t}\n+\n+\t/**\n+\t * @param refName\n+\t * @return a {@link ReflogReader} for the refname\n+\t * @throws IOException \n+\t */\n+\tpublic ReflogReader getReflogReader(String refName) throws IOException {\n+\t\treturn new ReflogReader(this, getRef(refName).getOrigName());\n+\t}\n }\n-- \n1.6.3.2.199.g7340d\n"},{"id":"115746","messageId":"20090607222154.GD16497@spearce.org","threadId":"16566","inReplyTo":"1244405951-21808-4-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 3/3] Add a ref log reader class","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-07T22:21:54Z","receivedAt":"2009-06-07T22:21:54Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n> new file mode 100644\n> index 0000000..15591af\n> --- /dev/null\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n> @@ -0,0 +1,186 @@\n...\n> +public class ReflogReader {\n> +\t/**\n> +\t * Parsed reflog entry\n> +\t */\n> +\tstatic public class Entry {\n> +\t\tEntry(byte[] raw, int pos) {\n> +\t\t\toldId = ObjectId.fromString(raw, pos);\n> +\t\t\tif (raw[pos + Constants.OBJECT_ID_LENGTH * 2] != ' ')\n> +\t\t\t\tthrow new IllegalArgumentException(\n> +\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n\nPlease, for the sake of everyone's sanity, increment pos after you\nread oldId.  Then its just raw[pos++] != ' ' here, and the next\nline for newId is even shorter.\n\n> +\t\t\tint p1 = RawParseUtils.next(raw, p0 + 1, ':');\n> +\t\t\tif (p1 == -1)\n> +\t\t\t\tthrow new IllegalArgumentException(\n> +\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n\nTechnically, missing a ':' is legal.  Everything after the '\\t'\nis the comment.  It may be splittable into an action/comment,\nit might not be.\n\n> +\t\t\taction = RawParseUtils.decode(raw, p0, p1-1).trim();\n> +\t\t\tint p2 = RawParseUtils.nextLF(raw, p1 + 1);\n> +\t\t\tif (p2 == -1)\n> +\t\t\t\tthrow new IllegalArgumentException(\n> +\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n> +\t\t\tcomment = RawParseUtils.decode(raw, p1, p2).trim();\n\ntrim() on these shouldn't be necessary.  If you parse the line\nright, you can avoid handing the '\\t' and the '\\n' to the decode,\nand then whatever is left is what was given to the logger, and we\nshould faithfully return that to the caller.  Its unlikely to have\nunncessary whitespace, so the trim is just wasting CPU looking\nfor it.\n\n> +\t\tprivate ObjectId oldId;\n> +\n> +\t\tprivate ObjectId newId;\n> +\n> +\t\tprivate PersonIdent who;\n> +\n> +\t\tprivate String action;\n> +\n> +\t\tprivate String comment;\n\nStyle nit: I much prefer fields to appear before the constructor.\n\n> +\t}\n...\n> +\tpublic List<Entry> getReverseEntries(int max) throws IOException {\n> +\t\tFileInputStream fileInputStream;\n> +\t\ttry {\n> +\t\t\tfileInputStream = new FileInputStream(logName);\n> +\t\t} catch (FileNotFoundException e) {\n> +\t\t\treturn Collections.emptyList();\n> +\t\t}\n\nPlease ensure fileInputStream doesn't leak and is closed before\nthis method block returns.\n\n> +\t\tif (logName.length() > Integer.MAX_VALUE)\n> +\t\t\t// implementation limit, will suck with smaller files too\n> +\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n> +\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n\nStyle nit: Please wrap this huge block in {}, its easier to read\nwhen there are more than one line dangling below.\n\n> +\t\tbyte[] log = new byte[(int) logName.length()];\n\nPlease use fileInputStream.getChannel().size() as it does an fstat()\nrather than a stat() on the path.\n\nBut I'd rather just read the file backwards, with a RandomAccessFile\nand what amounts to a reverse version of BufferedInputStream.\n\n> +\t\tNB.readFully(fileInputStream, log, 0, log.length);\n> +\t\tint rs = log.length - 2; // skip terminating \\n\n\nIf the file is currently being appended to, it might not end in\n'\\n'.  Something to keep in mind.\n\n-- \nShawn.\n"},{"id":"115751","messageId":"200906080045.26927.robin.rosenberg.lists@dewire.com","threadId":"16566","inReplyTo":"20090607222154.GD16497@spearce.org","subject":"Re: [EGIT PATCH 3/3] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-06-07T22:45:26Z","receivedAt":"2009-06-07T22:45:26Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"måndag 08 juni 2009 00:21:54 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n...\n> > +\t\t\tint p1 = RawParseUtils.next(raw, p0 + 1, ':');\n> > +\t\t\tif (p1 == -1)\n> > +\t\t\t\tthrow new IllegalArgumentException(\n> > +\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n> \n> Technically, missing a ':' is legal.  Everything after the '\\t'\n> is the comment.  It may be splittable into an action/comment,\n> it might not be.\n\nDo you think I should just skip parsing out action? I don't really need it. I can\ngo with everything after tab as one string for my purposes, i.e. reading reflogs\nin JUnit tests.\n\nAs for optimized reading, I'd rather spend my time on something else. Reading\nreflogs won't likely be a real problem and I think the interface will be stable\neven if it needs to be optimized.\n\nThe other stuff I'll fix.\n\n-- robin\n"},{"id":"115752","messageId":"20090607224754.GF16497@spearce.org","threadId":"16566","inReplyTo":"200906080045.26927.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 3/3] Add a ref log reader class","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-07T22:47:54Z","receivedAt":"2009-06-07T22:47:54Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> m?ndag 08 juni 2009 00:21:54 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> > Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> ...\n> > > +\t\t\tint p1 = RawParseUtils.next(raw, p0 + 1, ':');\n> > > +\t\t\tif (p1 == -1)\n> > > +\t\t\t\tthrow new IllegalArgumentException(\n> > > +\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n> > \n> > Technically, missing a ':' is legal.  Everything after the '\\t'\n> > is the comment.  It may be splittable into an action/comment,\n> > it might not be.\n> \n> Do you think I should just skip parsing out action? I don't really need it. I can\n> go with everything after tab as one string for my purposes, i.e. reading reflogs\n> in JUnit tests.\n\nYea, just skip it.\n \n> As for optimized reading, I'd rather spend my time on something else. Reading\n> reflogs won't likely be a real problem and I think the interface will be stable\n> even if it needs to be optimized.\n> \n> The other stuff I'll fix.\n\nOK, sounds fine to me.\n\nMaybe we should cap the limit at say 20 MiB of log or something, and\nrefuse to read anything more than that, rather than allowing 2 GiB.\n\nOr, since we most likely only care about the tail, if its over 5\nMiB, skip through to the end and then read the last 5 MiB, and if we\nhave a partial entry left over at the start of the buffer, ignore it.\n\n-- \nShawn.\n"},{"id":"115824","messageId":"1244482112-5935-1-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"20090607224754.GF16497@spearce.org","subject":"[EGIT PATCH 1/2] Add methods to RawParseUtils for scanning backwards.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-08T17:28:31Z","receivedAt":"2009-06-08T17:28:31Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   84 +++++++++++++++++++-\n 1 files changed, 83 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\nindex 79ebe41..bdd6a11 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n@@ -321,6 +321,67 @@ public static final int nextLF(final byte[] b, int ptr, final char chrA) {\n \t}\n \n \t/**\n+\t * Locate the first position before a given character.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before chrA, -1 for not found\n+\t */\n+\tpublic static final int prev(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tif (b[ptr--] == chrA)\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n+\t * Locate the first position before the previous LF.\n+\t * <p>\n+\t * This method stops on the first '\\n' it finds.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for LF at.\n+\t * @return new position just before the first LF found, -1 for not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr) {\n+\t\treturn prev(b, ptr, '\\n');\n+\t}\n+\n+\t/**\n+\t * Locate the previous position before either the given character or LF.\n+\t * <p>\n+\t * This method stops on the first match it finds from either chrA or '\\n'.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA or LF at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before the first chrA or LF to be found, -1 for\n+\t *         not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tfinal byte c = b[ptr--];\n+\t\t\tif (c == chrA || c == '\\n')\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n \t * Index the region between <code>[ptr, end)</code> to find line starts.\n \t * <p>\n \t * The returned list is 1 indexed. Index 0 contains\n@@ -519,7 +580,28 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t *         after decoding the region through the specified character set.\n \t */\n \tpublic static String decode(final byte[] buffer) {\n-\t\treturn decode(Constants.CHARSET, buffer, 0, buffer.length);\n+\t\treturn decode(buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n+\t * Decode a buffer under UTF-8, if possible.\n+\t * \n+\t * If the byte stream cannot be decoded that way, the platform default is\n+\t * tried and if that too fails, the fail-safe ISO-8859-1 encoding is tried.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @param start\n+\t *            start position in buffer\n+\t * @param end\n+\t *            one position past the last location within the buffer to take\n+\t *            data from.\n+\t * @return a string representation of the range <code>[start,end)</code>,\n+\t *         after decoding the region through the specified character set.\n+\t */\n+\tpublic static String decode(final byte[] buffer, final int start,\n+\t\t\tfinal int end) {\n+\t\treturn decode(Constants.CHARSET, buffer, start, end);\n \t}\n \n \t/**\n-- \n1.6.3.2.199.g7340d\n"},{"id":"115825","messageId":"1244482112-5935-2-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1244482112-5935-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 2/2] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-08T17:28:32Z","receivedAt":"2009-06-08T17:28:32Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"This reader allows the caller to the reflog entries for a given ref.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  187 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  181 +++++++++++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |    9 +\n 3 files changed, 377 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\nnew file mode 100644\nindex 0000000..3e1946e\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n@@ -0,0 +1,187 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileNotFoundException;\n+import java.io.FileOutputStream;\n+import java.io.IOException;\n+import java.text.SimpleDateFormat;\n+import java.util.Date;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.ReflogReader.Entry;\n+\n+public class ReflogReaderTest extends RepositoryTestCase {\n+\n+\tstatic byte[] oneLine = \"da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c Robin Rosenberg <robin.rosenberg@dewire.com> 1243028200 +0200\\tcommit: Add a toString for debugging to RemoteRefUpdate\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLine = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLineWithAppendInProgress = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\"\n+\t\t\t+ \"54794942a18a237c57a80719afed44bb78172b10 \")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] aLine = \"1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to a\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] masterLine = \"2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to master\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] headLine = \"3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to HEAD\\n\"\n+\t\t\t.getBytes();\n+\n+\tpublic void testReadOneLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", oneLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tEntry e = reader.getLastEntry();\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"da85355dfc525c9f6f3927b876f379f46ccf826e\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"3e7549db262d1e836d9bf0af7e22355468f1717c\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Robin Rosenberg\", e.getWho().getName());\n+\t\tassertEquals(\"robin.rosenberg@dewire.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(120, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:40\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"commit: Add a toString for debugging to RemoteRefUpdate\",\n+\t\t\t\te.getComment());\n+\t}\n+\n+\tprivate String iso(Date d) {\n+\t\treturn new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\").format(d);\n+\t}\n+\n+\tpublic void testReadTwoLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:42\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\n+\t\te = reverseEntries.get(1);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"0000000000000000000000000000000000000000\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"thor@committer.au\", e.getWho().getEmailAddress());\n+\t\tassertEquals(-60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:41\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"branch: Created from rr/renamebranchv4\", e.getComment());\n+\t}\n+\n+\tpublic void testReadWhileAppendIsInProgress() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLineWithAppendInProgress);\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:42\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\t\t// while similar to testReadTwoLine, we can assume that if we get the last entry\n+\t\t// right, everything else is too\n+\t}\n+\n+\n+\tpublic void testReadRightLog() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/a\", aLine);\n+\t\tsetupReflog(\"logs/refs/heads/master\", masterLine);\n+\t\tsetupReflog(\"logs/HEAD\", headLine);\n+\t\tassertEquals(\"branch: change to master\", db.getReflogReader(\"master\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to a\", db.getReflogReader(\"a\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to HEAD\", db.getReflogReader(\"HEAD\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t}\n+\n+\tpublic void testNoLog() throws Exception {\n+\t\tassertEquals(0, db.getReflogReader(\"master\").getReverseEntries().size());\n+\t\tassertNull(db.getReflogReader(\"master\").getLastEntry());\n+\t}\n+\n+\tprivate void setupReflog(String logName, byte[] data)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\tFile logfile = new File(db.getDirectory(), logName);\n+\t\tif (!logfile.getParentFile().mkdirs()\n+\t\t\t\t&& !logfile.getParentFile().isDirectory()) {\n+\t\t\tthrow new IOException(\n+\t\t\t\t\t\"oops, cannot create the directory for the test reflog file\"\n+\t\t\t\t\t\t\t+ logfile);\n+\t\t}\n+\t\tFileOutputStream fileOutputStream = new FileOutputStream(logfile);\n+\t\ttry {\n+\t\t\tfileOutputStream.write(data);\n+\t\t} finally {\n+\t\t\tfileOutputStream.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\nnew file mode 100644\nindex 0000000..73a5e0c\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n@@ -0,0 +1,181 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/**\n+ * Utility for reading reflog entries\n+ */\n+public class ReflogReader {\n+\t/**\n+\t * Parsed reflog entry\n+\t */\n+\tstatic public class Entry {\n+\t\tprivate ObjectId oldId;\n+\n+\t\tprivate ObjectId newId;\n+\n+\t\tprivate PersonIdent who;\n+\n+\t\tprivate String comment;\n+\n+\t\tEntry(byte[] raw, int pos) {\n+\t\t\toldId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ')\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tnewId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ') {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\twho = RawParseUtils.parsePersonIdent(raw, pos);\n+\t\t\tint p0 = RawParseUtils.next(raw, pos, '\\t'); // personident has no\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// \\t\n+\t\t\tif (p0 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tint p1 = RawParseUtils.nextLF(raw, p0);\n+\t\t\tif (p1 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tcomment = RawParseUtils.decode(raw, p0, p1 - 1);\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id before the change\n+\t\t */\n+\t\tpublic ObjectId getOldId() {\n+\t\t\treturn oldId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id after the change\n+\t\t */\n+\t\tpublic ObjectId getNewId() {\n+\t\t\treturn newId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return user performin the change\n+\t\t */\n+\t\tpublic PersonIdent getWho() {\n+\t\t\treturn who;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return textual description of the change\n+\t\t */\n+\t\tpublic String getComment() {\n+\t\t\treturn comment;\n+\t\t}\n+\t}\n+\n+\tprivate File logName;\n+\n+\tReflogReader(Repository db, String refname) {\n+\t\tlogName = new File(db.getDirectory(), \"logs/\" + refname);\n+\t}\n+\n+\t/**\n+\t * Get the last entry in the reflog\n+\t * \n+\t * @return the latest reflog entry, or null if no log\n+\t * @throws IOException\n+\t */\n+\tpublic Entry getLastEntry() throws IOException {\n+\t\tList<Entry> entries = getReverseEntries(1);\n+\t\treturn entries.size() > 0 ? entries.get(0) : null;\n+\t}\n+\n+\t/**\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries() throws IOException {\n+\t\treturn getReverseEntries(Integer.MAX_VALUE);\n+\t}\n+\n+\t/**\n+\t * @param max\n+\t *            max numer of entries to read\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries(int max) throws IOException {\n+\t\tFileInputStream fileInputStream;\n+\t\ttry {\n+\t\t\tfileInputStream = new FileInputStream(logName);\n+\t\t} catch (FileNotFoundException e) {\n+\t\t\treturn Collections.emptyList();\n+\t\t}\n+\t\ttry {\n+\t\t\tlong logSize = fileInputStream.getChannel().size();\n+\t\t\tif (logSize > Integer.MAX_VALUE) {\n+\t\t\t\t// implementation limit, will suck with smaller files too\n+\t\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n+\t\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n+\t\t\t}\n+\t\t\tbyte[] log = new byte[(int) logSize];\n+\t\t\tNB.readFully(fileInputStream, log, 0, log.length);\n+\t\t\tint rs = RawParseUtils.prevLF(log, log.length);\n+\t\t\tList<Entry> ret = new ArrayList<Entry>();\n+\t\t\twhile (rs >= 0 && max-- > 0) {\n+\t\t\t\trs = RawParseUtils.prevLF(log, rs);\n+\t\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n+\t\t\t\tret.add(entry);\n+\t\t\t}\n+\t\t\treturn ret;\n+\t\t} finally {\n+\t\t\tfileInputStream.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 5def5d3..89dc044 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -1089,4 +1089,13 @@ public String shortenRefName(String refName) {\n \t\t\treturn refName.substring(Constants.R_REMOTES.length());\n \t\treturn refName;\n \t}\n+\n+\t/**\n+\t * @param refName\n+\t * @return a {@link ReflogReader} for the refname\n+\t * @throws IOException \n+\t */\n+\tpublic ReflogReader getReflogReader(String refName) throws IOException {\n+\t\treturn new ReflogReader(this, getRef(refName).getOrigName());\n+\t}\n }\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116177","messageId":"20090612195251.GQ16497@spearce.org","threadId":"16566","inReplyTo":"1244482112-5935-2-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 2/2] Add a ref log reader class","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-12T19:52:52Z","receivedAt":"2009-06-12T19:52:52Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> This reader allows the caller to the reflog entries for a given ref.\n> \n> Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n> ---\n>  .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  187 ++++++++++++++++++++\n>  .../src/org/spearce/jgit/lib/ReflogReader.java     |  181 +++++++++++++++++++\n>  .../src/org/spearce/jgit/lib/Repository.java       |    9 +\n>  3 files changed, 377 insertions(+), 0 deletions(-)\n>  create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n>  create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\nTests fail, I suspect time zone failure (I am in PST):\n\n  ComparisonFailure: expected:<2009-05-22T[23]:36:40> but was:<2009-05-22T[14]:36:40>\n  at org.spearce.jgit.lib.ReflogReaderTest.testReadOneLine(ReflogReaderTest.java:86)\n\nOtherwise, it looks sane to me.\n\n-- \nShawn.\n"},{"id":"116358","messageId":"1245101136-31874-1-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"20090612195251.GQ16497@spearce.org","subject":"[EGIT PATCH 1/2] Use a UTC relative time zone for PersonIdent","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-15T21:25:35Z","receivedAt":"2009-06-15T21:25:35Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"The timezone used in Git objects is not the user's preferred\ntimezone, but rather a numeric form stating the offset relative to\nUTC in hours and minuutes. Any special rules like daylight savings\nis already accounted for and cannot be deduced from the time\nzone part of the git timestamp.\n\nAs an example, a committer in Sweden will have +0100 as\nhis/her timezone in winter and +0200 in summer.\n\nWe hereby abandon the attempts to guess a real timezone\nfrom the UTC offset. When creating a person ident a real\ntimezone can be used, but it's identifty will be lost when externalizing\nand only the UTC offset will be left.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/PersonIdent.java      |   54 ++++++++++++-------\n 1 files changed, 34 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PersonIdent.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PersonIdent.java\nindex 85435b5..4ed2f71 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PersonIdent.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PersonIdent.java\n@@ -39,7 +39,9 @@\n \n package org.spearce.jgit.lib;\n \n+import java.text.SimpleDateFormat;\n import java.util.Date;\n+import java.util.Locale;\n import java.util.TimeZone;\n \n /**\n@@ -228,24 +230,24 @@ public String getEmailAddress() {\n \t}\n \n \t/**\n-\t * @return timestamp\n+\t * @return timestamp in the person's local time\n \t */\n \tpublic Date getWhen() {\n \t\treturn new Date(when);\n \t}\n \n \t/**\n-\t * @return this person's preferred time zone; null if time zone is unknown.\n+\t * @return this person's declared time zone; null if time zone is unknown.\n \t */\n \tpublic TimeZone getTimeZone() {\n-\t\tfinal String[] ids = TimeZone.getAvailableIDs(tzOffset * 60 * 1000);\n-\t\tif (ids.length == 0)\n-\t\t\treturn null;\n-\t\treturn TimeZone.getTimeZone(ids[0]);\n+\t\tStringBuffer tzId = new StringBuffer(8);\n+\t\ttzId.append(\"GMT\");\n+\t\tappendTimezone(tzId);\n+\t\treturn TimeZone.getTimeZone(tzId.toString());\n \t}\n \n \t/**\n-\t * @return this person's preferred time zone as minutes east of UTC. If the\n+\t * @return this person's declared time zone as minutes east of UTC. If the\n \t *         timezone is to the west of UTC it is negative.\n \t */\n \tpublic int getTimeZoneOffset() {\n@@ -273,6 +275,17 @@ public boolean equals(final Object o) {\n \t */\n \tpublic String toExternalString() {\n \t\tfinal StringBuffer r = new StringBuffer();\n+\t\tr.append(getName());\n+\t\tr.append(\" <\");\n+\t\tr.append(getEmailAddress());\n+\t\tr.append(\"> \");\n+\t\tr.append(when / 1000);\n+\t\tr.append(' ');\n+\t\tappendTimezone(r);\n+\t\treturn r.toString();\n+\t}\n+\n+\tprivate void appendTimezone(final StringBuffer r) {\n \t\tint offset = tzOffset;\n \t\tfinal char sign;\n \t\tfinal int offsetHours;\n@@ -288,12 +301,6 @@ public String toExternalString() {\n \t\toffsetHours = offset / 60;\n \t\toffsetMins = offset % 60;\n \n-\t\tr.append(getName());\n-\t\tr.append(\" <\");\n-\t\tr.append(getEmailAddress());\n-\t\tr.append(\"> \");\n-\t\tr.append(when / 1000);\n-\t\tr.append(' ');\n \t\tr.append(sign);\n \t\tif (offsetHours < 10) {\n \t\t\tr.append('0');\n@@ -303,25 +310,32 @@ public String toExternalString() {\n \t\t\tr.append('0');\n \t\t}\n \t\tr.append(offsetMins);\n-\t\treturn r.toString();\n \t}\n \n \tpublic String toString() {\n \t\tfinal StringBuffer r = new StringBuffer();\n-\t\tint minutes;\n-\n-\t\tminutes = tzOffset < 0 ? -tzOffset : tzOffset;\n-\t\tminutes = (minutes / 100) * 60 + (minutes % 100);\n-\t\tminutes = tzOffset < 0 ? -minutes : minutes;\n \n \t\tr.append(\"PersonIdent[\");\n \t\tr.append(getName());\n \t\tr.append(\", \");\n \t\tr.append(getEmailAddress());\n \t\tr.append(\", \");\n-\t\tr.append(new Date(when + minutes * 60));\n+\t\tr.append(formatTime(new SimpleDateFormat(\"EEE MMM d HH:mm:ss yyyy Z\", Locale.US)));\n \t\tr.append(\"]\");\n \n \t\treturn r.toString();\n \t}\n+\n+\t/**\n+\t * Format the time represented by this object using the give\n+\t * SimpleDateFormat.\n+\t * \n+\t * @param simpleDateFormat\n+\t * @return a formatted time stamp.\n+\t */\n+\t@SuppressWarnings(\"boxing\")\n+\tpublic String formatTime(SimpleDateFormat simpleDateFormat) {\n+\t\tsimpleDateFormat.setTimeZone(getTimeZone());\n+\t\treturn simpleDateFormat.format(when);\n+\t}\n }\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116357","messageId":"1245101136-31874-2-git-send-email-robin.rosenberg@dewire.com","threadId":"16566","inReplyTo":"1245101136-31874-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 2/2] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-15T21:25:36Z","receivedAt":"2009-06-15T21:25:36Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"This reader allows the caller to the reflog entries for a given ref.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n ...pearce.jgit--All-External-Tests (Java 6).launch |   27 ++--\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  165 +++++++++++++++++\n .../org/spearce/jgit/lib/RepositoryTestCase.java   |   18 ++\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  187 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |   14 ++\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   46 +++++\n 6 files changed, 445 insertions(+), 12 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\ndiff --git a/org.spearce.jgit.test/org.spearce.jgit--All-External-Tests (Java 6).launch b/org.spearce.jgit.test/org.spearce.jgit--All-External-Tests (Java 6).launch\nindex 73a2f63..1e16df2 100644\n--- a/org.spearce.jgit.test/org.spearce.jgit--All-External-Tests (Java 6).launch\t\n+++ b/org.spearce.jgit.test/org.spearce.jgit--All-External-Tests (Java 6).launch\t\n@@ -1,21 +1,24 @@\n-<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n+<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n <launchConfiguration type=\"org.eclipse.jdt.junit.launchconfig\">\n-<booleanAttribute key=\"org.eclipse.jdt.junit.KEEPRUNNING_ATTR\" value=\"false\"/>\n-<stringAttribute key=\"org.eclipse.jdt.launching.MAIN_TYPE\" value=\"\"/>\n-<stringAttribute key=\"org.eclipse.jdt.junit.TESTNAME\" value=\"\"/>\n+<listAttribute key=\"org.eclipse.debug.core.MAPPED_RESOURCE_PATHS\">\n+<listEntry value=\"/org.spearce.jgit.test/exttst\"/>\n+</listAttribute>\n <listAttribute key=\"org.eclipse.debug.core.MAPPED_RESOURCE_TYPES\">\n-<listEntry value=\"4\"/>\n+<listEntry value=\"2\"/>\n </listAttribute>\n-<stringAttribute key=\"org.eclipse.jdt.launching.PROJECT_ATTR\" value=\"org.spearce.jgit.test\"/>\n-<stringAttribute key=\"org.eclipse.jdt.launching.JRE_CONTAINER\" value=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6\"/>\n+<booleanAttribute key=\"org.eclipse.debug.core.appendEnvironmentVariables\" value=\"true\"/>\n+<mapAttribute key=\"org.eclipse.debug.core.environmentVariables\">\n+<mapEntry key=\"TZ\" value=\"America/Los_Angeles\"/>\n+</mapAttribute>\n <listAttribute key=\"org.eclipse.debug.ui.favoriteGroups\">\n-<listEntry value=\"org.eclipse.debug.ui.launchGroup.debug\"/>\n <listEntry value=\"org.eclipse.debug.ui.launchGroup.run\"/>\n-</listAttribute>\n-<listAttribute key=\"org.eclipse.debug.core.MAPPED_RESOURCE_PATHS\">\n-<listEntry value=\"/org.spearce.jgit.test\"/>\n+<listEntry value=\"org.eclipse.debug.ui.launchGroup.debug\"/>\n </listAttribute>\n <stringAttribute key=\"org.eclipse.jdt.junit.CONTAINER\" value=\"=org.spearce.jgit.test/exttst\"/>\n-<booleanAttribute key=\"org.eclipse.debug.core.appendEnvironmentVariables\" value=\"true\"/>\n+<booleanAttribute key=\"org.eclipse.jdt.junit.KEEPRUNNING_ATTR\" value=\"false\"/>\n+<stringAttribute key=\"org.eclipse.jdt.junit.TESTNAME\" value=\"\"/>\n <stringAttribute key=\"org.eclipse.jdt.junit.TEST_KIND\" value=\"org.eclipse.jdt.junit.loader.junit3\"/>\n+<stringAttribute key=\"org.eclipse.jdt.launching.JRE_CONTAINER\" value=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6\"/>\n+<stringAttribute key=\"org.eclipse.jdt.launching.MAIN_TYPE\" value=\"\"/>\n+<stringAttribute key=\"org.eclipse.jdt.launching.PROJECT_ATTR\" value=\"org.spearce.jgit.test\"/>\n </launchConfiguration>\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\nnew file mode 100644\nindex 0000000..93fcf9d\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n@@ -0,0 +1,165 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.text.SimpleDateFormat;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.ReflogReader.Entry;\n+\n+public class ReflogReaderTest extends RepositoryTestCase {\n+\n+\tstatic byte[] oneLine = \"da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\\tcommit: Add a toString for debugging to RemoteRefUpdate\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLine = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLineWithAppendInProgress = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\"\n+\t\t\t+ \"54794942a18a237c57a80719afed44bb78172b10 \")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] aLine = \"1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to a\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] masterLine = \"2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to master\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] headLine = \"3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to HEAD\\n\"\n+\t\t\t.getBytes();\n+\n+\tpublic void testReadOneLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", oneLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tEntry e = reader.getLastEntry();\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"da85355dfc525c9f6f3927b876f379f46ccf826e\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"3e7549db262d1e836d9bf0af7e22355468f1717c\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"A O Thor Too\", e.getWho().getName());\n+\t\tassertEquals(\"authortoo@wri.tr\", e.getWho().getEmailAddress());\n+\t\tassertEquals(120, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:40\", iso(e.getWho()));\n+\t\tassertEquals(\"commit: Add a toString for debugging to RemoteRefUpdate\",\n+\t\t\t\te.getComment());\n+\t}\n+\n+\tprivate String iso(PersonIdent id) {\n+\t\treturn id.formatTime(new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\"));\n+\t}\n+\n+\tpublic void testReadTwoLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T22:36:42\", iso(e.getWho()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\n+\t\te = reverseEntries.get(1);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"0000000000000000000000000000000000000000\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"thor@committer.au\", e.getWho().getEmailAddress());\n+\t\tassertEquals(-60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T20:36:41\", iso(e.getWho()));\n+\t\tassertEquals(\"branch: Created from rr/renamebranchv4\", e.getComment());\n+\t}\n+\n+\tpublic void testReadWhileAppendIsInProgress() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLineWithAppendInProgress);\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T22:36:42\", iso(e.getWho()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\t\t// while similar to testReadTwoLine, we can assume that if we get the last entry\n+\t\t// right, everything else is too\n+\t}\n+\n+\n+\tpublic void testReadRightLog() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/a\", aLine);\n+\t\tsetupReflog(\"logs/refs/heads/master\", masterLine);\n+\t\tsetupReflog(\"logs/HEAD\", headLine);\n+\t\tassertEquals(\"branch: change to master\", db.getReflogReader(\"master\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to a\", db.getReflogReader(\"a\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to HEAD\", db.getReflogReader(\"HEAD\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t}\n+\n+\tpublic void testNoLog() throws Exception {\n+\t\tassertEquals(0, db.getReflogReader(\"master\").getReverseEntries().size());\n+\t\tassertNull(db.getReflogReader(\"master\").getLastEntry());\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\nindex 3b03ac1..3837ea9 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n@@ -40,6 +40,7 @@\n \n import java.io.File;\n import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n import java.io.FileOutputStream;\n import java.io.IOException;\n import java.io.InputStreamReader;\n@@ -348,4 +349,21 @@ public void run() {\n \t\treturn newRepo;\n \t}\n \n+\tprotected void setupReflog(String logName, byte[] data)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\t\t\tFile logfile = new File(db.getDirectory(), logName);\n+\t\t\t\tif (!logfile.getParentFile().mkdirs()\n+\t\t\t\t\t\t&& !logfile.getParentFile().isDirectory()) {\n+\t\t\t\t\tthrow new IOException(\n+\t\t\t\t\t\t\t\"oops, cannot create the directory for the test reflog file\"\n+\t\t\t\t\t\t\t\t\t+ logfile);\n+\t\t\t\t}\n+\t\t\t\tFileOutputStream fileOutputStream = new FileOutputStream(logfile);\n+\t\t\t\ttry {\n+\t\t\t\t\tfileOutputStream.write(data);\n+\t\t\t\t} finally {\n+\t\t\t\t\tfileOutputStream.close();\n+\t\t\t\t}\n+\t\t\t}\n+\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\nnew file mode 100644\nindex 0000000..081b52c\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n@@ -0,0 +1,187 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/**\n+ * Utility for reading reflog entries\n+ */\n+public class ReflogReader {\n+\t/**\n+\t * Parsed reflog entry\n+\t */\n+\tstatic public class Entry {\n+\t\tprivate ObjectId oldId;\n+\n+\t\tprivate ObjectId newId;\n+\n+\t\tprivate PersonIdent who;\n+\n+\t\tprivate String comment;\n+\n+\t\tEntry(byte[] raw, int pos) {\n+\t\t\toldId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ')\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tnewId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ') {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\twho = RawParseUtils.parsePersonIdentOnly(raw, pos);\n+\t\t\tint p0 = RawParseUtils.next(raw, pos, '\\t'); // personident has no\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// \\t\n+\t\t\tif (p0 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tint p1 = RawParseUtils.nextLF(raw, p0);\n+\t\t\tif (p1 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tcomment = RawParseUtils.decode(raw, p0, p1 - 1);\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id before the change\n+\t\t */\n+\t\tpublic ObjectId getOldId() {\n+\t\t\treturn oldId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id after the change\n+\t\t */\n+\t\tpublic ObjectId getNewId() {\n+\t\t\treturn newId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return user performin the change\n+\t\t */\n+\t\tpublic PersonIdent getWho() {\n+\t\t\treturn who;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return textual description of the change\n+\t\t */\n+\t\tpublic String getComment() {\n+\t\t\treturn comment;\n+\t\t}\n+\t\t\n+\t\t@Override\n+\t\tpublic String toString() {\n+\t\t\treturn \"Entry[\" + oldId.name() + \", \" + newId.name() + \", \" + getWho() + \", \"\n+\t\t\t\t\t+ getComment() + \"]\";\n+\t\t}\n+\t}\n+\n+\tprivate File logName;\n+\n+\tReflogReader(Repository db, String refname) {\n+\t\tlogName = new File(db.getDirectory(), \"logs/\" + refname);\n+\t}\n+\n+\t/**\n+\t * Get the last entry in the reflog\n+\t * \n+\t * @return the latest reflog entry, or null if no log\n+\t * @throws IOException\n+\t */\n+\tpublic Entry getLastEntry() throws IOException {\n+\t\tList<Entry> entries = getReverseEntries(1);\n+\t\treturn entries.size() > 0 ? entries.get(0) : null;\n+\t}\n+\n+\t/**\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries() throws IOException {\n+\t\treturn getReverseEntries(Integer.MAX_VALUE);\n+\t}\n+\n+\t/**\n+\t * @param max\n+\t *            max numer of entries to read\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries(int max) throws IOException {\n+\t\tFileInputStream fileInputStream;\n+\t\ttry {\n+\t\t\tfileInputStream = new FileInputStream(logName);\n+\t\t} catch (FileNotFoundException e) {\n+\t\t\treturn Collections.emptyList();\n+\t\t}\n+\t\ttry {\n+\t\t\tlong logSize = fileInputStream.getChannel().size();\n+\t\t\tif (logSize > Integer.MAX_VALUE) {\n+\t\t\t\t// implementation limit, will suck with smaller files too\n+\t\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n+\t\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n+\t\t\t}\n+\t\t\tbyte[] log = new byte[(int) logSize];\n+\t\t\tNB.readFully(fileInputStream, log, 0, log.length);\n+\t\t\tint rs = RawParseUtils.prevLF(log, log.length);\n+\t\t\tList<Entry> ret = new ArrayList<Entry>();\n+\t\t\twhile (rs >= 0 && max-- > 0) {\n+\t\t\t\trs = RawParseUtils.prevLF(log, rs);\n+\t\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n+\t\t\t\tret.add(entry);\n+\t\t\t}\n+\t\t\treturn ret;\n+\t\t} finally {\n+\t\t\tfileInputStream.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 5def5d3..971215c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -1089,4 +1089,18 @@ public String shortenRefName(String refName) {\n \t\t\treturn refName.substring(Constants.R_REMOTES.length());\n \t\treturn refName;\n \t}\n+\n+\t/**\n+\t * @param refName\n+\t * @return a {@link ReflogReader} for the refname, or null if the names ref\n+\t *         does not exist.\n+\t * @throws IOException \n+\t */\n+\tpublic ReflogReader getReflogReader(String refName) throws IOException {\n+\t\tRef ref = getRef(refName);\n+\t\tif (ref != null)\n+\t\t\treturn new ReflogReader(this, ref.getOrigName());\n+\t\telse\n+\t\t\treturn null;\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\nindex bdd6a11..df24190 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n@@ -569,6 +569,52 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t}\n \n \t/**\n+\t * Parse a name data (e.g. as within a reflog) into a PersonIdent.\n+\t * <p>\n+\t * When passing in a value for <code>nameB</code> callers should use the\n+\t * return value of {@link #author(byte[], int)} or\n+\t * {@link #committer(byte[], int)}, as these methods provide the proper\n+\t * position within the buffer.\n+\t * \n+\t * @param raw\n+\t *            the buffer to parse character data from.\n+\t * @param nameB\n+\t *            first position of the identity information. This should be the\n+\t *            first position after the space which delimits the header field\n+\t *            name (e.g. \"author\" or \"committer\") from the rest of the\n+\t *            identity line.\n+\t * @return the parsed identity. Never null.\n+\t */\n+\tpublic static PersonIdent parsePersonIdentOnly(final byte[] raw, final int nameB) {\n+\t\tint stop = nextLF(raw, nameB);\n+\t\tint emailB = nextLF(raw, nameB, '<');\n+\t\tint emailE = nextLF(raw, emailB, '>');\n+\t\tfinal String name;\n+\t\tfinal String email;\n+\t\tif (emailE < stop) {\n+\t\t\temail = decode(raw, emailB, emailE - 1);\n+\t\t} else {\n+\t\t\temail = \"invalid\";\n+\t\t}\n+\t\tif (emailB < stop)\n+\t\t\tname = decode(raw, nameB, emailB - 2);\n+\t\telse\n+\t\t\tname = decode(raw, nameB, stop);\n+\n+\t\tfinal MutableInteger ptrout = new MutableInteger();\n+\t\tlong when;\n+\t\tint tz;\n+\t\tif (emailE < stop) {\n+\t\t\twhen = parseLongBase10(raw, emailE + 1, ptrout);\n+\t\t\ttz = parseTimeZoneOffset(raw, ptrout.value);\n+\t\t} else {\n+\t\t\twhen = 0;\n+\t\t\ttz = 0;\n+\t\t}\n+\t\treturn new PersonIdent(name, email, when * 1000L, tz);\n+\t}\n+\n+\t/**\n \t * Decode a buffer under UTF-8, if possible.\n \t *\n \t * If the byte stream cannot be decoded that way, the platform default is tried\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116366","messageId":"200906160021.19939.robin.rosenberg.lists@dewire.com","threadId":"16566","inReplyTo":"1245101136-31874-2-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 2/2] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-06-15T22:21:19Z","receivedAt":"2009-06-15T22:21:19Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"måndag 15 juni 2009 23:25:36 skrev Robin Rosenberg <robin.rosenberg@dewire.com>:\n> This reader allows the caller to the reflog entries for a given ref.\n\nOops a typo.\n\nThis reader allows the caller to read the reflog entries for a given ref.. I also noted \ntwo missing comments in UIText.java. If you want to I can resend the whole\nseries(es).\n\n-- robin\n"},{"id":"116756","messageId":"21854.439963545$1245671417@news.gmane.org","threadId":"16566","inReplyTo":"y","subject":"[BUG] apply: test for trailing whitespace & no new line bug","fromName":"","fromEmail":"y@vger.kernel.org","sentAt":"2009-06-22T11:49:26Z","receivedAt":"2009-06-22T11:49:26Z","isPatch":false,"sender":{"key":"y@vger.kernel.org","avatar":null},"body":"From: SZEDER Gábor <szeder@ira.uka.de>\n\nIf a patch adds a new line to the end of a file and this line ends\nwith trailing whitespace but has no newline, then 'git apply\n--whitespace=fix' does not remove the trailing whitespace from that\nline.\n\nSigned-off-by: SZEDER Gábor <szeder@ira.uka.de>\n---\n\nNoticed last week, but couldn't fix it myself during the weekend.\n\n t/t4124-apply-ws-rule.sh |   18 ++++++++++++++++++\n 1 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex f83322e..0db9b59 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -148,4 +148,22 @@ do\n \tdone\n done\n \n+create_patch () {\n+\tsed -e \"s/_/ /\" <<-\\EOF\n+\t\tdiff --git a/target b/target\n+\t\tindex e69de29..8bd6648 100644\n+\t\t--- a/target\n+\t\t+++ b/target\n+\t\t@@ -0,0 +1 @@\n+\t\t+A line with trailing whitespace and no newline_\n+\t\t\\ No newline at end of file\n+\tEOF\n+}\n+\n+test_expect_failure 'trailing whitespace & no newline at the end of file' '\n+\t>target &&\n+\tcreate_patch | git apply --whitespace=fix - &&\n+\tgit diff --check -- target\n+'\n+\n test_done\n-- \n1.6.3.2.370.g6bb4.dirty\n"},{"id":"299242","messageId":"1245671366-23528-1-git-send-email-y","threadId":"16566","inReplyTo":"y","subject":"[BUG] apply: test for trailing whitespace & no new line bug","fromName":"","fromEmail":"y@vger.kernel.org","sentAt":"2009-06-22T11:49:26Z","receivedAt":"2009-06-22T11:49:26Z","isPatch":false,"sender":{"key":"y@vger.kernel.org","avatar":null},"body":"From: SZEDER Gábor <szeder@ira.uka.de>\n\nIf a patch adds a new line to the end of a file and this line ends\nwith trailing whitespace but has no newline, then 'git apply\n--whitespace=fix' does not remove the trailing whitespace from that\nline.\n\nSigned-off-by: SZEDER Gábor <szeder@ira.uka.de>\n---\n\nNoticed last week, but couldn't fix it myself during the weekend.\n\n t/t4124-apply-ws-rule.sh |   18 ++++++++++++++++++\n 1 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex f83322e..0db9b59 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -148,4 +148,22 @@ do\n \tdone\n done\n \n+create_patch () {\n+\tsed -e \"s/_/ /\" <<-\\EOF\n+\t\tdiff --git a/target b/target\n+\t\tindex e69de29..8bd6648 100644\n+\t\t--- a/target\n+\t\t+++ b/target\n+\t\t@@ -0,0 +1 @@\n+\t\t+A line with trailing whitespace and no newline_\n+\t\t\\ No newline at end of file\n+\tEOF\n+}\n+\n+test_expect_failure 'trailing whitespace & no newline at the end of file' '\n+\t>target &&\n+\tcreate_patch | git apply --whitespace=fix - &&\n+\tgit diff --check -- target\n+'\n+\n test_done\n-- \n1.6.3.2.370.g6bb4.dirty\n\n"},{"id":"145133","messageId":"4c35eb6c.21078e0a.1455.ffffe42d@mx.google.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] diff.c: fix a graph output bug","fromName":"","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-07-08T15:12:34Z","receivedAt":"2010-07-08T15:12:34Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"From: Bo Yang <struggleyb.nku@gmail.com>\n\nOuput the line_prefix ahead of color sequence.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\n---\n diff.c |    3 +--\n 1 files changed, 1 insertions(+), 2 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 3aa695d..17873f3 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2627,8 +2627,7 @@ static void fill_metainfo(struct strbuf *msg,\n \t\t\t    (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))\n \t\t\t\tabbrev = 40;\n \t\t}\n-\t\tstrbuf_addf(msg, \"%s%sindex %s..\", set,\n-\t\t\t    line_prefix,\n+\t\tstrbuf_addf(msg, \"%s%sindex %s..\", line_prefix, set,\n \t\t\t    find_unique_abbrev(one->sha1, abbrev));\n \t\tstrbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));\n \t\tif (one->mode == two->mode)\n-- \n1.7.0.2.273.gc2413.dirty\n"},{"id":"145174","messageId":"7vhbk9o6ie.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"4c35eb6c.21078e0a.1455.ffffe42d@mx.google.com","subject":"Re: [PATCH] diff.c: fix a graph output bug","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-09T00:15:05Z","receivedAt":"2010-07-09T00:15:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"struggleyb.nku@gmail.com writes:\n\n> From: Bo Yang <struggleyb.nku@gmail.com>\n>\n> Ouput the line_prefix ahead of color sequence.\n\nJustification for the patch, and/or a better bug description (\"when run\nwith this combination of options, X gets painted in that color that is not\nmeant to be used for X\") please?\n\n>\n> Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>\n> ---\n>  diff.c |    3 +--\n>  1 files changed, 1 insertions(+), 2 deletions(-)\n>\n> diff --git a/diff.c b/diff.c\n> index 3aa695d..17873f3 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -2627,8 +2627,7 @@ static void fill_metainfo(struct strbuf *msg,\n>  \t\t\t    (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))\n>  \t\t\t\tabbrev = 40;\n>  \t\t}\n> -\t\tstrbuf_addf(msg, \"%s%sindex %s..\", set,\n> -\t\t\t    line_prefix,\n> +\t\tstrbuf_addf(msg, \"%s%sindex %s..\", line_prefix, set,\n>  \t\t\t    find_unique_abbrev(one->sha1, abbrev));\n>  \t\tstrbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));\n>  \t\tif (one->mode == two->mode)\n> -- \n> 1.7.0.2.273.gc2413.dirty\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"145177","messageId":"7v1vbdo4j5.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"7vhbk9o6ie.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff.c: fix a graph output bug","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-09T00:57:50Z","receivedAt":"2010-07-09T00:57:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> struggleyb.nku@gmail.com writes:\n>\n>> From: Bo Yang <struggleyb.nku@gmail.com>\n>>\n>> Ouput the line_prefix ahead of color sequence.\n>\n> Justification for the patch, and/or a better bug description (\"when run\n> with this combination of options, X gets painted in that color that is not\n> meant to be used for X\") please?\n>>\n>> Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>\n\nPerhaps something like this?\n\nFrom: Bo Yang <struggleyb.nku@gmail.com>\nDate: Thu, 8 Jul 2010 23:12:34 +0800\nSubject: [PATCH] diff.c: fix a graph output bug\n\nWhen --graph is in effect, the line-prefix typically has colored graph\nline segments and ends with reset.  The color sequence \"set\" given to\nthis function is for showing the metainfo part of the patch text and\n(1) it should not be applied to the graph lines, and (2) it will be\nreset at the end of line_prefix so it won't be effect anyway.\n\nSigned-off-by: Bo Yang <struggleyb.nku@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n diff.c |    3 +--\n 1 files changed, 1 insertions(+), 2 deletions(-)\n ...\n"},{"id":"145178","messageId":"AANLkTilSbmU5ejI5lWcN9GFQ544xd0zBtn-5s-nLzBAv@mail.gmail.com","threadId":"16566","inReplyTo":"7v1vbdo4j5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff.c: fix a graph output bug","fromName":"Nazri Ramliy","fromEmail":"ayiehere@gmail.com","sentAt":"2010-07-09T01:04:45Z","receivedAt":"2010-07-09T01:04:45Z","isPatch":true,"sender":{"key":"ayiehere@gmail.com","avatar":"https://avatars.githubusercontent.com/u/164756?v=4"},"body":"On Fri, Jul 9, 2010 at 8:57 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> reset at the end of line_prefix so it won't be effect anyway.\n\ns/effect/affected/  ?\n\nnazri\n"},{"id":"145179","messageId":"7vwrt5mpey.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"AANLkTilSbmU5ejI5lWcN9GFQ544xd0zBtn-5s-nLzBAv@mail.gmail.com","subject":"Re: [PATCH] diff.c: fix a graph output bug","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-07-09T01:09:41Z","receivedAt":"2010-07-09T01:09:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nazri Ramliy <ayiehere@gmail.com> writes:\n\n> On Fri, Jul 9, 2010 at 8:57 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> reset at the end of line_prefix so it won't be effect anyway.\n>\n> s/effect/affected/  ?\n\nThanks for spotting.  I meant to say \"won't be in effect\".\n"},{"id":"145293","messageId":"AANLkTimfQ3ZBCdz0aeDdpsWvcLrG-AxWoLYGCoa8zLi4@mail.gmail.com","threadId":"16566","inReplyTo":"7v1vbdo4j5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] diff.c: fix a graph output bug","fromName":"Bo Yang","fromEmail":"struggleyb.nku@gmail.com","sentAt":"2010-07-11T06:10:20Z","receivedAt":"2010-07-11T06:10:20Z","isPatch":true,"sender":{"key":"struggleyb.nku@gmail.com","avatar":"https://avatars.githubusercontent.com/u/233030?v=4"},"body":"Hi Junio,\n\nOn Fri, Jul 9, 2010 at 8:57 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Perhaps something like this?\n>\n> From: Bo Yang <struggleyb.nku@gmail.com>\n> Date: Thu, 8 Jul 2010 23:12:34 +0800\n> Subject: [PATCH] diff.c: fix a graph output bug\n>\n> When --graph is in effect, the line-prefix typically has colored graph\n> line segments and ends with reset.  The color sequence \"set\" given to\n> this function is for showing the metainfo part of the patch text and\n> (1) it should not be applied to the graph lines, and (2) it will be\n> reset at the end of line_prefix so it won't be effect anyway.\n>\n> Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  diff.c |    3 +--\n>  1 files changed, 1 insertions(+), 2 deletions(-)\n>  ...\n>\n\nHmm, your description is great, thanks a lot! And please apply this\npatch, thanks!\n\n-- \nRegards!\nBo\n----------------------------\nMy blog: http://blog.morebits.org\nWhy Git: http://www.whygitisbetterthanx.com/\n"},{"id":"152842","messageId":"4cad50cc.0626730a.5616.673d@mx.google.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH 1/2] setup.c: add enter_work_tree()","fromName":"","fromEmail":"pclouds@gmail.com","sentAt":"2010-10-07T04:46:52Z","receivedAt":"2010-10-07T04:46:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nThis function, apart from chdir() to worktree topdir, also invalidates\ninside_work_tree so is_inside_work_tree() should reflect the new\nsituation correctly.\n\nThis function may be used when cwd is outside worktree and the running\ncommand supports this case.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n To be used by builtin/grep.c when cwd is outside worktree.\n More explanation in the next patch.\n\n cache.h |    1 +\n setup.c |    7 +++++++\n 2 files changed, 8 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 2ef2fa3..e71db23 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -414,6 +414,7 @@ extern int set_git_dir(const char *path);\n extern const char *get_git_work_tree(void);\n extern const char *read_gitfile_gently(const char *path);\n extern void set_git_work_tree(const char *tree);\n+extern void enter_work_tree();\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/setup.c b/setup.c\nindex a3b76de..3fbe928 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -208,6 +208,13 @@ int is_inside_work_tree(void)\n \treturn inside_work_tree;\n }\n \n+void enter_work_tree(void)\n+{\n+\tif (chdir(get_git_work_tree()))\n+\t\tdie(\"Could not chdir to %s\", get_git_work_tree());\n+\tinside_work_tree = -1;\n+}\n+\n /*\n  * set_work_tree() is only ever called if you set GIT_DIR explicitly.\n  * The old behaviour (which we retain here) is to set the work tree root\n-- \n1.7.1.rc1.70.g788ca\n"},{"id":"152843","messageId":"4cad50da.0e958e0a.40e3.5efd@mx.google.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"","fromEmail":"pclouds@gmail.com","sentAt":"2010-10-07T04:46:53Z","receivedAt":"2010-10-07T04:46:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nWhen both GIT_DIR and GIT_WORK_TREE are set, if cwd is outside\nworktree, prefix (the one passed to every builtin commands) will be\nset to NULL, which means \"user stays at worktree topdir\", but cwd is\nnot moved to worktree topdir.\n\nAs a consequence, command line arguments will be interpreted\ndifferently. Assume there is a file named \"bar\" in foo/.git. A command\ncan expect command line arguments to be \"path in repository\":\n\n$ GIT_DIR=foo/.git GIT_WORK_TREE=foo git blah bar\n\nor \"path in file system\":\n\n$ GIT_DIR=foo/.git GIT_WORK_TREE=foo git blah foo/bar\n\nSome commands may want \"path in repository\" and \"path in file system\"\nto be identical. Moreover, output from commands in such situations are\nrelative to worktree topdir (because prefix is NULL), not what users\nexpect. It's just confusing.\n\nThis patch does not address that. Instead it prepares some variables\nto ease access to original cwd in this case. Commands that work well\noutside worktree should:\n\n - detect this case, via is_inside_work_tree()\n\n - enter_work_tree() (not strictly needed, but I believe it would\n   less work this way)\n\n - prepend startup_info->cwd_to_worktree to output so it becomes\n   relative to original cwd\n\n - prepend startup_info->worktree_to_cwd to command line arguments\n   if they are meant to access file system\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n I don't care Windows UNC path. And I did not test the case\n where cwd is on C:\\ while worktree is on another drive.\n\n As stated elsewhere, I neglect other cases where cwd may be\n outside worktree. The goal of these patches is to help\n Chris' grep series. If it works out, I'll fix the rest.\n\n Sorry Chris, I think I have dragged you into worktree setup\n mess.\n\n builtin/rev-parse.c        |   10 ++++\n cache.h                    |    2 +\n setup.c                    |  119 ++++++++++++++++++++++++++++++++++++++++++--\n t/t1510-worktree-prefix.sh |   52 +++++++++++++++++++\n 4 files changed, 179 insertions(+), 4 deletions(-)\n create mode 100755 t/t1510-worktree-prefix.sh\n\ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex a5a1c86..525610e 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -623,6 +623,16 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\t\t\tputs(prefix);\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--cwd-to-worktree\")) {\n+\t\t\t\tif (startup_info->cwd_to_worktree)\n+\t\t\t\t\tputs(startup_info->cwd_to_worktree);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(arg, \"--worktree-to-cwd\")) {\n+\t\t\t\tif (startup_info->worktree_to_cwd)\n+\t\t\t\t\tputs(startup_info->worktree_to_cwd);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-cdup\")) {\n \t\t\t\tconst char *pfx = prefix;\n \t\t\t\tif (!is_inside_work_tree()) {\ndiff --git a/cache.h b/cache.h\nindex e71db23..be35b8f 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1111,6 +1111,8 @@ const char *split_cmdline_strerror(int cmdline_errno);\n /* git.c */\n struct startup_info {\n \tint have_repository;\n+\tchar *cwd_to_worktree; /* path from original cwd to worktree */\n+\tchar *worktree_to_cwd; /* path from worktree to original cwd */\n };\n extern struct startup_info *startup_info;\n \ndiff --git a/setup.c b/setup.c\nindex 3fbe928..1f5117a 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -320,10 +320,120 @@ const char *read_gitfile_gently(const char *path)\n \treturn path;\n }\n \n+/*\n+ * Given \"foo/bar\" and \"hey/hello/world\", return \"../../hey/hello/world/\"\n+ * Either path1 or path2 can be NULL. All must be canonical.\n+ */\n+static char *make_path_to_path(const char *path1, const char *path2)\n+{\n+\tint nr_back = 0;\n+\tint i, pathlen = path2 ? strlen(path2) : 0;\n+\tchar *buf, *p;\n+\n+\tif (path1 && *path1) {\n+\t\tnr_back = 1;\n+\t\twhile (*path1) {\n+\t\t\tif (is_dir_sep(*path1))\n+\t\t\t\tnr_back++;\n+\t\t\tpath1++;\n+\t\t}\n+\t}\n+\n+\tif (!nr_back && !pathlen)\n+\t\treturn NULL;\n+\n+\tp = buf = xmalloc(3*nr_back + pathlen + 2); /* \"../\"^nr_back + path2 + '/' + NULL */\n+\tfor (i = 0; i < nr_back; i++) {\n+\t\tmemcpy(p, \"../\", 3);\n+\t\tp += 3;\n+\t}\n+\tif (pathlen) {\n+\t\tmemcpy(p, path2, pathlen);\n+\t\tp += pathlen;\n+\t\tif (p[-1] != '/') /* path2 may be c:/, don't put another slash  */\n+\t\t\t*p++ = '/';\n+\t}\n+\t*p = '\\0';\n+\treturn buf;\n+}\n+\n+/*\n+ * Return a prefix if cwd inside worktree, or NULL otherwise.\n+ * Also fill startup_info struct.\n+ */\n+static const char *setup_prefix(const char *cwd)\n+{\n+\tconst char *worktree = get_git_work_tree();\n+\tint len = 0, cwd_len = strlen(cwd), worktree_len = strlen(worktree);\n+\n+\twhile (worktree[len] && worktree[len] == cwd[len])\n+\t\tlen++;\n+\n+\tif (!worktree[len] && !cwd[len]) {\n+\t\tif (startup_info) {\n+\t\t\tstartup_info->cwd_to_worktree = NULL;\n+\t\t\tstartup_info->worktree_to_cwd = NULL;\n+\t\t}\n+\t\treturn NULL;\n+\t}\n+\t/* get /foo/, not /foo/baa if /foo/baa1 and /foo/baa2 are given */\n+\telse if (worktree[len] && cwd[len]) {\n+\t\twhile (len && !is_dir_sep(worktree[len]))\n+\t\t\tlen--;\n+\t\tlen++;\n+\n+\t\t/* Worktree and cwd are on different drives? */\n+\t\tif (len == 3 && has_dos_drive_prefix(cwd)) {\n+\t\t\tif (startup_info) {\n+\t\t\t\t/* make_path_to_path will add the trailing slash */\n+\t\t\t\tstartup_info->cwd_to_worktree = make_path_to_path(NULL, worktree);\n+\t\t\t\tstartup_info->worktree_to_cwd = make_path_to_path(NULL, cwd);\n+\t\t\t}\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\telse {\n+\t\tif (worktree[len]) {\n+\t\t\tif (!is_dir_sep(worktree[len])) {\n+\t\t\t\twhile (len && !is_dir_sep(worktree[len]))\n+\t\t\t\t\tlen--;\n+\t\t\t}\n+\t\t}\n+\t\telse {\n+\t\t\tif (!is_dir_sep(cwd[len])) {\n+\t\t\t\twhile (len && !is_dir_sep(cwd[len]))\n+\t\t\t\t\tlen--;\n+\t\t\t}\n+\t\t}\n+\t\tlen++;\t\t/* must be a slash here, skip it */\n+\t}\n+\n+\tif (len < cwd_len && len < worktree_len) {\n+\t\tif (startup_info) {\n+\t\t\tstartup_info->cwd_to_worktree = make_path_to_path(cwd+len, worktree+len);\n+\t\t\tstartup_info->worktree_to_cwd = make_path_to_path(worktree+len, cwd+len);\n+\t\t}\n+\t\treturn NULL;\n+\t}\n+\n+\tif (startup_info) {\n+\t\tif (len < cwd_len) { /* cwd inside worktree */\n+\t\t\tstartup_info->cwd_to_worktree = make_path_to_path(cwd+len, NULL);\n+\t\t\tstartup_info->worktree_to_cwd = make_path_to_path(NULL, cwd+len);\n+\t\t}\n+\t\telse {\n+\t\t\tstartup_info->cwd_to_worktree = make_path_to_path(NULL, worktree+len);\n+\t\t\tstartup_info->worktree_to_cwd = make_path_to_path(worktree+len, NULL);\n+\t\t}\n+\t}\n+\n+\treturn len < cwd_len ? cwd+len : NULL;\n+}\n+\n static const char *setup_explicit_git_dir(const char *gitdirenv,\n \t\t\t\tconst char *work_tree_env, int *nongit_ok)\n {\n-\tstatic char buffer[1024 + 1];\n+\tstatic char buffer[PATH_MAX];\n \tconst char *retval;\n \n \tif (PATH_MAX - 40 < strlen(gitdirenv))\n@@ -344,9 +454,10 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,\n \t}\n \tif (check_repository_format_gently(nongit_ok))\n \t\treturn NULL;\n-\tretval = get_relative_cwd(buffer, sizeof(buffer) - 1,\n-\t\t\tget_git_work_tree());\n-\tif (!retval || !*retval)\n+\tif (!getcwd(buffer, sizeof(buffer)))\n+\t\tdie_errno(\"can't find the current directory\");\n+\tretval = setup_prefix(buffer);\n+\tif (!retval)\n \t\treturn NULL;\n \tset_git_dir(make_absolute_path(gitdirenv));\n \tif (chdir(work_tree_env) < 0)\ndiff --git a/t/t1510-worktree-prefix.sh b/t/t1510-worktree-prefix.sh\nnew file mode 100755\nindex 0000000..3839493\n--- /dev/null\n+++ b/t/t1510-worktree-prefix.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+\n+test_description='test rev-parse --cwd-to-worktree and --worktree-to-cwd'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tmkdir foo bar &&\n+\tmv .git foo &&\n+\tmkdir foo/bar &&\n+\tGIT_DIR=`pwd`/foo/.git &&\n+\tGIT_WORK_TREE=`pwd`/foo &&\n+\texport GIT_DIR GIT_WORK_TREE\n+'\n+\n+test_expect_success 'at root' '\n+\t(\n+\tcd foo &&\n+\tgit rev-parse --cwd-to-worktree --worktree-to-cwd >result &&\n+\t: >expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_expect_success 'cwd inside worktree' '\n+\t(\n+\tcd foo/bar &&\n+\tgit rev-parse --cwd-to-worktree --worktree-to-cwd >result &&\n+\techo ../ >expected &&\n+\techo bar/ >>expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_expect_success 'cwd outside worktree' '\n+\tgit rev-parse --cwd-to-worktree --worktree-to-cwd >result &&\n+\techo foo/ >expected &&\n+\techo ../ >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'cwd outside worktree (2)' '\n+\t(\n+\tcd bar &&\n+\tgit rev-parse --cwd-to-worktree --worktree-to-cwd >result &&\n+\techo ../foo/ >expected &&\n+\techo ../bar/ >>expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_done\n-- \n1.7.1.rc1.70.g788ca\n"},{"id":"152846","messageId":"AANLkTikQJDBsSg96DBohqGjgg+E4LUXU-F1a5DEyz0JU@mail.gmail.com","threadId":"16566","inReplyTo":"4cad50da.0e958e0a.40e3.5efd@mx.google.com","subject":"Re: [PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-10-07T05:53:33Z","receivedAt":"2010-10-07T05:53:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 7, 2010 at 11:46 AM,  <pclouds@gmail.com> wrote:\n> +               /* Worktree and cwd are on different drives? */\n> +               if (len == 3 && has_dos_drive_prefix(cwd)) {\n> +                       if (startup_info) {\n> +                               /* make_path_to_path will add the trailing slash */\n> +                               startup_info->cwd_to_worktree = make_path_to_path(NULL, worktree);\n> +                               startup_info->worktree_to_cwd = make_path_to_path(NULL, cwd);\n> +                       }\n> +                       return NULL;\n> +               }\n\nWent for lunch and realized this code is just wrong. Gaahh..\n-- \nDuy\n"},{"id":"152847","messageId":"7veic2y2c5.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"4cad50da.0e958e0a.40e3.5efd@mx.google.com","subject":"Re: [PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-10-07T05:54:50Z","receivedAt":"2010-10-07T05:54:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"pclouds@gmail.com writes:\n\n> From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>\n> When both GIT_DIR and GIT_WORK_TREE are set, if cwd is outside\n> worktree, prefix (the one passed to every builtin commands) will be\n> set to NULL, which means \"user stays at worktree topdir\", but cwd is\n> not moved to worktree topdir.\n\nWell, NULL traditionally meant \"_if_ the cwd is inside the working tree,\nit is at the top\", not \"stays at worktree topdir\" if you started from\nelsewhere.\n\nAnd when cwd is inside the working tree, we do want to feed paths given\nfrom the command line after adding the prefix, so that we will see the\npaths relative to the cwd after we do cd-to-topdir.\n\nObviously when you run git command that requires you to be in a working\ntree from outside the working tree, relative or full paths would not make\nany difference.  The command should simply fail.\n\nI suspect that you are introducing a new semantics on top of that\ntraditional semantics; if so, you may want to state it more clearly.\n\nFor example:\n\n    When you run git command that requires you to be in a working tree\n    from outside the working tree, the command should simply fail.\n\n    When GIT_WORK_TREE is in use, however, it could be argued that we do\n    not necessarily have to be in anywhere in the working tree to perform\n    a whole-tree operation.  Instead, we could just say the operation\n    always runs as if the command was started at the root level of the\n    working tree.\n\n    To support this new mode of operation, however, prefix needs to be\n    adjusted to allow the program, after running chdir(2) to the root of\n    the working tree, to refer to non-absolute paths originally given from\n    the command line as relative to the original cwd.  This patch adds a\n    mechanism to support that.\n\nI have a queasy feeling about the idea of the second paragraph above,\nthough.\n\nIf the original cwd is inside GIT_WORK_TREE, limiting ourselves inside\nprefix naturally limits the operation to the subdirectory we started from\n(if the original cwd is at GIT_WORK_TREE, that would make it a whole-tree\noperation).  A natural extension of this idea to limit the operation to\nthe part of the subtree of the working tree we started from is to refuse\nto work in the case where the original cwd is outside GIT_WORK_TREE (the\ncurrent implementation of GIT_WORK_TREE may or may not correctly implement\nit, though---I never use it myself).\n\nFutzing with the prefix that is not a prefix to reach into the working\ntree from sideways may make the relative paths given from the command line\nmean something to the current implementation, but it doesn't change the\nfundamental fact that you are introducing a funny special case where your\ncwd does _not_ mean anything with respect to which part of the working\ntree should be affected.\n\n> Some commands may want \"path in repository\" and \"path in file system\"\n> to be identical. Moreover, output from commands in such situations are\n> relative to worktree topdir (because prefix is NULL), not what users\n> expect. It's just confusing.\n\nMy gut feeling is that this is probably made more confusing, not less,\nwith the change.  Perhaps we should instead make sure this fails?\n\n    $ cd /srv/git/git.git\n    $ export GIT_DIR=$(pwd)/.git GIT_WORK_TREE=$(pwd)\n    $ cd /var/tmp ;# no git stuff there\n    $ git status\n"},{"id":"152852","messageId":"AANLkTim8R2ZxJ1_KnxxRwTjf2mz=NtgQ9MyV_7iAqEpG@mail.gmail.com","threadId":"16566","inReplyTo":"7veic2y2c5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-10-07T07:20:38Z","receivedAt":"2010-10-07T07:20:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 7, 2010 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> pclouds@gmail.com writes:\n>\n>> From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>>\n>> When both GIT_DIR and GIT_WORK_TREE are set, if cwd is outside\n>> worktree, prefix (the one passed to every builtin commands) will be\n>> set to NULL, which means \"user stays at worktree topdir\", but cwd is\n>> not moved to worktree topdir.\n>\n> Well, NULL traditionally meant \"_if_ the cwd is inside the working tree,\n> it is at the top\", not \"stays at worktree topdir\" if you started from\n> elsewhere.\n>\n> And when cwd is inside the working tree, we do want to feed paths given\n> from the command line after adding the prefix, so that we will see the\n> paths relative to the cwd after we do cd-to-topdir.\n>\n> Obviously when you run git command that requires you to be in a working\n> tree from outside the working tree, relative or full paths would not make\n> any difference.  The command should simply fail.\n>\n> I suspect that you are introducing a new semantics on top of that\n> traditional semantics; if so, you may want to state it more clearly.\n\nYes. The better semantics _when_ cwd is outside worktree.\n\n> For example:\n>\n>    When you run git command that requires you to be in a working tree\n>    from outside the working tree, the command should simply fail.\n>\n>    When GIT_WORK_TREE is in use, however, it could be argued that we do\n>    not necessarily have to be in anywhere in the working tree to perform\n>    a whole-tree operation.  Instead, we could just say the operation\n>    always runs as if the command was started at the root level of the\n>    working tree.\n>\n>    To support this new mode of operation, however, prefix needs to be\n>    adjusted to allow the program, after running chdir(2) to the root of\n>    the working tree, to refer to non-absolute paths originally given from\n>    the command line as relative to the original cwd.  This patch adds a\n>    mechanism to support that.\n>\n> I have a queasy feeling about the idea of the second paragraph above,\n> though.\n>\n> If the original cwd is inside GIT_WORK_TREE, limiting ourselves inside\n> prefix naturally limits the operation to the subdirectory we started from\n> (if the original cwd is at GIT_WORK_TREE, that would make it a whole-tree\n> operation).  A natural extension of this idea to limit the operation to\n> the part of the subtree of the working tree we started from is to refuse\n> to work in the case where the original cwd is outside GIT_WORK_TREE (the\n> current implementation of GIT_WORK_TREE may or may not correctly implement\n> it, though---I never use it myself).\n\nI tend to think that as we go up to worktree's root, prefix is shorten\nand the operation area is widen. When cwd is at worktree's, we operate\non full worktree. If it goes up one level higher, the operation area\nremains full worktree (but not everything under cwd because cwd now\ncan have non-worktree directories).\n\n> Futzing with the prefix that is not a prefix to reach into the working\n> tree from sideways may make the relative paths given from the command line\n> mean something to the current implementation, but it doesn't change the\n> fundamental fact that you are introducing a funny special case where your\n> cwd does _not_ mean anything with respect to which part of the working\n> tree should be affected.\n\nIt would mean full worktree is affected. But not full cwd. In other\nwords, in set theory, it's a union of cwd and worktree.\n\n>> Some commands may want \"path in repository\" and \"path in file system\"\n>> to be identical. Moreover, output from commands in such situations are\n>> relative to worktree topdir (because prefix is NULL), not what users\n>> expect. It's just confusing.\n>\n> My gut feeling is that this is probably made more confusing, not less,\n> with the change.  Perhaps we should instead make sure this fails?\n>\n>    $ cd /srv/git/git.git\n\nErr.. you did not mean bare repo, did you?\n\n>    $ export GIT_DIR=$(pwd)/.git GIT_WORK_TREE=$(pwd)\n>    $ cd /var/tmp ;# no git stuff there\n>    $ git status\n\nI think it used to fail some time ago. Though I see nothing wrong if\nthat command works. And it'd be better if it outputs relative to\n/var/tmp rather than /srv/git/git.git. I don't use this a lot but\nsometimes I export GIT_DIR/GIT_WORK_TREE, cd around then do \"git add\n/srv/git/git.git/blah\". To me, it should work. I may stand outside\nworktree, but I give input inside worktree.\n-- \nDuy\n"},{"id":"152879","messageId":"7vvd5evyo6.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"AANLkTim8R2ZxJ1_KnxxRwTjf2mz=NtgQ9MyV_7iAqEpG@mail.gmail.com","subject":"Re: [PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-10-07T14:56:57Z","receivedAt":"2010-10-07T14:56:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Thu, Oct 7, 2010 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> pclouds@gmail.com writes:\n>> ...\n>> If the original cwd is inside GIT_WORK_TREE, limiting ourselves inside\n>> prefix naturally limits the operation to the subdirectory we started from\n>> (if the original cwd is at GIT_WORK_TREE, that would make it a whole-tree\n>> operation).  A natural extension of this idea to limit the operation to\n>> the part of the subtree of the working tree we started from is to refuse\n>> to work in the case where the original cwd is outside GIT_WORK_TREE (the\n>> current implementation of GIT_WORK_TREE may or may not correctly implement\n>> it, though---I never use it myself).\n>\n> I tend to think that as we go up to worktree's root, prefix is shorten\n> and the operation area is widen. When cwd is at worktree's, we operate\n> on full worktree. If it goes up one level higher, the operation area\n> remains full worktree (but not everything under cwd because cwd now\n> can have non-worktree directories).\n\nI have a feeling that you did not understand my /srv/git/git.git (no, it\nis not a bare repository) vs /var/tmp example.\n\nI think it makes the new semantics much less yucky if the special case is\nlimited to \"the working tree is a subdirectory somewhere under cwd\".  But\ndoes your patch check and notice that /var/tmp is not above the working\ntree and they are completely unrelated?\n"},{"id":"152881","messageId":"AANLkTikoDMX17U77Y1+=Lq94AUAZ3mTEX7MG9aRjaEh6@mail.gmail.com","threadId":"16566","inReplyTo":"7vvd5evyo6.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/2] worktree: provide better prefix to go back to original cwd","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-10-07T15:11:58Z","receivedAt":"2010-10-07T15:11:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 7, 2010 at 9:56 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Thu, Oct 7, 2010 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> pclouds@gmail.com writes:\n>>> ...\n>>> If the original cwd is inside GIT_WORK_TREE, limiting ourselves inside\n>>> prefix naturally limits the operation to the subdirectory we started from\n>>> (if the original cwd is at GIT_WORK_TREE, that would make it a whole-tree\n>>> operation).  A natural extension of this idea to limit the operation to\n>>> the part of the subtree of the working tree we started from is to refuse\n>>> to work in the case where the original cwd is outside GIT_WORK_TREE (the\n>>> current implementation of GIT_WORK_TREE may or may not correctly implement\n>>> it, though---I never use it myself).\n>>\n>> I tend to think that as we go up to worktree's root, prefix is shorten\n>> and the operation area is widen. When cwd is at worktree's, we operate\n>> on full worktree. If it goes up one level higher, the operation area\n>> remains full worktree (but not everything under cwd because cwd now\n>> can have non-worktree directories).\n>\n> I have a feeling that you did not understand my /srv/git/git.git (no, it\n> is not a bare repository) vs /var/tmp example.\n>\n> I think it makes the new semantics much less yucky if the special case is\n> limited to \"the working tree is a subdirectory somewhere under cwd\".  But\n> does your patch check and notice that /var/tmp is not above the working\n> tree and they are completely unrelated?\n>\n\nOK I see it now. I think my patch deals with completely unrelated\nworktree/cwd just fine (both cwd-to-worktree and worktree-to-cwd\ncontain a few \"../\").\n\nThere is however a case where \"git status\" on separate cwd/worktree\nmight make sense. Suppose a master repo a/.git has two submodules\na/b/.git and a/c/.git. If user stands in a/c and want to do git-status\non all repos (let's skip how it finds out a/.git is the master repo),\nit would make sense to display paths, including ones from a/b/.git,\nrelative to a/c (which is cwd and completely unrelated to a/b/.git).\n-- \nDuy\n"},{"id":"156349","messageId":"1290479526-21167-1-git-send-email-michal.pomorski@scalado.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] Corrected cmitmode set in the right lower window when initiating a diff.","fromName":"Michał Pomorski","fromEmail":"misieck@gmail.com","sentAt":"2010-11-23T02:32:06Z","receivedAt":"2010-11-23T02:32:06Z","isPatch":true,"sender":{"key":"misieck@gmail.com","avatar":null},"body":"Under certain circumstances it caused an error: bad text index \"a:_number_\" in treeopendir:\n-Start gitk;\n-Select the tree radio button in the lower right window;\n-Choose a diff from the context menu in the main window (this->selected or vice versa)\n-Observe the lower right window: a file list is displayed, but the radio button 'tree' remains selected\n-Select one of the files listed. This should cause the error.\n\nBecause the variable cmitmode remains in the 'tree' setting, gitk is trying to calculate a tree view selection.\nThis patch corrects this by setting the cmitmode variable to 'patch' each time a diff is selected.\n---\n gitk-git/gitk |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/gitk-git/gitk b/gitk-git/gitk\nindex 1b0e09a..5d76de4 100644\n--- a/gitk-git/gitk\n+++ b/gitk-git/gitk\n@@ -7330,8 +7330,9 @@ proc mergediff {id} {\n }\n \n proc startdiff {ids} {\n-    global treediffs diffids treepending diffmergeid nullid nullid2\n+    global treediffs diffids treepending diffmergeid nullid nullid2 cmitmode\n \n+    set cmitmode patch\n     settabs 1\n     set diffids $ids\n     catch {unset diffmergeid}\n-- \n1.7.3.2\n"},{"id":"165339","messageId":"1302156081-3286-1-git-send-email-andrew@andrewgarber.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] git-p4: replace each tab with 8 spaces for consistency","fromName":"Andrew Garber","fromEmail":"andrew@andrewgarber.com","sentAt":"2011-04-07T06:01:20Z","receivedAt":"2011-04-07T06:01:20Z","isPatch":true,"sender":{"key":"andrew@andrewgarber.com","avatar":"https://avatars.githubusercontent.com/u/265048?v=4"},"body":"Note that the majority of git-p4 uses spaces, not tabs, for indentation.\nConsistent indentation is critical to the functionality of Python\nscripts: mixing tabs and spaces in Python can lead to hard-to-find bugs.\n\nAndrew Garber (1):\n  git-p4: replace each tab with 8 spaces for consistency\n\n contrib/fast-import/git-p4 |   64 ++++++++++++++++++++++----------------------\n 1 files changed, 32 insertions(+), 32 deletions(-)\n\n-- \n1.7.3.4\n"},{"id":"165340","messageId":"1302156081-3286-2-git-send-email-andrew@andrewgarber.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] git-p4: replace each tab with 8 spaces for consistency","fromName":"Andrew Garber","fromEmail":"andrew@andrewgarber.com","sentAt":"2011-04-07T06:01:21Z","receivedAt":"2011-04-07T06:01:21Z","isPatch":true,"sender":{"key":"andrew@andrewgarber.com","avatar":"https://avatars.githubusercontent.com/u/265048?v=4"},"body":"Note that the majority of git-p4 uses spaces, not tabs, for indentation.\nConsistent indentation is critical to the functionality of Python\nscripts: mixing tabs and spaces in Python can lead to hard-to-find bugs.\n\nSigned-off-by: Andrew Garber <andrew@andrewgarber.com>\n---\n contrib/fast-import/git-p4 |   64 ++++++++++++++++++++++----------------------\n 1 files changed, 32 insertions(+), 32 deletions(-)\n\ndiff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4\nindex 3881515..78e5b3a 100755\n--- a/contrib/fast-import/git-p4\n+++ b/contrib/fast-import/git-p4\n@@ -222,10 +222,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):\n     try:\n         while True:\n             entry = marshal.load(p4.stdout)\n-\t    if cb is not None:\n-\t\tcb(entry)\n-\t    else:\n-\t\tresult.append(entry)\n+            if cb is not None:\n+                cb(entry)\n+            else:\n+                result.append(entry)\n     except EOFError:\n         pass\n     exitCode = p4.wait()\n@@ -449,8 +449,8 @@ def p4ChangesForPaths(depotPaths, changeRange):\n \n     changes = {}\n     for line in output:\n-\tchangeNum = int(line.split(\" \")[1])\n-\tchanges[changeNum] = True\n+        changeNum = int(line.split(\" \")[1])\n+        changes[changeNum] = True\n \n     changelist = changes.keys()\n     changelist.sort()\n@@ -1033,10 +1033,10 @@ class P4Sync(Command):\n     # - helper for streamP4Files\n \n     def streamOneP4File(self, file, contents):\n-\tif file[\"type\"] == \"apple\":\n-\t    print \"\\nfile %s is a strange apple file that forks. Ignoring\" % \\\n-\t\tfile['depotFile']\n-\t    return\n+        if file[\"type\"] == \"apple\":\n+            print \"\\nfile %s is a strange apple file that forks. Ignoring\" % \\\n+                file['depotFile']\n+            return\n \n         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)\n         relPath = self.wildcard_decode(relPath)\n@@ -1085,22 +1085,22 @@ class P4Sync(Command):\n     # handle another chunk of streaming data\n     def streamP4FilesCb(self, marshalled):\n \n-\tif marshalled.has_key('depotFile') and self.stream_have_file_info:\n-\t    # start of a new file - output the old one first\n-\t    self.streamOneP4File(self.stream_file, self.stream_contents)\n-\t    self.stream_file = {}\n-\t    self.stream_contents = []\n-\t    self.stream_have_file_info = False\n+        if marshalled.has_key('depotFile') and self.stream_have_file_info:\n+            # start of a new file - output the old one first\n+            self.streamOneP4File(self.stream_file, self.stream_contents)\n+            self.stream_file = {}\n+            self.stream_contents = []\n+            self.stream_have_file_info = False\n \n-\t# pick up the new file information... for the\n-\t# 'data' field we need to append to our array\n-\tfor k in marshalled.keys():\n-\t    if k == 'data':\n-\t\tself.stream_contents.append(marshalled['data'])\n-\t    else:\n-\t\tself.stream_file[k] = marshalled[k]\n+        # pick up the new file information... for the\n+        # 'data' field we need to append to our array\n+        for k in marshalled.keys():\n+            if k == 'data':\n+                self.stream_contents.append(marshalled['data'])\n+            else:\n+                self.stream_file[k] = marshalled[k]\n \n-\tself.stream_have_file_info = True\n+        self.stream_have_file_info = True\n \n     # Stream directly from \"p4 files\" into \"git fast-import\"\n     def streamP4Files(self, files):\n@@ -1132,14 +1132,14 @@ class P4Sync(Command):\n             self.stream_contents = []\n             self.stream_have_file_info = False\n \n-\t    # curry self argument\n-\t    def streamP4FilesCbSelf(entry):\n-\t\tself.streamP4FilesCb(entry)\n+            # curry self argument\n+            def streamP4FilesCbSelf(entry):\n+                self.streamP4FilesCb(entry)\n \n-\t    p4CmdList(\"-x - print\",\n-\t\t'\\n'.join(['%s#%s' % (f['path'], f['rev'])\n+            p4CmdList(\"-x - print\",\n+                '\\n'.join(['%s#%s' % (f['path'], f['rev'])\n                                                   for f in filesToRead]),\n-\t        cb=streamP4FilesCbSelf)\n+                cb=streamP4FilesCbSelf)\n \n             # do the last chunk\n             if self.stream_file.has_key('depotFile'):\n@@ -1148,7 +1148,7 @@ class P4Sync(Command):\n     def commit(self, details, files, branch, branchPrefixes, parent = \"\"):\n         epoch = details[\"time\"]\n         author = details[\"user\"]\n-\tself.branchPrefixes = branchPrefixes\n+        self.branchPrefixes = branchPrefixes\n \n         if self.verbose:\n             print \"commit into %s\" % branch\n@@ -1253,7 +1253,7 @@ class P4Sync(Command):\n \n         s = ''\n         for (key, val) in self.users.items():\n-\t    s += \"%s\\t%s\\n\" % (key.expandtabs(1), val.expandtabs(1))\n+            s += \"%s\\t%s\\n\" % (key.expandtabs(1), val.expandtabs(1))\n \n         open(self.getUserCacheFilename(), \"wb\").write(s)\n         self.userMapFromPerforceServer = True\n-- \n1.7.3.4\n"},{"id":"165343","messageId":"7vhbaafr7r.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"1302156081-3286-1-git-send-email-andrew@andrewgarber.com","subject":"Re: [PATCH] git-p4: replace each tab with 8 spaces for consistency","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-07T07:36:24Z","receivedAt":"2011-04-07T07:36:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Garber <andrew@andrewgarber.com> writes:\n\n> Note that the majority of git-p4 uses spaces, not tabs, for indentation.\n> Consistent indentation is critical to the functionality of Python\n> scripts: mixing tabs and spaces in Python can lead to hard-to-find bugs.\n\nMixing tabs and spaces can lead to hard-to-find bugs, I agree.\n\nI just threw this in to my .git/info/attributes:\n\n    contrib/fast-import/git-p4 whitespace=!indent,tail,tab-in-indent\n\nand then did this:\n\n    $ >contrib/fast-import/git-p4\n    $ git diff -R | git apply --whitespace=fix\n    $ git diff\n\nThe changes I get out of the last step seem to exactly match your patch.\n\nThanks.\n"},{"id":"165361","messageId":"4D9DE7E4.1030808@cisco.com","threadId":"16566","inReplyTo":"7vhbaafr7r.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] git-p4: replace each tab with 8 spaces for consistency","fromName":"Phil Hord","fromEmail":"hordp@cisco.com","sentAt":"2011-04-07T16:35:48Z","receivedAt":"2011-04-07T16:35:48Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On 04/07/2011 03:36 AM, Junio C Hamano wrote:\n> Mixing tabs and spaces can lead to hard-to-find bugs, I agree.\n>\n> I just threw this in to my .git/info/attributes:\n>\n>     contrib/fast-import/git-p4 whitespace=!indent,tail,tab-in-indent\n>\n> and then did this:\n>\n>     $ >contrib/fast-import/git-p4\n>     $ git diff -R | git apply --whitespace=fix\n>     $ git diff\n>\n> The changes I get out of the last step seem to exactly match your patch.\n\nJunio!  Thanks for that recipe.\n\nI suspected there must be some way to employ git-apply's whitespace=fix\nfeature to ws-normalize my code, but I was clueless how to do that.  I\nwound up cobbling together a python script that reimplements the same\nfixes (hopefully).\n\nI wish this (the git-apply trick) was documented somewhere and/or made\nmore prominent.  Is it, and I have also overlooked it?\n\nThis is what I want:\n\n   git fix-whitespace             # fix new ws errors in workdir\n   git fix-whitespace --all       # fix all ws errors in workdir\n   git fix-whitespace foo         # fix new ws errors since foo\n   git fix-whitespace --cached    # fix new ws errors in the index\n   etc.\n\nIs this already implemented somewhere in the git porcelain?\n\nPhil\n"},{"id":"165365","messageId":"7v1v1dgat7.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"4D9DE7E4.1030808@cisco.com","subject":"Re: [PATCH] git-p4: replace each tab with 8 spaces for consistency","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-07T18:45:24Z","receivedAt":"2011-04-07T18:45:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phil Hord <hordp@cisco.com> writes:\n\n> This is what I want:\n>\n>    git fix-whitespace             # fix new ws errors in workdir\n>    git fix-whitespace --all       # fix all ws errors in workdir\n>    git fix-whitespace foo         # fix new ws errors since foo\n>    git fix-whitespace --cached    # fix new ws errors in the index\n>    etc.\n\nIf you understood the example I gave, you should be able to figure all of\nthese out.  In the most general case, you would come up with a way to\ngenerate a diff that represents the change you want to fix (e.g. \"new\nerrors\" would be comparing the HEAD and the working tree), reverse apply\nthe diff and then forward apply the same diff with --whitespace=fix, but\noften you can omit \"reverse apply the diff\" step by finding an appropriate\ncombination between HEAD/index/working tree.\n\nI long time wrote this alias\n\n[alias]\n        wsadd = \"!sh -c 'git diff -- \\\"$@\\\" | git apply --cached --whitespace=fix;\\\n                git co -- ${1-.} \\\"$@\\\"' -\"\n\nthat acts as if you are saying \"git add $path\" while fixing the whitespace\nerrors I introduced in the working tree version.\n"},{"id":"192835","messageId":"1338837351-8996-1-git-send-email-Pavel.Volek@ensimag.imag.fr","threadId":"16566","inReplyTo":"y","subject":"[PATCH/RFC] file import functionality for git-remote-mw","fromName":"Pavel Volek","fromEmail":"pavel.volek@ensimag.imag.fr","sentAt":"2012-06-04T19:15:51Z","receivedAt":"2012-06-04T19:15:51Z","isPatch":true,"sender":{"key":"pavel.volek@ensimag.imag.fr","avatar":null},"body":"From: Volek Pavel <me@pavelvolek.cz>\n\nThe current version of the git-remote-mediawiki supports only import and export of the pages, doesn't support import and export of file attachements which are also exposed by MediaWiki API. This patch adds the functionality to import the last versions of the files and all versions of description pages for these files.\n\nSigned-off-by: Pavel Volek <Pavel.Volek@ensimag.imag.fr>\nSigned-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\nSigned-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n contrib/mw-to-git/git-remote-mediawiki | 128 +++++++++++++++++++++++++++++++--\n 1 file changed, 123 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki\nindex c18bfa1..4168218 100755\n--- a/contrib/mw-to-git/git-remote-mediawiki\n+++ b/contrib/mw-to-git/git-remote-mediawiki\n@@ -267,6 +267,39 @@ sub get_mw_pages {\n \treturn values(%pages);\n }\n \n+sub get_mw_media_pages {\n+\tmw_connect_maybe();\n+\n+\tmy %pages; # hash on page titles to avoid duplicates\n+\n+\t# get all pages for mediafiles (they are in different namespace)\n+\t# only one namespace can be queried at the same moment\n+\tmy $mw_pages = $mediawiki->list({\n+\t\taction => 'query',\n+\t\tlist => 'allpages',\n+\t\tapnamespace => get_mw_namespace_id(\"File\"),\n+\t\taplimit => 500,\n+\t});\n+\tif (!defined($mw_pages)) {\n+\t\tprint STDERR \"fatal: could not get the list of media file pages.\\n\";\n+\t\tprint STDERR \"fatal: '$url' does not appear to be a mediawiki\\n\";\n+\t\tprint STDERR \"fatal: make sure '$url/api.php' is a valid page.\\n\";\n+\t\texit 1;\n+\t}\n+\tforeach my $page (@{$mw_pages}) {\n+\t\t$pages{$page->{title}} = $page;\n+\t}\n+\treturn values(%pages);\n+}\n+\n+sub get_all_mw_pages() {\n+\tmy @pages = get_mw_pages();\n+\tmy @media_pages = get_mw_media_pages();\n+\tpush(@pages,@media_pages);\n+\n+\treturn @pages;\n+}\n+\n sub run_git {\n \topen(my $git, \"-|:encoding(UTF-8)\", \"git \" . $_[0]);\n \tmy $res = do { local $/; <$git> };\n@@ -300,7 +333,7 @@ my %basetimestamps;\n sub get_last_remote_revision {\n \tmw_connect_maybe();\n \n-\tmy @pages = get_mw_pages();\n+\tmy @pages = get_all_mw_pages();\n \n \tmy $max_rev_num = 0;\n \n@@ -403,6 +436,25 @@ sub mw_option {\n \tprint STDOUT \"unsupported\\n\";\n }\n \n+# Returns MediaWiki id for a canonical namespace name. Ex.: \"File\", \"Project\".\n+sub get_mw_namespace_id() {\n+\tmw_connect_maybe();\n+\tmy $name = shift;\n+\tmy $query = {\n+\t\taction => 'query',\n+\t\tmeta => 'siteinfo',\n+\t\tsiprop => 'namespaces',\n+\t};\n+\tmy $result = $mediawiki->api($query);\n+\n+\twhile (my ($id, $ns) = each(%{$result->{query}->{namespaces}})) {\n+\t\tif (defined($ns->{canonical}) && ($ns->{canonical} eq $name)) {\n+\t\t\treturn $ns->{id};\n+\t\t}\n+\t}\n+\tdie \"Namespace $name was not found on MediaWiki.\";\n+}\n+\n sub fetch_mw_revisions_for_page {\n \tmy $page = shift;\n \tmy $id = shift;\n@@ -461,11 +513,37 @@ sub fetch_mw_revisions {\n \treturn ($n, @revisions);\n }\n \n+sub download_mw_mediafile {\n+\tmy $filename = shift;\n+\n+\t$mediawiki->{config}->{files_url} = $url;\n+\n+\tmy $file = $mediawiki->download( { title => $filename } )\n+\t\t\t|| die 'Fatal: Error ' .\n+\t\t\t\t$mediawiki->{error}->{code} .\n+\t\t\t\t' from mediwiki: ' . $mediawiki->{error}->{details};\n+\tif (!defined($file)){\n+\t\tprint STDERR \"\\tFile \\'$filename\\' could not be downloaded.\\n\";\n+\t\texit 1;\n+\t} elsif ($file eq \"\") {\n+\t\tprint STDERR \"\\tFile \\'$filename\\' does not exist on the wiki.\\n\";\n+\t\texit 1;\n+\t} else {\n+\t\treturn $file;\n+\t}\n+}\n+\n sub import_file_revision {\n \tmy $commit = shift;\n \tmy %commit = %{$commit};\n \tmy $full_import = shift;\n \tmy $n = shift;\n+\tmy $mediafile_import = shift;\n+\tmy $mediafile;my %mediafile;\n+\tif ($mediafile_import) {\n+\t\t$mediafile = shift;\n+\t\t%mediafile = %{$mediafile};\n+\t}\n \n \tmy $title = $commit{title};\n \tmy $comment = $commit{comment};\n@@ -485,6 +563,10 @@ sub import_file_revision {\n \tif ($content ne DELETED_CONTENT) {\n \t\tprint STDOUT \"M 644 inline $title.mw\\n\";\n \t\tliteral_data($content);\n+\t\tif ($mediafile_import) {\n+\t\t\tprint STDOUT \"M 644 inline $mediafile{title}\\n\";\n+\t\t\tliteral_data($mediafile{content});\n+\t\t}\n \t\tprint STDOUT \"\\n\\n\";\n \t} else {\n \t\tprint STDOUT \"D $title.mw\\n\";\n@@ -547,7 +629,7 @@ sub mw_import_ref {\n \n \tmw_connect_maybe();\n \n-\tmy @pages = get_mw_pages();\n+\tmy @pages = get_all_mw_pages();\n \n \tprint STDERR \"Searching revisions...\\n\";\n \tmy $last_local = get_last_local_revision();\n@@ -580,6 +662,7 @@ sub mw_import_ref {\n \n \t\t$n++;\n \n+\t\tmy $page_title = $result->{query}->{pages}->{$pagerevid->{pageid}}->{title};\n \t\tmy %commit;\n \t\t$commit{author} = $rev->{user} || 'Anonymous';\n \t\t$commit{comment} = $rev->{comment} || '*Empty MediaWiki Message*';\n@@ -596,9 +679,44 @@ sub mw_import_ref {\n \t\t}\n \t\t$commit{date} = DateTime::Format::ISO8601->parse_datetime($last_timestamp);\n \n-\t\tprint STDERR \"$n/\", scalar(@revisions), \": Revision #$pagerevid->{revid} of $commit{title}\\n\";\n-\n-\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n);\n+\t\t# differentiates classic pages and media pages\n+\t\tmy @prefix = split (\":\",$page_title);\n+\t\tif ($prefix[0] eq \"File\" || $prefix[0] eq \"Image\") {\n+\t\t\t# check if there is a corresponding mediafile with the same timestamp => it is page\n+\t\t\t# for new verion of the file (not only for new version of the description of the file)\n+\t\t\t# => download corresponding file version\n+\t\t\t$query = {\n+\t\t\t\taction => 'query',\n+\t\t\t\tprop => 'imageinfo',\n+\t\t\t\ttitles => $page_title,\n+\t\t\t\tiistart => $rev->{timestamp},\n+\t\t\t\tiiend => $rev->{timestamp},\n+\t\t\t\tiiprop => 'timestamp|archivename',\n+\t\t\t\tiilimit => 1,\n+\t\t\t};\n+\t\t\t$result = $mediawiki->api($query);\n+\n+\t\t\tmy ($imageid,$imageinfo) = each ( %{$result->{query}->{pages}} );\n+\t\t\t# page has a related version of the file\n+\t\t\tif (defined($imageinfo->{imageinfo})) {\n+\t\t\t\tforeach ( @{$imageinfo->{imageinfo}} ) {\n+\t\t\t\t\tmy %mediafile;\n+\t\t\t\t\tif ($prefix[0] eq \"File\") { $mediafile{title} = substr $page_title, 5; }\n+\t\t\t\t\telse { $mediafile{title} = substr $page_title, 6; }\n+\n+\t\t\t\t\t$mediafile{content} = download_mw_mediafile(\"File:\".$mediafile{title});\n+\t\t\t\t\tprint STDERR \"$n/\", scalar(@revisions), \": Revision #$pagerevid->{revid} of $commit{title}\\n\";\n+\t\t\t\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n, 1, \\%mediafile);\n+\t\t\t\t}\n+\t\t\t# page has no related version of the file, do commit only for the page\n+\t\t\t} else {\n+\t\t\t\tprint STDERR \"$n/\", scalar(@revisions), \": Revision #$pagerevid->{revid} of $commit{title}\\n\";\n+\t\t\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n, 0);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tprint STDERR \"$n/\", scalar(@revisions), \": Revision #$pagerevid->{revid} of $commit{title}\\n\";\n+\t\t\timport_file_revision(\\%commit, ($fetch_from == 1), $n, 0);\n+\t\t}\n \t}\n \n \tif ($fetch_from == 1 && $n == 0) {\n-- \n1.7.10.2.552.gaa3bb87\n"},{"id":"192851","messageId":"1338842961-3477-1-git-send-email-nguyenkimthuat@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"NGUYEN Kim Thuat","fromEmail":"kim-thuat.nguyen@ensimag.imag.fr","sentAt":"2012-06-04T20:49:21Z","receivedAt":"2012-06-04T20:49:21Z","isPatch":true,"sender":{"key":"kim-thuat.nguyen@ensimag.imag.fr","avatar":null},"body":"From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n\nThe current version of the git-remote-mediawiki supports only import and export of the pages, doesn't support import and export of file attachements which are also exposed by MediaWiki API. This patch add the functionnality to export the file attachements from the local git's repository using the API of mediawiki.\n For the moment, i removed the line: \"use enconding 'utf8'\" because it's broken, especially when reding a binary file for exemple .pdf or .jpg.\n\nSigned-off-by: Pavel Volek <Pavel.Volek@ensimag.imag.fr>\nSigned-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\nSigned-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n contrib/mw-to-git/git-remote-mediawiki | 90 ++++++++++++++++++++++++++++++++--\n 1 file changed, 87 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki\nindex c18bfa1..830e2d0 100755\n--- a/contrib/mw-to-git/git-remote-mediawiki\n+++ b/contrib/mw-to-git/git-remote-mediawiki\n@@ -36,7 +36,6 @@\n use strict;\n use MediaWiki::API;\n use DateTime::Format::ISO8601;\n-use encoding 'utf8';\n \n # use encoding 'utf8' doesn't change STDERROR\n # but we're going to output UTF-8 filenames to STDERR\n@@ -114,6 +113,9 @@ $wiki_name =~ s/[^\\/]*:\\/\\///;\n # and '@' sign, to avoid author like MWUser@HTTPUser@host.com\n $wiki_name =~ s/^.*@//;\n \n+# Get the list of file extensions supported by the current version of mediawiki \n+my @list_file_extensions = get_file_extensions();\n+\n # Commands parser\n my $entry;\n my @cmd;\n@@ -275,6 +277,13 @@ sub run_git {\n \treturn $res;\n }\n \n+sub run_git_raw {\n+        open(my $g,\"-|\",\"git \" . $_[0]);   \n+        my $r = do { local $/; <$g> };\n+        close($g);\n+\n+        return $r;\n+}\n \n sub get_last_local_revision {\n \t# Get note regarding last mediawiki revision\n@@ -642,8 +651,14 @@ sub mw_push_file {\n \tmy $old_sha1 = $diff_info_split[2];\n \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n+\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n \n+\tmy %hashFiles = map {$_ => 1}@list_file_extensions;\n+\tmy $path = \"File:\".$complete_file_name;\n+\tmy @extensions = split(/\\./,$complete_file_name);\n+\tmy $extension = pop(@extensions);\n+\n \tif (substr($complete_file_name,-3) eq \".mw\") {\n \t\tmy $title = substr($complete_file_name,0,-3);\n \n@@ -686,8 +701,34 @@ sub mw_push_file {\n \t\t}\n \t\t$newrevid = $result->{edit}->{newrevid};\n \t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n-\t} else {\n-\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n+\t} elsif (exists($hashFiles{$extension})) {\n+\t\t# Deleting and uploading a file require the priviledge of the user\n+\t\tif ($file_deleted) {\n+\t\t\tmw_connect_maybe();\n+\t\t\tmy $res = $mediawiki->edit( {\n+\t\t\taction => 'delete',\n+\t\t\ttitle => $path,\n+\t\t\treason => $summary } )\n+\t\t\t|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n+\n+\t\t} else {\n+\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n+\t\t\tmw_connect_maybe();\n+\t\t\t\t$mediawiki->{config}->{upload_url} = \"$url/index.php/Special:Upload\";\n+\n+\t\t\t\t$mediawiki->upload( {\n+\t\t\t\ttitle => $complete_file_name,\n+\t\t\t\tsummary => $summary,\n+\t\t\t\tdata => $content,\n+\t\t\t\tignorewarnings=>1\n+                                  }, {\n+\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated characters\n+\t\t\t} ) || die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n+\t\t\t$newrevid = get_reviId_filepage();\n+\t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name\\n\";\n+\t\t\t }\n+\telse {\n+\t\tprint STDERR \"$complete_file_name is not supported on this version of Mediawiki.\\n\"\n \t}\n \treturn ($newrevid, \"ok\");\n }\n@@ -825,3 +866,46 @@ sub mw_push_revision {\n \tprint STDOUT \"ok $remote\\n\";\n \treturn 1;\n }\n+\n+sub get_reviId_filepage() {\n+\tmw_connect_maybe();\n+\n+\tmy $max_rev_num_file = 0;\n+\n+\tmy @list_file_pages = get_mw_media_pages();\n+\n+\tforeach my $file_page (@list_file_pages) {\n+\tmy $id = $file_page->{pageid};\n+        \n+\tmy $query = {\n+\t\taction => 'query',\n+\t\tprop => 'revisions',\n+\t\trvprop => 'ids',\n+\t\tpageids => $id,\n+\t\t};\n+\n+\t\tmy $result = $mediawiki->api($query);\n+\n+\t\tmy $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}});\n+\t}\n+}\n+\n+sub get_file_extensions {\n+\tmw_connect_maybe();\n+\n+\tmy $query = {\n+\taction => 'query',\n+\tmeta => 'siteinfo',\n+\tsiprop => 'fileextensions'\n+\t};\n+\n+\tmy $result = $mediawiki->api($query);\n+\n+\tmy @file_extensions = map $_->{ext},@{$result->{query}->{fileextensions}};\n+\n+\treturn @file_extensions;\n+}\n+\n+\n+\n+\n-- \n1.7.10.2.552.gaa3bb87\n"},{"id":"192853","messageId":"vpqd35en6h8.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"1338837351-8996-1-git-send-email-Pavel.Volek@ensimag.imag.fr","subject":"Re: [PATCH/RFC] file import functionality for git-remote-mw","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-04T20:54:11Z","receivedAt":"2012-06-04T20:54:11Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Pavel Volek <Pavel.Volek@ensimag.imag.fr> writes:\n\n> +sub get_mw_media_pages {\n> +\tmw_connect_maybe();\n> +\n> +\tmy %pages; # hash on page titles to avoid duplicates\n> +\n> +\t# get all pages for mediafiles (they are in different namespace)\n> +\t# only one namespace can be queried at the same moment\n> +\tmy $mw_pages = $mediawiki->list({\n> +\t\taction => 'query',\n> +\t\tlist => 'allpages',\n> +\t\tapnamespace => get_mw_namespace_id(\"File\"),\n> +\t\taplimit => 500,\n> +\t});\n\nThis seems to be done unconditionally. Is this reasonable if the user\nhas explicitely set remote.origin.pages or remote.origin.categories?\n\nActually, shouldn't this be added to get_mw_pages, next to the code\ndealing with these two variables? Perhaps the function should be\nsplit into multiple functions, along the lines of:\n\nsub get_mw_pages {\n\tmw_connect_maybe();\n\n        my %pages;\n\tif (@tracked_pages) {\n\t\t$user_defined = 1;\n\t\tget_mw_tracked_pages(\\%pages);\n\t}\n\tif (@tracked_categories) {\n\t\t$user_defined = 1;\n\t\tget_mw_tracked_categories(\\%pages);\n\t}\n\tif (!$user_defined) {\n\t\tget_mw_all_pages(\\%pages);\n\t}\n\treturn values(%pages);\n}\n\nAnd your code would need to take these 3 options into account.\n\n> +sub get_all_mw_pages() {\n> +\tmy @pages = get_mw_pages();\n> +\tmy @media_pages = get_mw_media_pages();\n> +\tpush(@pages,@media_pages);\n\nSpace after comma.\n\n> +# Returns MediaWiki id for a canonical namespace name. Ex.: \"File\", \"Project\".\n> +sub get_mw_namespace_id() {\n> +\tmw_connect_maybe();\n> +\tmy $name = shift;\n> +\tmy $query = {\n> +\t\taction => 'query',\n> +\t\tmeta => 'siteinfo',\n> +\t\tsiprop => 'namespaces',\n> +\t};\n> +\tmy $result = $mediawiki->api($query);\n\nIt may make sense to cache the result, to avoid querying the API\nmultiple times if you call the function more than once. We can even\ncache this in a configuration variable as the namespace identifiers are\nunlikely to change for a given wiki.\n\n> +\tif (!defined($file)){\n\nSpace between ) and { please.\n\n> +\t\tmy @prefix = split (\":\",$page_title);\n\nSpace after , please.\n\n> +\t\tif ($prefix[0] eq \"File\" || $prefix[0] eq \"Image\") {\n> +\t\t\t# check if there is a corresponding mediafile with the same timestamp => it is page\n> +\t\t\t# for new verion of the file (not only for new version of the description of the file)\n\n> +\t\t\t# => download corresponding file version\n\nDon't make long lines like this. In general, we avoid lines longer than\n80 characters (or even a bit less), these are >100 and the following are\nworse.\n\nLong lines are usually an indication that you did not structure your\ncode into functions, and this diagnosis seems to apply here.\n\n> +\t\t\tmy ($imageid,$imageinfo) = each ( %{$result->{query}->{pages}} );\n\nSpace after \",\".\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192857","messageId":"vpqvcj6lq1m.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"1338842961-3477-1-git-send-email-nguyenkimthuat@gmail.com","subject":"Re: [PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-04T21:34:29Z","receivedAt":"2012-06-04T21:34:29Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"NGUYEN Kim Thuat <kim-thuat.nguyen@ensimag.imag.fr> writes:\n\n> +# Get the list of file extensions supported by the current version of mediawiki \n> +my @list_file_extensions = get_file_extensions();\n\nYou should do it only on demand (like $mediawiki is created lazily).\n\n> +        open(my $g,\"-|\",\"git \" . $_[0]);   \n\nSpace after \",\".\n\n> @@ -642,8 +651,14 @@ sub mw_push_file {\n>  \tmy $old_sha1 = $diff_info_split[2];\n>  \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n>  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n> +\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n\nThis line looks suspiciously similar to the previous one. Do you need\nanother variable for the same value?\n\n> +\tmy @extensions = split(/\\./,$complete_file_name);\n\nSpace after \",\" (many more instances after).\n\n> -\t} else {\n> -\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n> +\t} elsif (exists($hashFiles{$extension})) {\n> +\t\t# Deleting and uploading a file require the priviledge of the user\n> +\t\tif ($file_deleted) {\n> +\t\t\tmw_connect_maybe();\n> +\t\t\tmy $res = $mediawiki->edit( {\n> +\t\t\taction => 'delete',\n> +\t\t\ttitle => $path,\n> +\t\t\treason => $summary } )\n\nIndent the body of {} please.\n\n> +\t\t} else {\n> +\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n> +\t\t\tmw_connect_maybe();\n> +\t\t\t\t$mediawiki->{config}->{upload_url} = \"$url/index.php/Special:Upload\";\n\nBroken indentation.\n\nDoes this work on wiki configured in foreign languages, like french that\nhas Spécial:Téléverser instead?\n\n> +\t\t\t} ) || die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n> +\t\t\t$newrevid = get_reviId_filepage();\n\nThis queries the wiki to get the last revision id. The existing code (to\ndeal with page) could get this from the response of the API to the edit\nrequest, like this:\n\n\t\t$newrevid = $result->{edit}->{newrevid};\n\nYour version is much more inefficient (many requests each time you\nupload a file), and has a race condition (what happens if someone else\ncreates a revision at the same time?). Isn't there a better way?\n\n> +\t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name\\n\";\n> +\t\t\t }\n\nBroken indentation.\n\n> +\telse {\n> +\t\tprint STDERR \"$complete_file_name is not supported on this version of Mediawiki.\\n\"\n\nIt's not a matter of version, it's a matter of configuration.\n\n> +sub get_reviId_filepage() {\n\nStrange name (two consecutive i?). If this function fetches the last\nwiki revision, why not call it get_last_mw_revid or something like this?\n\n> +\tmw_connect_maybe();\n> +\n> +\tmy $max_rev_num_file = 0;\n> +\n> +\tmy @list_file_pages = get_mw_media_pages();\n> +\n> +\tforeach my $file_page (@list_file_pages) {\n> +\tmy $id = $file_page->{pageid};\n\nBroken indentation.\n\n> +\tmy $query = {\n> +\t\taction => 'query',\n> +\t\tprop => 'revisions',\n> +\t\trvprop => 'ids',\n> +\t\tpageids => $id,\n> +\t\t};\n> +\n> +\t\tmy $result = $mediawiki->api($query);\n\nBroken indentation.\n\n> +sub get_file_extensions {\n> +\tmw_connect_maybe();\n> +\n> +\tmy $query = {\n> +\taction => 'query',\n> +\tmeta => 'siteinfo',\n> +\tsiprop => 'fileextensions'\n> +\t};\n\nBroken indentation.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192928","messageId":"59db730bded272b60cbd16806c757660@ensibm.imag.fr","threadId":"16566","inReplyTo":"vpqvcj6lq1m.fsf@bauges.imag.fr","subject":"Re: [PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"nguyenki","fromEmail":"nguyenki@ensibm.imag.fr","sentAt":"2012-06-05T17:00:01Z","receivedAt":"2012-06-05T17:00:01Z","isPatch":true,"sender":{"key":"nguyenki@ensibm.imag.fr","avatar":null},"body":"On Mon, 04 Jun 2012 23:34:29 +0200, Matthieu Moy wrote:\n> NGUYEN Kim Thuat <kim-thuat.nguyen@ensimag.imag.fr> writes:\n>\n>> +# Get the list of file extensions supported by the current version \n>> of mediawiki\n>> +my @list_file_extensions = get_file_extensions();\n>\n> You should do it only on demand (like $mediawiki is created lazily).\nyeah, i changed the code in this part, it look like:\n\n@@ -113,9 +113,6 @@ $wiki_name =~ s/[^\\/]*:\\/\\///;\n  # and '@' sign, to avoid author like MWUser@HTTPUser@host.com\n  $wiki_name =~ s/^.*@//;\n\n-# Get the list of file extensions supported by the current version of \nmediawiki\n-my @list_file_extensions = get_file_extensions();\n-\n  # Commands parser\n  my $entry;\n  my @cmd;\n@@ -654,7 +651,7 @@ sub mw_push_file {\n  \tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n  \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n\n-\tmy %hashFiles = map {$_ => 1}@list_file_extensions;\n+\tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n  \tmy $path = \"File:\".$complete_file_name;\n  \tmy @extensions = split(/\\./,$complete_file_name);\n  \tmy $extension = pop(@extensions);\n\n-sub get_file_extensions {\n-\tmw_connect_maybe();\n-\n-\tmy $query = {\n-\taction => 'query',\n-\tmeta => 'siteinfo',\n-\tsiprop => 'fileextensions'\n-\t};\n+sub get_file_extensions_maybe {\n+\tmy $file_name = shift;\n+\tmy $est_mw_page = substr($file_name,-3) eq \".mw\";\n+\tif(!$est_mw_page) {\n+\t\tmw_connect_maybe();\n\n-\tmy $result = $mediawiki->api($query);\n+\t\tmy $query = {\n+\t\t\taction => 'query',\n+\t\t\tmeta => 'siteinfo',\n+\t\t\tsiprop => 'fileextensions'\n+\t\t\t};\n\n-\tmy @file_extensions = map \n$_->{ext},@{$result->{query}->{fileextensions}};\n+\t\tmy $result = $mediawiki->api($query);\n+\t\tmy @file_extensions = map \n$_->{ext},@{$result->{query}->{fileextensions}};\n+\t\tmy %hashFile = map {$_ => 1}@file_extensions;\n\n-\treturn @file_extensions;\n+\t\treturn %hashFile;\n+\t} else {\n+\t\treturn ;\n+\t}\n  }\n\n  Now, the function will list the file extensions on demand.\n\n\n>> @@ -642,8 +651,14 @@ sub mw_push_file {\n>>  \tmy $old_sha1 = $diff_info_split[2];\n>>  \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n>>  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n>> +\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n>\n> This line looks suspiciously similar to the previous one. Do you need\n> another variable for the same value?\nYes, it's true. I just want the code to be more visible. Because, when \nwe delete a file attachment, it's not a page wiki.\n\n> Does this work on wiki configured in foreign languages, like french \n> that\n> has Spécial:Téléverser instead?\n>\n\n>> +\telse {\n>> +\t\tprint STDERR \"$complete_file_name is not supported on this \n>> version of Mediawiki.\\n\"\n>\n> It's not a matter of version, it's a matter of configuration.\nWhat do you think if i change it like:\n         else {\n\tprint STDERR \"$complete_file_name is not a permitted file type. Check \nyour configuration for more information\\n\"\n"},{"id":"192929","messageId":"vpq62b5g047.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"59db730bded272b60cbd16806c757660@ensibm.imag.fr","subject":"Re: [PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-05T17:05:44Z","receivedAt":"2012-06-05T17:05:44Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"nguyenki <nguyenki@ensibm.imag.fr> writes:\n\n>>> @@ -642,8 +651,14 @@ sub mw_push_file {\n>>>  \tmy $old_sha1 = $diff_info_split[2];\n>>>  \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n>>>  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n>>> +\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n>>\n>> This line looks suspiciously similar to the previous one. Do you need\n>> another variable for the same value?\n> Yes, it's true. I just want the code to be more visible. Because, when\n> we delete a file attachment, it's not a page wiki.\n\nI still don't see a reason to have two variables for the same thing. If\n$page_deleted is not a good name, then rename the variable to\nsomething more general (e.g. $path_deleted ?)\n\n>>> +\telse {\n>>> +\t\tprint STDERR \"$complete_file_name is not supported on\n>>> this version of Mediawiki.\\n\"\n>>\n>> It's not a matter of version, it's a matter of configuration.\n> What do you think if i change it like:\n>         else {\n> \tprint STDERR \"$complete_file_name is not a permitted file\n> type. Check your configuration for more information\\n\"\n\nThat's better, but it doesn't tell the user which configuration to\ncheck (Git's one, or the wiki's one).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"192933","messageId":"d94a39850ce5bbf2dcab73c751574a1a@ensibm.imag.fr","threadId":"16566","inReplyTo":"vpqvcj6lq1m.fsf@bauges.imag.fr","subject":"Re: [PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"nguyenki","fromEmail":"nguyenki@ensibm.imag.fr","sentAt":"2012-06-05T17:11:52Z","receivedAt":"2012-06-05T17:11:52Z","isPatch":true,"sender":{"key":"nguyenki@ensibm.imag.fr","avatar":null},"body":"On Mon, 04 Jun 2012 23:34:29 +0200, Matthieu Moy wrote:\n> NGUYEN Kim Thuat <kim-thuat.nguyen@ensimag.imag.fr> writes:\n>\n\n>> +\t\t} else {\n>> +\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n>> +\t\t\tmw_connect_maybe();\n>> +\t\t\t\t$mediawiki->{config}->{upload_url} = \n>> \"$url/index.php/Special:Upload\";\n>\n> Broken indentation.\n>\n> Does this work on wiki configured in foreign languages, like french \n> that\n> has Spécial:Téléverser instead?\n>\n\n[[Special:Upload]] will work on any wiki, since the English names \n(known\ninternally as the \"canonical\" page) will work regardless of the wiki's\nlanguage.\n\nFor example, if you go to http://fr.wikipedia.org/wiki/Special:Upload, \nyou'll\nsee that it redirects automatically.\n"},{"id":"193019","messageId":"24f8b089a3ca00153ca927d6a5e4090b@ensibm.imag.fr","threadId":"16566","inReplyTo":"vpq62b5g047.fsf@bauges.imag.fr","subject":"Re: [PATCH/RFC] Export file attachements in git-remote-mediawiki","fromName":"nguyenki","fromEmail":"nguyenki@ensibm.imag.fr","sentAt":"2012-06-06T22:25:47Z","receivedAt":"2012-06-06T22:25:47Z","isPatch":true,"sender":{"key":"nguyenki@ensibm.imag.fr","avatar":null},"body":"On Tue, 05 Jun 2012 19:05:44 +0200, Matthieu Moy wrote:\n> nguyenki <nguyenki@ensibm.imag.fr> writes:\n>\n>>>> @@ -642,8 +651,14 @@ sub mw_push_file {\n>>>>  \tmy $old_sha1 = $diff_info_split[2];\n>>>>  \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n>>>>  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n>>>> +\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n>>>\n>>> This line looks suspiciously similar to the previous one. Do you \n>>> need\n>>> another variable for the same value?\n>> Yes, it's true. I just want the code to be more visible. Because, \n>> when\n>> we delete a file attachment, it's not a page wiki.\n>\n> I still don't see a reason to have two variables for the same thing. \n> If\n> $page_deleted is not a good name, then rename the variable to\n> something more general (e.g. $path_deleted ?)\n>\n\n>>>> +\telse {\n>>>> +\t\tprint STDERR \"$complete_file_name is not supported on\n>>>> this version of Mediawiki.\\n\"\n>>>\n>>> It's not a matter of version, it's a matter of configuration.\n>> What do you think if i change it like:\n>>         else {\n>> \tprint STDERR \"$complete_file_name is not a permitted file\n>> type. Check your configuration for more information\\n\"\n>\n> That's better, but it doesn't tell the user which configuration to\n> check (Git's one, or the wiki's one).\nThank for your advices, i changed the code from your suggestions, now \nit look like:\n@@ -36,6 +36,7 @@\n  use strict;\n  use MediaWiki::API;\n  use DateTime::Format::ISO8601;\n+use encoding 'utf8';\n\n  # use encoding 'utf8' doesn't change STDERROR\n  # but we're going to output UTF-8 filenames to STDERR\n@@ -275,7 +276,8 @@ sub run_git {\n  }\n\n  sub run_git_raw {\n-        open(my $g,\"-|\",\"git \" . $_[0]);\n+\tno encoding 'utf8';\n+        open(my $g, \"-|:utf8\", \"git \" . $_[0]);\n          my $r = do { local $/; <$g> };\n          close($g);\n\n@@ -648,7 +650,6 @@ sub mw_push_file {\n  \tmy $old_sha1 = $diff_info_split[2];\n  \tmy $page_created = ($old_sha1 eq NULL_SHA1);\n  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n-\tmy $file_deleted = ($new_sha1 eq NULL_SHA1);\n  \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n\n  \tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n@@ -700,28 +701,29 @@ sub mw_push_file {\n  \t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n  \t} elsif (exists($hashFiles{$extension})) {\n  \t\t# Deleting and uploading a file require the priviledge of the user\n-\t\tif ($file_deleted) {\n+\t\tif ($page_deleted) {\n  \t\t\tmw_connect_maybe();\n  \t\t\tmy $res = $mediawiki->edit( {\n-\t\t\taction => 'delete',\n-\t\t\ttitle => $path,\n-\t\t\treason => $summary } )\n+\t\t\t\taction => 'delete',\n+\t\t\t\ttitle => $path,\n+\t\t\t\treason => $summary } )\n  \t\t\t|| die $mediawiki-> {error}->{code} . ':' . \n$mediawiki->{error}->{details};\n\n  \t\t} else {\n  \t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n  \t\t\tmw_connect_maybe();\n-\t\t\t\t$mediawiki->{config}->{upload_url} = \n\"$url/index.php/Special:Upload\";\n+\t\t\t$mediawiki->{config}->{upload_url} = \n\"$url/index.php/Special:Upload\";\n\n-\t\t\t\t$mediawiki->upload( {\n+\t\t\t$mediawiki->upload( {\n  \t\t\t\ttitle => $complete_file_name,\n  \t\t\t\tsummary => $summary,\n  \t\t\t\tdata => $content,\n  \t\t\t\tignorewarnings=>1\n-                                  }, {\n-\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated characters\n-\t\t\t} ) || die $mediawiki-> {error}->{code} . ':' . \n$mediawiki->{error}->{details};\n-\t\t\t$newrevid = get_reviId_filepage();\n+\t\t\t\t\t}, {\n+\t\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated \ncharacters\n+\t\t\t\t\t} ) || die $mediawiki-> {error}->{code} . ':' . \n$mediawiki->{error}->{details};\n+\t\t\tmy $last_file_page = $mediawiki->get_page({title =>$path});\n+\t\t\t$newrevid = $last_file_page->{revid};\n  \t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name\\n\";\n  \t\t\t }\n  \telse {\n@@ -864,29 +866,6 @@ sub mw_push_revision {\n  \treturn 1;\n  }\n\n-sub get_reviId_filepage() {\n-\tmw_connect_maybe();\n-\n-\tmy $max_rev_num_file = 0;\n-\n-\tmy @list_file_pages = get_mw_media_pages();\n-\n-\tforeach my $file_page (@list_file_pages) {\n-\tmy $id = $file_page->{pageid};\n-\n-\tmy $query = {\n-\t\taction => 'query',\n-\t\tprop => 'revisions',\n-\t\trvprop => 'ids',\n-\t\tpageids => $id,\n-\t\t};\n-\n-\tmy $result = $mediawiki->api($query);\n-\n-\tmy $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}});\n-\t}\n-}\n-\n"},{"id":"193147","messageId":"1339162024-3120-1-git-send-email-nguyenkimthuat@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCHv1] Export file in git-remote-mediawiki","fromName":"Kim Thuat NGUYEN","fromEmail":"kim-thuat.nguyen@ensimag.imag.fr","sentAt":"2012-06-08T13:27:04Z","receivedAt":"2012-06-08T13:27:04Z","isPatch":false,"sender":{"key":"kim-thuat.nguyen@ensimag.imag.fr","avatar":null},"body":"From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n\nThis patch adds the functionnality to export the file attachements from the local git's repository using the API of mediawiki. It also provides the possibility for\nan user to delete a page in the local repository Git which means the page  will be deleted in the wiki site after user do the 'push'.\n\nSigned-off-by: VOLEK Pavel <Pavel.Volek@ensimag.imag.fr>\nSigned-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\nSigned-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n contrib/mw-to-git/git-remote-mediawiki | 126 +++++++++++++++++++++++++--------\n 1 file changed, 98 insertions(+), 28 deletions(-)\n\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki\nindex c18bfa1..2fd0e5b 100755\n--- a/contrib/mw-to-git/git-remote-mediawiki\n+++ b/contrib/mw-to-git/git-remote-mediawiki\n@@ -275,6 +275,14 @@ sub run_git {\n \treturn $res;\n }\n \n+sub run_git_raw {\n+\tno encoding 'utf8';\n+\topen(my $g,\"-|\",\"git \" . $_[0]); \n+\tmy $r = do { local $/; <$g> };\n+\tclose($g);\n+\n+\treturn $r;\n+}\n \n sub get_last_local_revision {\n \t# Get note regarding last mediawiki revision\n@@ -644,6 +652,11 @@ sub mw_push_file {\n \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n \n+\tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n+\tmy $path = \"File:\".$complete_file_name;\n+\tmy @extensions = split(/\\./, $complete_file_name);\n+\tmy $extension = pop(@extensions);\n+\n \tif (substr($complete_file_name,-3) eq \".mw\") {\n \t\tmy $title = substr($complete_file_name,0,-3);\n \n@@ -653,39 +666,74 @@ sub mw_push_file {\n \t\t\t# special priviledges. A common\n \t\t\t# convention is to replace the page\n \t\t\t# with this content instead:\n-\t\t\t$file_content = DELETED_CONTENT;\n+\t\t\tmw_connect_maybe();\n+\t\t\tmy $re = $mediawiki->edit( {\n+\t\t\t\taction => 'delete',\n+\t\t\t\ttitle => $title,\n+\t\t\t\treason => $summary \n+\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n \t\t} else {\n \t\t\t$file_content = run_git(\"cat-file blob $new_sha1\");\n-\t\t}\n-\n-\t\tmw_connect_maybe();\n \n-\t\tmy $result = $mediawiki->edit( {\n-\t\t\taction => 'edit',\n-\t\t\tsummary => $summary,\n-\t\t\ttitle => $title,\n-\t\t\tbasetimestamp => $basetimestamps{$newrevid},\n-\t\t\ttext => mediawiki_clean($file_content, $page_created),\n-\t\t\t\t  }, {\n-\t\t\t\t\t  skip_encoding => 1 # Helps with names with accentuated characters\n-\t\t\t\t  });\n-\t\tif (!$result) {\n-\t\t\tif ($mediawiki->{error}->{code} == 3) {\n-\t\t\t\t# edit conflicts, considered as non-fast-forward\n-\t\t\t\tprint STDERR 'Warning: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediwiki: ' . $mediawiki->{error}->{details} .\n-\t\t\t\t    \".\\n\";\n-\t\t\t\treturn ($newrevid, \"non-fast-forward\");\n-\t\t\t} else {\n-\t\t\t\t# Other errors. Shouldn't happen => just die()\n-\t\t\t\tdie 'Fatal: Error ' .\n-\t\t\t\t    $mediawiki->{error}->{code} .\n-\t\t\t\t    ' from mediwiki: ' . $mediawiki->{error}->{details};\n+\t\t\tmw_connect_maybe();\n+\t\n+\t\t\tmy $result = $mediawiki->edit( {\n+\t\t\t\taction => 'edit',\n+\t\t\t\tsummary => $summary,\n+\t\t\t\ttitle => $title,\n+\t\t\t\tbasetimestamp => $basetimestamps{$newrevid},\n+\t\t\t\ttext => mediawiki_clean($file_content, $page_created),\n+\t\t\t\t\t  }, {\n+\t\t\t\t\t\t  skip_encoding => 1 # Helps with names with accentuated characters\n+\t\t\t\t\t  });\n+\t\t\tif (!$result) {\n+\t\t\t\tif ($mediawiki->{error}->{code} == 3) {\n+\t\t\t\t\t# edit conflicts, considered as non-fast-forward\n+\t\t\t\t\tprint STDERR 'Warning: Error ' .\n+\t\t\t\t\t    $mediawiki->{error}->{code} .\n+\t\t\t\t   \t ' from mediwiki: ' . $mediawiki->{error}->{details} .\".\\n\";\n+\t\t\t\t\treturn ($newrevid, \"non-fast-forward\");\n+\t\t\t\t} else {\n+\t\t\t\t\t# Other errors. Shouldn't happen => just die()\n+\t\t\t\t\tdie 'Fatal: Error ' .\n+\t\t\t\t\t    $mediawiki->{error}->{code} . ' from mediwiki: ' . $mediawiki->{error}->{details};\n+\t\t\t\t}\n \t\t\t}\n+\t\t\t$newrevid = $result->{edit}->{newrevid};\n+\t\t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n+\t\t}\n+\telsif (exists($hashFiles{$extension}))      \n+\t{\t\t\n+\t\t# Deleting and uploading a file require the priviledge of the user\n+\t\tif ($page_deleted) {       \n+\t\t\tmw_connect_maybe();\n+\t\t\tmy $res = $mediawiki->edit( {\n+\t\t\t\taction => 'delete',\n+\t\t\t\ttitle => $path,\n+\t\t\t\treason => $summary\n+\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\t\n+\t\t} else {\n+\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\t\n+\t\t\tif ($content ne \"\") { \n+\t\t\t\tmw_connect_maybe();\t\n+\t\t\t\t$mediawiki->{config}->{upload_url} = \"$url/index.php/Special:Upload\";\n+\t\t\t\n+\t\t\t\tmy $res = $mediawiki->edit ( {\n+\t\t\t\t\taction => 'upload',\n+\t\t\t\t\tfilename => $complete_file_name,\n+\t\t\t\t\tcomment => $summary,\n+\t\t\t\t\tfile => [undef, $complete_file_name, Content => $content ],\n+\t\t\t\t\tignorewarnings=>1,\n+\t\t\t\t\t}, {\n+\t\t\t\t\t\t  skip_encoding => 1 # Helps with names with accentuated characters\n+\t\t\t\t\t\t} ) || die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n+\t\t\t\tmy $last_file_page = $mediawiki->get_page({title =>$path});\n+\t\t\t\t$newrevid = $last_file_page->{revid};\n+\t\t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name\\n\";\n+\t\t\t} else {\n+\t\t\t\tprint STDERR \"Empty file. Can not upload \\n \";\n+\t\t\t\t}\n \t\t}\n-\t\t$newrevid = $result->{edit}->{newrevid};\n-\t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n \t} else {\n \t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n \t}\n@@ -825,3 +873,25 @@ sub mw_push_revision {\n \tprint STDOUT \"ok $remote\\n\";\n \treturn 1;\n }\n+\n+sub get_file_extensions_maybe {\n+\tmy $file_name = shift;\n+\tmy $est_mw_page = substr($file_name,-3) eq \".mw\";\n+\tif(!$est_mw_page) {\n+\t\tmw_connect_maybe();\n+\n+        \tmy $query = {\n+\t\t\taction => 'query',\n+\t\t\tmeta => 'siteinfo',\n+\t\t\tsiprop => 'fileextensions'\n+\t\t\t};\n+\n+\t\tmy $result = $mediawiki->api($query);\n+\t\tmy @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};\n+\t\tmy %hashFile = map {$_ => 1}@file_extensions;\n+\n+\t\treturn %hashFile;\n+\t} else {\n+\t\treturn ;\n+\t}\n+}\n-- \n1.7.10.2.552.gaa3bb87\n"},{"id":"193150","messageId":"vpqwr3hrj6s.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"1339162024-3120-1-git-send-email-nguyenkimthuat@gmail.com","subject":"Re: [PATCHv1] Export file in git-remote-mediawiki","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-08T14:07:23Z","receivedAt":"2012-06-08T14:07:23Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"> Subject: Re: [PATCHv1] Export file in git-remote-mediawiki\n\nWe usually write commit subject lines as \"subsystem: description\", hence\n\ngit-remote-mediawiki: export \"File:\" attachments\n\nKim Thuat NGUYEN <kim-thuat.nguyen@ensimag.imag.fr> writes:\n\n> From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n>\n> This patch adds the functionnality to export the file attachements from the local git's repository using the API of mediawiki. It also provides the possibility for\n> an user to delete a page in the local repository Git which means the page  will be deleted in the wiki site after user do the 'push'.\n\nPlease, avoid long lines (> 80 characters).\n\n> +\topen(my $g,\"-|\",\"git \" . $_[0]); \n\nSpace after , please.\n\n> +\tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n\nWhat does this function do? My first understanding was that it queried\nthe wiki for allowed file extensions, but why does it need the file\nname? It does nothing if $complete_file_name ends with .mw, but then why\ndo you run it before entering the following if() statement?\n\n>  \tif (substr($complete_file_name,-3) eq \".mw\") {\n>  \t\tmy $title = substr($complete_file_name,0,-3);\n\n> @@ -653,39 +666,74 @@ sub mw_push_file {\n>  \t\t\t# special priviledges. A common\n>  \t\t\t# convention is to replace the page\n>  \t\t\t# with this content instead:\n> -\t\t\t$file_content = DELETED_CONTENT;\n> +\t\t\tmw_connect_maybe();\n> +\t\t\tmy $re = $mediawiki->edit( {\n> +\t\t\t\taction => 'delete',\n> +\t\t\t\ttitle => $title,\n> +\t\t\t\treason => $summary \n> +\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n\nThis is an unrelated topic, and should not appear in this patch.\n\nIf you want to propagate page deletions, then you also need to deal with\nthe case where the user is not allowed to do so (very common on\nMediaWiki). Also, if you change the code corresponding to the comment\nright above, you should update the comment too.\n\n> +\telsif (exists($hashFiles{$extension}))      \n> +\t{\n\nBrace on the same line as else please.\n\n> +\t\t\t} else {\n> +\t\t\t\tprint STDERR \"Empty file. Can not upload \\n \";\n> +\t\t\t\t}\n\nBroken indentation.\n\n>  \t} else {\n>  \t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n>  \t}\n\nIsn't the very point of this patch to remove this error message?\n\n> @@ -825,3 +873,25 @@ sub mw_push_revision {\n>  \tprint STDOUT \"ok $remote\\n\";\n>  \treturn 1;\n>  }\n> +\n> +sub get_file_extensions_maybe {\n> +\tmy $file_name = shift;\n> +\tmy $est_mw_page = substr($file_name,-3) eq \".mw\";\n\nEnglish please. \"est\" is french ;-).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"193199","messageId":"c23d4a113c735e093e3e9b06e4f16a70@ensibm.imag.fr","threadId":"16566","inReplyTo":"vpqwr3hrj6s.fsf@bauges.imag.fr","subject":"Re: [PATCHv1] Export file in git-remote-mediawiki","fromName":"nguyenki","fromEmail":"nguyenki@ensibm.imag.fr","sentAt":"2012-06-08T22:59:22Z","receivedAt":"2012-06-08T22:59:22Z","isPatch":false,"sender":{"key":"nguyenki@ensibm.imag.fr","avatar":null},"body":"On Fri, 08 Jun 2012 16:07:23 +0200, Matthieu Moy wrote:\n>> Subject: Re: [PATCHv1] Export file in git-remote-mediawiki\n>\n> We usually write commit subject lines as \"subsystem: description\", \n> hence\n>\n> git-remote-mediawiki: export \"File:\" attachments\n>\nYes, it will be corrected in the next version.\n\n> Kim Thuat NGUYEN <kim-thuat.nguyen@ensimag.imag.fr> writes:\n>\n>> From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n>>\n>> This patch adds the functionnality to export the file attachements \n>> from the local git's repository using the API of mediawiki. It also \n>> provides the possibility for\n>> an user to delete a page in the local repository Git which means the \n>> page  will be deleted in the wiki site after user do the 'push'.\n>\n> Please, avoid long lines (> 80 characters).\n>\n>> +\topen(my $g,\"-|\",\"git \" . $_[0]);\n>\n> Space after , please.\n>\n>> +\tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n>\n> What does this function do? My first understanding was that it \n> queried\n> the wiki for allowed file extensions, but why does it need the file\n> name? It does nothing if $complete_file_name ends with .mw, but then \n> why\n> do you run it before entering the following if() statement?\n>\n>>  \tif (substr($complete_file_name,-3) eq \".mw\") {\n>>  \t\tmy $title = substr($complete_file_name,0,-3);\n>\n  This function will get a list of allowed file extensions. It need the \nfile name to verify if this name begins with .mw or not. If not, it does \nnothing like\nyou said. But if $complete_file_name is not a wiki page (end with .mw), \nthe function will return the list of file extensions %hashFiles to \nverify if this file is allowed in this condition:\n\n                              } elsif (exists($hashFiles{$extension})) {\n\n\n>> @@ -653,39 +666,74 @@ sub mw_push_file {\n>>  \t\t\t# special priviledges. A common\n>>  \t\t\t# convention is to replace the page\n>>  \t\t\t# with this content instead:\n>> -\t\t\t$file_content = DELETED_CONTENT;\n>> +\t\t\tmw_connect_maybe();\n>> +\t\t\tmy $re = $mediawiki->edit( {\n>> +\t\t\t\taction => 'delete',\n>> +\t\t\t\ttitle => $title,\n>> +\t\t\t\treason => $summary\n>> +\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . \n>> $mediawiki->{error}->{details};\n>\n> This is an unrelated topic, and should not appear in this patch.\n>\n> If you want to propagate page deletions, then you also need to deal \n> with\n> the case where the user is not allowed to do so (very common on\n> MediaWiki). Also, if you change the code corresponding to the comment\n> right above, you should update the comment too.\n>\n>> +\telsif (exists($hashFiles{$extension}))\n>> +\t{\n>\nYes, i'll correct it in the next patch.\nFor the moment, i added these lines to deal with the case similar - the \ncase when an user tries to upload a file or pages but he doesn't have \nsufficient rights or he failed to login.\n\n+sub error_insufficient_right {\n+       print STDERR \"Can not delete or upload file and wiki pages\\n\";\n+       print STDERR \"You don't have right to do it\\n\";\n+       print STDOUT \"error $_[0]\\\"right insufficient\\\"\\n\";\n+       return 0;\n+}\n@@ -726,17 +1011,32 @@ sub mw_push_file {\n                                         ignorewarnings=>1,\n                                         }, {\n                                                   skip_encoding => 1 # \nHelps with names with accentuated characters\n-                                               } ) || die $mediawiki-> \n{error}->{code} . ':' . $mediawiki->{error}->{details};\n+                                               } );\n+                               if (!$res) {\n+                                       if ($mediawiki->{error}->{code} \n== 3) {\n+                                               # Failed to upload, \nuser didn't login or he doesn't have sufficient rights\n+                                               print STDERR 'Warning: \nError ' .\n+                                                   \n$mediawiki->{error}->{code} .\n+                                                   ' from mediwiki: ' \n. $mediawiki->{error}->{details} .\n+                                                   \".\\n\";\n+                                               return \n($newrevid,\"insufficient-right\");\n+                                       } else {\n+                                               # Other errors. \nShouldn't happen => just die()\n+                                               die 'Fatal: Error ' .\n+                                                   \n$mediawiki->{error}->{code} .\n+                                                   ' from mediwiki: ' \n. $mediawiki->{error}->{details};\n+                                       }\n+                               }\n\n\n@@ -860,6 +1161,9 @@ sub mw_push_revision {\n                                 # accurate error message.\n                                 return error_non_fast_forward($remote);\n                         }\n+                       if($status eq \"insufficient-right\") {\n+                               return \nerror_insufficient_right($remote);\n+                       }\n                         if ($status ne \"ok\") {\n                                 die(\"Unknown error from \nmw_push_file()\");\n                         }\n\n\n\n>\n>>  \t} else {\n>>  \t\tprint STDERR \"$complete_file_name not a mediawiki file (Not \n>> pushable on this version of git-remote-mediawiki).\\n\"\n>>  \t}\n>\n> Isn't the very point of this patch to remove this error message?\nNow, the message is\n+               print STDERR \"$complete_file_name is not a permitted \nfile. Check the configuration of file uploads in your mediawiki \\n\"\n+       }\n\n\n>\n>> @@ -825,3 +873,25 @@ sub mw_push_revision {\n>>  \tprint STDOUT \"ok $remote\\n\";\n>>  \treturn 1;\n>>  }\n>> +\n>> +sub get_file_extensions_maybe {\n>> +\tmy $file_name = shift;\n>> +\tmy $est_mw_page = substr($file_name,-3) eq \".mw\";\n>\n> English please. \"est\" is french ;-).\nCorrected.\n-       my $est_mw_page = substr($file_name,-3) eq \".mw\";\n-       if(!$est_mw_page) {\n+       my $is_mw_page = substr($file_name,-3) eq \".mw\";\n+       if(!$is_mw_page) {\n\nThanks you so much for your advices.\n\nThuat.\n"},{"id":"193257","messageId":"vpqehpnnwwo.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"c23d4a113c735e093e3e9b06e4f16a70@ensibm.imag.fr","subject":"Re: [PATCHv1] Export file in git-remote-mediawiki","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-10T13:01:27Z","receivedAt":"2012-06-10T13:01:27Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"nguyenki <nguyenki@ensibm.imag.fr> writes:\n\n>>> +\tmy %hashFiles = get_file_extensions_maybe($complete_file_name);\n>>\n>> What does this function do? My first understanding was that it\n>> queried\n>> the wiki for allowed file extensions, but why does it need the file\n>> name? It does nothing if $complete_file_name ends with .mw, but then\n>> why\n>> do you run it before entering the following if() statement?\n>>\n>>>  \tif (substr($complete_file_name,-3) eq \".mw\") {\n>>>  \t\tmy $title = substr($complete_file_name,0,-3);\n>>\n>  This function will get a list of allowed file extensions. It need the\n> file name to verify if this name begins with .mw or not. If not, it\n> does nothing like\n> you said. But if $complete_file_name is not a wiki page (end with\n> .mw), the function will return the list of file extensions %hashFiles\n> to verify if this file is allowed in this condition:\n\nIf $complete_file_name is not a wiki page, you basically don't need to\ncall this function. As I said, move the call to the function within the\nnext \"if\" statement, and you won't need this extra complexity.\n\n> Yes, i'll correct it in the next patch.\n> For the moment, i added these lines to deal with the case similar -\n> the case when an user tries to upload a file or pages but he doesn't\n> have sufficient rights or he failed to login.\n>\n> +sub error_insufficient_right {\n> +       print STDERR \"Can not delete or upload file and wiki pages\\n\";\n> +       print STDERR \"You don't have right to do it\\n\";\n> +       print STDOUT \"error $_[0]\\\"right insufficient\\\"\\n\";\n> +       return 0;\n> +}\n\nThis is still a regression. The previous version was propagating page\ndeletion as \"replace content with [[Category:Deleted]]\", which did work\nwith insufficient priviledge.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"193432","messageId":"1339505176-2962-1-git-send-email-nguyenkimthuat@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCHv2] git-remote-mediawiki: export File: attachments","fromName":"Kim Thuat NGUYEN","fromEmail":"kim-thuat.nguyen@ensimag.imag.fr","sentAt":"2012-06-12T12:46:16Z","receivedAt":"2012-06-12T12:46:16Z","isPatch":false,"sender":{"key":"kim-thuat.nguyen@ensimag.imag.fr","avatar":null},"body":"From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n\nThis patch adds the functionnality to export the \nfile attachements from the local git's repository \nusing the API of mediawiki.\n\nSigned-off-by: VOLEK Pavel <Pavel.Volek@ensimag.imag.fr>\nSigned-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\nSigned-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n contrib/mw-to-git/git-remote-mediawiki | 62 +++++++++++++++++++++++++++++++++-\n 1 file changed, 61 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki\nindex c18bfa1..8f711e2 100755\n--- a/contrib/mw-to-git/git-remote-mediawiki\n+++ b/contrib/mw-to-git/git-remote-mediawiki\n@@ -275,6 +275,14 @@ sub run_git {\n \treturn $res;\n }\n \n+sub run_git_raw {\n+\tno encoding 'utf8';\n+\topen(my $g, \"-|\", \"git \" . $_[0]);\n+\tmy $r = do { local $/; <$g> };\n+\tclose($g);\n+\n+\treturn $r;\n+}\n \n sub get_last_local_revision {\n \t# Get note regarding last mediawiki revision\n@@ -644,6 +652,10 @@ sub mw_push_file {\n \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n \n+\tmy $path = \"File:\".$complete_file_name;\n+\tmy @extensions = split(/\\./, $complete_file_name);\n+\tmy $extension = pop(@extensions);\n+\n \tif (substr($complete_file_name,-3) eq \".mw\") {\n \t\tmy $title = substr($complete_file_name,0,-3);\n \n@@ -687,7 +699,40 @@ sub mw_push_file {\n \t\t$newrevid = $result->{edit}->{newrevid};\n \t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n \t} else {\n-\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n+\t\tmy %hashFiles = get_file_extensions();\n+\t\tif (exists($hashFiles{$extension})) {\n+\t\t\t# Deleting and uploading a file require the priviledge of the user\n+\t\t\tif ($page_deleted) {\n+\t\t\t\tmw_connect_maybe();\n+\t\t\t\tmy $res = $mediawiki->edit( {\n+\t\t\t\t\taction => 'delete',\n+\t\t\t\t\ttitle => $path,\n+\t\t\t\t\treason => $summary\n+\t\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n+\t\t\t} else {\n+\t\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n+\t\t\t\tif ($content ne \"\") {\n+\t\t\t\t\tmw_connect_maybe();\n+\t\t\t\t\t$mediawiki->{config}->{upload_url} = \"$url/index.php/Special:Upload\";\n+\t\t\t\t\tmy $res = $mediawiki->edit ( {\n+\t\t\t\t\t\taction => 'upload',\n+\t\t\t\t\t\tfilename => $complete_file_name,\n+\t\t\t\t\t\tcomment => $summary,\n+\t\t\t\t\t\tfile => [undef, $complete_file_name, Content => $content ],\n+\t\t\t\t\t\tignorewarnings=>1,\n+\t\t\t\t\t\t}, {\n+\t\t\t\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated characters\n+\t\t\t\t\t\t\t} )  || die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n+\t\t\t\t\tmy $last_file_page = $mediawiki->get_page({title =>$path});\n+\t\t\t\t\t$newrevid = $last_file_page->{revid};\n+\t\t\t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name\\n\";\n+\t\t\t\t} else {\n+\t\t\t\t\tprint STDERR \"Empty file. Can not upload \\n \";\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\";\n+\t\t}\n \t}\n \treturn ($newrevid, \"ok\");\n }\n@@ -825,3 +870,18 @@ sub mw_push_revision {\n \tprint STDOUT \"ok $remote\\n\";\n \treturn 1;\n }\n+\n+sub get_file_extensions {\n+\tmw_connect_maybe();\n+\n+\tmy $query = {\n+\t\taction => 'query',\n+\t\tmeta => 'siteinfo',\n+\t\tsiprop => 'fileextensions'\n+\t\t};\n+\tmy $result = $mediawiki->api($query);\n+\tmy @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};\n+\tmy %hashFile = map {$_ => 1}@file_extensions;\n+\n+\treturn %hashFile;\n+}\n-- \n1.7.10.GIT\n"},{"id":"193433","messageId":"vpqvciwu1yl.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"1339505176-2962-1-git-send-email-nguyenkimthuat@gmail.com","subject":"Re: [PATCHv2] git-remote-mediawiki: export File: attachments","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-12T12:52:34Z","receivedAt":"2012-06-12T12:52:34Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Kim Thuat NGUYEN <kim-thuat.nguyen@ensimag.imag.fr> writes:\n\n> From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n\nPlease use your @ensimag email for Ensimag-related things. This adress\nis the one used in the commit itself, i.e. ~/.gitconfig. \"git commit\n--reset-author\" can help.\n\n> @@ -644,6 +652,10 @@ sub mw_push_file {\n>  \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n>  \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n>  \n> +\tmy $path = \"File:\".$complete_file_name;\n> +\tmy @extensions = split(/\\./, $complete_file_name);\n> +\tmy $extension = pop(@extensions);\n> +\n>  \tif (substr($complete_file_name,-3) eq \".mw\") {\n>  \t\tmy $title = substr($complete_file_name,0,-3);\n\nIf you extract the extension explicitely, then you don't need these\n\"substr(...)\" anymore.\n\n> +\t\tmy %hashFiles = get_file_extensions();\n> +\t\tif (exists($hashFiles{$extension})) {\n> +\t\t\t# Deleting and uploading a file require the priviledge of the user\n> +\t\t\tif ($page_deleted) {\n> +\t\t\t\tmw_connect_maybe();\n> +\t\t\t\tmy $res = $mediawiki->edit( {\n> +\t\t\t\t\taction => 'delete',\n> +\t\t\t\t\ttitle => $path,\n> +\t\t\t\t\treason => $summary\n> +\t\t\t\t\t} )|| die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n\nHere and below: you still have too long lines.\n\n> +\t\t\t\t\t\tignorewarnings=>1,\n\nSpaces around =>.\n\n> +\t\t\t\t\t\t}, {\n> +\t\t\t\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated characters\n> +\t\t\t\t\t\t\t} )  || die $mediawiki-> {error}->{code} . ':' . $mediawiki->{error}->{details};\n\nWeird indentation.\n\n> +\t\t\t\t} else {\n> +\t\t\t\t\tprint STDERR \"Empty file. Can not upload \\n \";\n> +\t\t\t\t}\n\nNo space, but a \".\" before \\n.\n\n> +\t\t} else {\n> +\t\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\";\n> +\t\t}\n\nWhy does this message keep reappearing?\n\n> +sub get_file_extensions {\n> +\tmw_connect_maybe();\n> +\n> +\tmy $query = {\n> +\t\taction => 'query',\n> +\t\tmeta => 'siteinfo',\n> +\t\tsiprop => 'fileextensions'\n> +\t\t};\n> +\tmy $result = $mediawiki->api($query);\n> +\tmy @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};\n> +\tmy %hashFile = map {$_ => 1}@file_extensions;\n> +\n> +\treturn %hashFile;\n> +}\n\nI like the new function much more than the previous one.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"193434","messageId":"7770d61cdb91fff129c35ba2fd97f649@ensibm.imag.fr","threadId":"16566","inReplyTo":"1339505176-2962-1-git-send-email-nguyenkimthuat@gmail.com","subject":"Re: [PATCHv2] git-remote-mediawiki: export File: attachments","fromName":"nguyenki","fromEmail":"nguyenki@ensibm.imag.fr","sentAt":"2012-06-12T12:54:21Z","receivedAt":"2012-06-12T12:54:21Z","isPatch":false,"sender":{"key":"nguyenki@ensibm.imag.fr","avatar":null},"body":"On Tue, 12 Jun 2012 14:46:16 +0200, Kim Thuat NGUYEN wrote:\n\n> +\t\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not\n> pushable on this version of git-remote-mediawiki).\\n\";\n> +\t\t}\n>  \t}\n>  \treturn ($newrevid, \"ok\");\n>  }\n\nI changed the comment like that, i'll be correct in the nex patch.\n\n-\t\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not \npushable on this version of git-remote-mediawiki).\\n\";\n+\t\t\tprint STDERR \"$complete_file_name is not a permitted file. Check \nthe configuration of file uploads in your mediawiki \\n\";\n"},{"id":"193438","messageId":"8F321FFA-68B1-46A9-B67D-D68AE33C6147@quendi.de","threadId":"16566","inReplyTo":"1339505176-2962-1-git-send-email-nguyenkimthuat@gmail.com","subject":"Re: [PATCHv2] git-remote-mediawiki: export File: attachments","fromName":"Max Horn","fromEmail":"max@quendi.de","sentAt":"2012-06-12T13:51:06Z","receivedAt":"2012-06-12T13:51:06Z","isPatch":false,"sender":{"key":"max@quendi.de","avatar":"https://avatars.githubusercontent.com/u/241512?v=4"},"body":"\nAm 12.06.2012 um 14:46 schrieb Kim Thuat NGUYEN:\n\n> From: nguyenkimthuat <nguyenkimthuat@gmail.com>\n> \n> This patch adds the functionnality to export the \n\nMinor nitpick:\nfunctionnality => functionality\n\n\nCheers,\nMax\n"},{"id":"193526","messageId":"1339574190-2596-1-git-send-email-Kim-Thuat.Nguyen@ensimag.imag.fr","threadId":"16566","inReplyTo":"y","subject":"[PATCHv3] git-remote-mediawiki: export File: attachments","fromName":"","fromEmail":"kim-thuat.nguyen@ensimag.imag.fr","sentAt":"2012-06-13T07:56:30Z","receivedAt":"2012-06-13T07:56:30Z","isPatch":false,"sender":{"key":"kim-thuat.nguyen@ensimag.imag.fr","avatar":null},"body":"From: nguyenkimthuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\n\nThis patch adds the functionality to export the \nfile attachements from the local git's repository \nusing the API of mediawiki.\n\nSigned-off-by: VOLEK Pavel <Pavel.Volek@ensimag.imag.fr>\nSigned-off-by: NGUYEN Kim Thuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\nSigned-off-by: ROUCHER IGLESIAS Javier <roucherj@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n contrib/mw-to-git/git-remote-mediawiki | 67 +++++++++++++++++++++++++++++++++-\n 1 file changed, 65 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki\nindex c18bfa1..e6f8940 100755\n--- a/contrib/mw-to-git/git-remote-mediawiki\n+++ b/contrib/mw-to-git/git-remote-mediawiki\n@@ -275,6 +275,14 @@ sub run_git {\n \treturn $res;\n }\n \n+sub run_git_raw {\n+\tno encoding 'utf8';\n+\topen(my $g, \"-|\", \"git \" . $_[0]);\n+\tmy $r = do { local $/; <$g> };\n+\tclose($g);\n+\n+\treturn $r;\n+}\n \n sub get_last_local_revision {\n \t# Get note regarding last mediawiki revision\n@@ -644,7 +652,11 @@ sub mw_push_file {\n \tmy $page_deleted = ($new_sha1 eq NULL_SHA1);\n \t$complete_file_name = mediawiki_clean_filename($complete_file_name);\n \n-\tif (substr($complete_file_name,-3) eq \".mw\") {\n+\tmy $path = \"File:\".$complete_file_name;\n+\tmy @extensions = split(/\\./, $complete_file_name);\n+\tmy $extension = pop(@extensions);\n+\n+\tif ($extension eq \"mw\") {\n \t\tmy $title = substr($complete_file_name,0,-3);\n \n \t\tmy $file_content;\n@@ -687,7 +699,43 @@ sub mw_push_file {\n \t\t$newrevid = $result->{edit}->{newrevid};\n \t\tprint STDERR \"Pushed file: $new_sha1 - $title\\n\";\n \t} else {\n-\t\tprint STDERR \"$complete_file_name not a mediawiki file (Not pushable on this version of git-remote-mediawiki).\\n\"\n+\t\tmy %hashFiles = get_file_extensions();\n+\t\tif (exists($hashFiles{$extension})) {\n+\t\t\t# Deleting and uploading a file require the priviledge of the user\n+\t\t\tif ($page_deleted) {\n+\t\t\t\tmw_connect_maybe();\n+\t\t\t\tmy $res = $mediawiki->edit( {\n+\t\t\t\t\taction => 'delete',\n+\t\t\t\t\ttitle => $path,\n+\t\t\t\t\treason => $summary\n+\t\t\t\t\t} )|| die $mediawiki-> {error}->{code} .\n+\t\t\t\t\t\t':' . $mediawiki->{error}->{details};\n+\t\t\t} else {\n+\t\t\t\tmy $content = run_git_raw(\"cat-file blob $new_sha1\");\n+\t\t\t\tif ($content ne \"\") {\n+\t\t\t\t\tmw_connect_maybe();\n+\t\t\t\t\t$mediawiki->{config}->{upload_url} = \"$url/index.php/Special:Upload\";\n+\n+\t\t\t\t\tmy $res = $mediawiki->edit ( {\n+\t\t\t\t\t\taction => 'upload',\n+\t\t\t\t\t\tfilename => $complete_file_name,\n+\t\t\t\t\t\tcomment => $summary,\n+\t\t\t\t\t\tfile => [undef, $complete_file_name, Content => $content ],\n+\t\t\t\t\t\tignorewarnings => 1,\n+\t\t\t\t\t\t}, {\n+\t\t\t\t\t\t\tskip_encoding => 1 # Helps with names with accentuated characters\n+\t\t\t\t\t\t} )  || die $mediawiki-> {error}->{code} .\n+\t\t\t\t\t\t\t':' . $mediawiki->{error}->{details};\n+\t\t\t\t\tmy $last_file_page = $mediawiki->get_page({title =>$path});\n+\t\t\t\t\t$newrevid = $last_file_page->{revid};\n+\t\t\t\t\tprint STDERR \"Pushed file: $new_sha1 - $complete_file_name.\\n\";\n+\t\t\t\t} else {\n+\t\t\t\t\tprint STDERR \"Empty file. Can not upload.\\n \";\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\tprint STDERR \"$complete_file_name is not a permitted file. Check the configuration of file uploads in your mediawiki.\\n\";\n+\t\t}\n \t}\n \treturn ($newrevid, \"ok\");\n }\n@@ -825,3 +873,18 @@ sub mw_push_revision {\n \tprint STDOUT \"ok $remote\\n\";\n \treturn 1;\n }\n+\n+sub get_file_extensions {\n+\tmw_connect_maybe();\n+\n+\tmy $query = {\n+\t\taction => 'query',\n+\t\tmeta => 'siteinfo',\n+\t\tsiprop => 'fileextensions'\n+\t\t};\n+\tmy $result = $mediawiki->api($query);\n+\tmy @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};\n+\tmy %hashFile = map {$_ => 1}@file_extensions;\n+\n+\treturn %hashFile;\n+}\n-- \n1.7.11.rc2.10.g9fb1faf\n"},{"id":"193546","messageId":"vpq62avgodr.fsf@bauges.imag.fr","threadId":"16566","inReplyTo":"1339574190-2596-1-git-send-email-Kim-Thuat.Nguyen@ensimag.imag.fr","subject":"Re: [PATCHv3] git-remote-mediawiki: export File: attachments","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2012-06-13T16:36:32Z","receivedAt":"2012-06-13T16:36:32Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"kim-thuat.nguyen@ensimag.imag.fr writes:\n\n> From: nguyenkimthuat <Kim-Thuat.Nguyen@ensimag.imag.fr>\n\nPlease, use your real name, not your login name.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"206539","messageId":"50F0402A.1000108@redhat.com","threadId":"16566","inReplyTo":"50EFD066.60501@redhat.com","subject":"git send-email should not allow 'y' for in-reply-to","fromName":"Eric Blake","fromEmail":"eblake@redhat.com","sentAt":"2013-01-11T16:39:06Z","receivedAt":"2013-01-11T16:39:06Z","isPatch":false,"sender":{"key":"eblake@redhat.com","avatar":"https://avatars.githubusercontent.com/u/32933908?v=4"},"body":"[raising this UI wart to the git list]\n\nOn 01/11/2013 01:42 AM, Peter Krempa wrote:\n> On 01/11/13 07:31, Chunyan Liu wrote:\n>> This patch series is to...\n[snip]\n\n> \n> Please don't answer \"y\" when git send email shows the following prompt:\n> \n> \"Message-ID to be used as In-Reply-To for the first email?\"\n> \n> you should respond with a message ID there. Unfortunately we have a\n> growing thread that contains submissions with this mistake.\n\nAnyone willing to patch upstream 'git send-email' to reject a simple 'y'\nrather than blindly sending a bad messageID for the in-reply-to field,\nto help future users avoid this mistake?  Obviously, it won't help until\nthe patch eventually percolates into distros, so it would be a few more\nmonths before we see the benefits, but down the road it will prevent\nconfusing threads.\n\n-- \nEric Blake   eblake redhat com    +1-919-301-3266\nLibvirt virtualization library http://libvirt.org\n\n"},{"id":"206542","messageId":"20130111164730.GA7921@sigill.intra.peff.net","threadId":"16566","inReplyTo":"50F0402A.1000108@redhat.com","subject":"Re: git send-email should not allow 'y' for in-reply-to","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-01-11T16:47:30Z","receivedAt":"2013-01-11T16:47:30Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 11, 2013 at 09:39:06AM -0700, Eric Blake wrote:\n\n> > Please don't answer \"y\" when git send email shows the following prompt:\n> > \n> > \"Message-ID to be used as In-Reply-To for the first email?\"\n> > \n> > you should respond with a message ID there. Unfortunately we have a\n> > growing thread that contains submissions with this mistake.\n> \n> Anyone willing to patch upstream 'git send-email' to reject a simple 'y'\n> rather than blindly sending a bad messageID for the in-reply-to field,\n> to help future users avoid this mistake?  Obviously, it won't help until\n> the patch eventually percolates into distros, so it would be a few more\n> months before we see the benefits, but down the road it will prevent\n> confusing threads.\n\nWhat version of git? Commit 51bbccf is in v1.7.12.1 and higher, and\nsays:\n\n  $ git show 51bbccf\n  commit 51bbccfd1b4a9e2807413022c56ab05c835164fb\n  Author: Junio C Hamano <gitster@pobox.com>\n  Date:   Tue Aug 14 15:15:53 2012 -0700\n\n  send-email: validate & reconfirm interactive responses\n\n  People answer 'y' to \"Who should the emails appear to be from?\"  and\n  'n' to \"Message-ID to be used as In-Reply-To for the first email?\"\n  for some unknown reason.  While it is possible that your local\n  username really is \"y\" and you are sending the mail to your local\n  colleagues, it is possible, and some might even say it is likely,\n  that it is a user error.\n\n  Fortunately, our interactive prompter already has input validation\n  mechanism built-in.  Enhance it so that we can optionally reconfirm\n  and allow the user to pass an input that does not validate, and\n  \"softly\" require input to the sender, in-reply-to, and recipient to\n  contain \"@\" and \".\" in this order, which would catch most cases of\n  mistakes.\n\n-Peff\n"},{"id":"206544","messageId":"50F0512E.8000205@redhat.com","threadId":"16566","inReplyTo":"20130111164730.GA7921@sigill.intra.peff.net","subject":"Re: git send-email should not allow 'y' for in-reply-to","fromName":"Eric Blake","fromEmail":"eblake@redhat.com","sentAt":"2013-01-11T17:51:42Z","receivedAt":"2013-01-11T17:51:42Z","isPatch":false,"sender":{"key":"eblake@redhat.com","avatar":"https://avatars.githubusercontent.com/u/32933908?v=4"},"body":"On 01/11/2013 09:47 AM, Jeff King wrote:\n> On Fri, Jan 11, 2013 at 09:39:06AM -0700, Eric Blake wrote:\n> \n>>> Please don't answer \"y\" when git send email shows the following prompt:\n>>>\n\n>>\n>> Anyone willing to patch upstream 'git send-email' to reject a simple 'y'\n\n> What version of git? Commit 51bbccf is in v1.7.12.1 and higher, and\n> says:\n> \n>   $ git show 51bbccf\n>   commit 51bbccfd1b4a9e2807413022c56ab05c835164fb\n>   Author: Junio C Hamano <gitster@pobox.com>\n>   Date:   Tue Aug 14 15:15:53 2012 -0700\n> \n>   send-email: validate & reconfirm interactive responses\n> \n>   People answer 'y' to \"Who should the emails appear to be from?\"  and\n>   'n' to \"Message-ID to be used as In-Reply-To for the first email?\"\n>   for some unknown reason.  While it is possible that your local\n>   username really is \"y\" and you are sending the mail to your local\n>   colleagues, it is possible, and some might even say it is likely,\n>   that it is a user error.\n\nAwesome!  Already implemented!  In the case that sparked this particular\nemail, the culprit was using 1.7.3.4; earlier this month, a separate\nculprit to the same libvirt mailing list was using 1.7.11.7.\n\nI was right about it needing to take a few months to percolate to the\nactual users.\n\n-- \nEric Blake   eblake redhat com    +1-919-301-3266\nLibvirt virtualization library http://libvirt.org\n\n"},{"id":"206547","messageId":"CAE1pOi0zc+d6d9Y4KViX24qHgc1WL9atmRuyygorX_DQMj69Hg@mail.gmail.com","threadId":"16566","inReplyTo":"20130111164730.GA7921@sigill.intra.peff.net","subject":"Re: git send-email should not allow 'y' for in-reply-to","fromName":"Hilco Wijbenga","fromEmail":"hilco.wijbenga@gmail.com","sentAt":"2013-01-11T18:43:39Z","receivedAt":"2013-01-11T18:43:39Z","isPatch":false,"sender":{"key":"hilco.wijbenga@gmail.com","avatar":null},"body":"On 11 January 2013 08:47, Jeff King <peff@peff.net> wrote:\n> On Fri, Jan 11, 2013 at 09:39:06AM -0700, Eric Blake wrote:\n>\n>> > Please don't answer \"y\" when git send email shows the following prompt:\n>> >\n>> > \"Message-ID to be used as In-Reply-To for the first email?\"\n>> >\n>> > you should respond with a message ID there. Unfortunately we have a\n>> > growing thread that contains submissions with this mistake.\n\n<snip/>\n\n>   People answer 'y' to \"Who should the emails appear to be from?\"  and\n>   'n' to \"Message-ID to be used as In-Reply-To for the first email?\"\n>   for some unknown reason.  While it is possible that your local\n>   username really is \"y\" and you are sending the mail to your local\n>   colleagues, it is possible, and some might even say it is likely,\n>   that it is a user error.\n\nI have never used Git's email support so this doesn't affect me one\nway or another but it seems that checking the results is fixing the\nsymptoms, not the problem? I apologize if this was already discussed\nbut I couldn't find such a discussion.\n\nI was wondering if it might be a better idea to change the wording of\nthe questions if they have proven so confusing? The first time (just\nnow) that I read \"Message-ID to be used as In-Reply-To for the first\nemail?\", it clearly seemed like a yes/no question to me. :-)\n\nHow about \"What Message-ID to use as In-Reply-To for the first email?\"\nor \"Provide the Message-ID to use as In-Reply-To for the first\nemail:\". I'm a little surprised that \"Who should the emails appear to\nbe from?\" would be interpreted as a yes/no question but we could\nrephrase that similarly as \"Provide the name of the email sender:\" (I\ndon't really like this particular version but you get the idea).\n"},{"id":"206549","messageId":"20130111185417.GA12852@sigill.intra.peff.net","threadId":"16566","inReplyTo":"CAE1pOi0zc+d6d9Y4KViX24qHgc1WL9atmRuyygorX_DQMj69Hg@mail.gmail.com","subject":"Re: git send-email should not allow 'y' for in-reply-to","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-01-11T18:54:17Z","receivedAt":"2013-01-11T18:54:17Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 11, 2013 at 10:43:39AM -0800, Hilco Wijbenga wrote:\n\n> >   People answer 'y' to \"Who should the emails appear to be from?\"  and\n> >   'n' to \"Message-ID to be used as In-Reply-To for the first email?\"\n> >   for some unknown reason.  While it is possible that your local\n> >   username really is \"y\" and you are sending the mail to your local\n> >   colleagues, it is possible, and some might even say it is likely,\n> >   that it is a user error.\n> \n> I have never used Git's email support so this doesn't affect me one\n> way or another but it seems that checking the results is fixing the\n> symptoms, not the problem? I apologize if this was already discussed\n> but I couldn't find such a discussion.\n\nIt depends on who you are. If you are the person running send-email,\nthen the symptom is your confusion. If you are somebody else, the\nsymptom is somebody else sending out a bogus email. That patch fixes\nonly the latter. :)\n\nMore seriously, I agree that re-wording the question is a reasonable\nthing to do. I do not use send-email, either, so I don't have a strong\nopinion on it. The suggestions you made:\n\n> How about \"What Message-ID to use as In-Reply-To for the first email?\"\n> or \"Provide the Message-ID to use as In-Reply-To for the first\n> email:\".\n\nseem fine to me. Maybe somebody who has been confused by it can offer\nmore. At any rate, patches welcome.\n\n-Peff\n"},{"id":"210175","messageId":"7vfw0m5bin.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"20130111185417.GA12852@sigill.intra.peff.net","subject":"Re: git send-email should not allow 'y' for in-reply-to","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-02-24T09:03:44Z","receivedAt":"2013-02-24T09:03:44Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Jan 11, 2013 at 10:43:39AM -0800, Hilco Wijbenga wrote:\n> ...\n> More seriously, I agree that re-wording the question is a reasonable\n> thing to do. I do not use send-email, either, so I don't have a strong\n> opinion on it. The suggestions you made:\n>\n>> How about \"What Message-ID to use as In-Reply-To for the first email?\"\n>> or \"Provide the Message-ID to use as In-Reply-To for the first\n>> email:\".\n>\n> seem fine to me. Maybe somebody who has been confused by it can offer\n> more. At any rate, patches welcome.\n\nHas anything come out of this discussion?  Is the current phrasing\nfine as-is?\n"},{"id":"220299","messageId":"36606.9748219617$1370877207@news.gmane.org","threadId":"16566","inReplyTo":"y","subject":"v3 [PATCH 1/2] status: introduce status.short to enable --short by default","fromName":"","fromEmail":"y@ensimag.imag.fr","sentAt":"2013-06-10T15:13:03Z","receivedAt":"2013-06-10T15:13:03Z","isPatch":true,"sender":{"key":"y@ensimag.imag.fr","avatar":null},"body":"From: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n\nSome people always run 'git status -s'.\nThe configuration variable status.short allows to set it by default.\n\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Changes to be commented:\n- Cleaning test\n\n Documentation/config.txt |    4 ++++\n builtin/commit.c         |    5 +++++\n t/t7508-status.sh        |   35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 44 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6e53fc5..1983bf7 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2066,6 +2066,10 @@ status.relativePaths::\n \trelative to the repository root (this was the default for Git\n \tprior to v1.5.4).\n \n+status.short::\n+\tSet to true to enable --short by default in linkgit:git-status[1].\n+\tThe option --no-short takes precedence over this variable.\n+\n status.showUntrackedFiles::\n \tBy default, linkgit:git-status[1] and linkgit:git-commit[1] show\n \tfiles which are not currently tracked by Git. Directories which\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 1621dfc..287f1cb 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1112,6 +1112,11 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\t\ts->submodule_summary = -1;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.short\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\tstatus_format = STATUS_FORMAT_SHORT;\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n \t\ts->use_color = git_config_colorbool(k, v);\n \t\treturn 0;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex e2ffdac..9a07f15 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -1335,4 +1335,39 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' '\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n+test_expect_success '\"Setup of environment of test\"' '\n+\tgit config status.showUntrackedFiles no\n+'\n+\n+test_expect_success '\"status.short=true\" same as \"-s\"' '\n+\tgit -c status.short=true status >actual &&\n+\tgit status -s >expected_short &&\n+\ttest_cmp actual expected_short\n+'\n+\n+test_expect_success '\"status.short=true\" different from \"--no-short\"' '\n+\tgit status --no-short >expected_noshort &&\n+\ttest_must_fail test_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=true\" weaker than \"--no-short\"' '\n+\tgit -c status.short=true status --no-short >actual &&\n+\ttest_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=false\" same as \"--no-short\"' '\n+\tgit -c status.short=false status >actual &&\n+\tgit status -s >expected_short &&\n+\ttest_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=false\" weaker than \"-s\"' '\n+\tgit -c status.short=false status -s >actual &&\n+\ttest_cmp actual expected_short\n+'\n+\n+test_expect_success '\"Back to environment of test by default\"' '\n+\tgit config status.showUntrackedFiles yes\n+'\n+\n test_done\n-- \n1.7.8\n"},{"id":"299216","messageId":"1370877184-19409-1-git-send-email-y","threadId":"16566","inReplyTo":"y","subject":"v3 [PATCH 1/2] status: introduce status.short to enable --short by default","fromName":"","fromEmail":"y@ensimag.imag.fr","sentAt":"2013-06-10T15:13:03Z","receivedAt":"2013-06-10T15:13:03Z","isPatch":true,"sender":{"key":"y@ensimag.imag.fr","avatar":null},"body":"From: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n\nSome people always run 'git status -s'.\nThe configuration variable status.short allows to set it by default.\n\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Changes to be commented:\n- Cleaning test\n\n Documentation/config.txt |    4 ++++\n builtin/commit.c         |    5 +++++\n t/t7508-status.sh        |   35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 44 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6e53fc5..1983bf7 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2066,6 +2066,10 @@ status.relativePaths::\n \trelative to the repository root (this was the default for Git\n \tprior to v1.5.4).\n \n+status.short::\n+\tSet to true to enable --short by default in linkgit:git-status[1].\n+\tThe option --no-short takes precedence over this variable.\n+\n status.showUntrackedFiles::\n \tBy default, linkgit:git-status[1] and linkgit:git-commit[1] show\n \tfiles which are not currently tracked by Git. Directories which\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 1621dfc..287f1cb 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1112,6 +1112,11 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\t\ts->submodule_summary = -1;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.short\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\tstatus_format = STATUS_FORMAT_SHORT;\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n \t\ts->use_color = git_config_colorbool(k, v);\n \t\treturn 0;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex e2ffdac..9a07f15 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -1335,4 +1335,39 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' '\n \tgit config -f .gitmodules  --remove-section submodule.subname\n '\n \n+test_expect_success '\"Setup of environment of test\"' '\n+\tgit config status.showUntrackedFiles no\n+'\n+\n+test_expect_success '\"status.short=true\" same as \"-s\"' '\n+\tgit -c status.short=true status >actual &&\n+\tgit status -s >expected_short &&\n+\ttest_cmp actual expected_short\n+'\n+\n+test_expect_success '\"status.short=true\" different from \"--no-short\"' '\n+\tgit status --no-short >expected_noshort &&\n+\ttest_must_fail test_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=true\" weaker than \"--no-short\"' '\n+\tgit -c status.short=true status --no-short >actual &&\n+\ttest_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=false\" same as \"--no-short\"' '\n+\tgit -c status.short=false status >actual &&\n+\tgit status -s >expected_short &&\n+\ttest_cmp actual expected_noshort\n+'\n+\n+test_expect_success '\"status.short=false\" weaker than \"-s\"' '\n+\tgit -c status.short=false status -s >actual &&\n+\ttest_cmp actual expected_short\n+'\n+\n+test_expect_success '\"Back to environment of test by default\"' '\n+\tgit config status.showUntrackedFiles yes\n+'\n+\n test_done\n-- \n1.7.8\n\n"},{"id":"220300","messageId":"11199.4715095081$1370877380@news.gmane.org","threadId":"16566","inReplyTo":"1370877184-19409-1-git-send-email-y","subject":"v3 [PATCH 2/2] status:introduce status.branch to enable --branch by default","fromName":"","fromEmail":"y@ensimag.imag.fr","sentAt":"2013-06-10T15:13:04Z","receivedAt":"2013-06-10T15:13:04Z","isPatch":true,"sender":{"key":"y@ensimag.imag.fr","avatar":null},"body":"From: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n\nSome people often run 'git status -b'.\nThe config variable status.branch allows to set it by default.\n\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n--- \nChanges to be commented:\n- Cleaning test\n\n Documentation/config.txt |    4 ++++\n builtin/commit.c         |    4 ++++\n t/t7508-status.sh        |   27 +++++++++++++++++++++++++++\n 3 files changed, 35 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 1983bf7..ecdcd6d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2070,6 +2070,10 @@ status.short::\n \tSet to true to enable --short by default in linkgit:git-status[1].\n \tThe option --no-short takes precedence over this variable.\n \n+status.branch::\n+\tSet to true to enable --branch by default in linkgit:git-status[1].\n+\tThe option --no-branch takes precedence over this variable.\n+\n status.showUntrackedFiles::\n \tBy default, linkgit:git-status[1] and linkgit:git-commit[1] show\n \tfiles which are not currently tracked by Git. Directories which\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 287f1cb..f2b5d44 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1117,6 +1117,10 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\t\tstatus_format = STATUS_FORMAT_SHORT;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.branch\")) {\n+\t\ts->show_branch = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n \t\ts->use_color = git_config_colorbool(k, v);\n \t\treturn 0;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex 9a07f15..958617a 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -1366,6 +1366,33 @@ test_expect_success '\"status.short=false\" weaker than \"-s\"' '\n \ttest_cmp actual expected_short\n '\n \n+test_expect_success '\"status.branch=true\" same as \"-b\"' '\n+\tgit -c status.branch=true status -s >actual &&\n+\tgit status -sb >expected_branch &&\n+\ttest_cmp actual expected_branch\n+'\n+\n+test_expect_success '\"status.branch=true\" different from \"--no-branch\"' '\n+\tgit -c status.branch=true status -s >actual &&\n+\tgit status -s --no-branch  >expected_nobranch &&\n+\ttest_must_fail test_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=true\" weaker than \"--no-branch\"' '\n+\tgit -c status.branch=true status -s --no-branch >actual &&\n+\ttest_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=false\" same as \"--no-branch\"' '\n+\tgit -c status.branch=false status -s >actual &&\n+\ttest_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=false\" weaker than \"-b\"' '\n+\tgit -c status.branch=false status -sb >actual &&\n+\ttest_cmp actual expected_branch\n+'\n+\n test_expect_success '\"Back to environment of test by default\"' '\n \tgit config status.showUntrackedFiles yes\n '\n-- \n1.7.8\n"},{"id":"299217","messageId":"1370877184-19409-2-git-send-email-y","threadId":"16566","inReplyTo":"1370877184-19409-1-git-send-email-y","subject":"v3 [PATCH 2/2] status:introduce status.branch to enable --branch by default","fromName":"","fromEmail":"y@ensimag.imag.fr","sentAt":"2013-06-10T15:13:04Z","receivedAt":"2013-06-10T15:13:04Z","isPatch":true,"sender":{"key":"y@ensimag.imag.fr","avatar":null},"body":"From: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n\nSome people often run 'git status -b'.\nThe config variable status.branch allows to set it by default.\n\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n--- \nChanges to be commented:\n- Cleaning test\n\n Documentation/config.txt |    4 ++++\n builtin/commit.c         |    4 ++++\n t/t7508-status.sh        |   27 +++++++++++++++++++++++++++\n 3 files changed, 35 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 1983bf7..ecdcd6d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2070,6 +2070,10 @@ status.short::\n \tSet to true to enable --short by default in linkgit:git-status[1].\n \tThe option --no-short takes precedence over this variable.\n \n+status.branch::\n+\tSet to true to enable --branch by default in linkgit:git-status[1].\n+\tThe option --no-branch takes precedence over this variable.\n+\n status.showUntrackedFiles::\n \tBy default, linkgit:git-status[1] and linkgit:git-commit[1] show\n \tfiles which are not currently tracked by Git. Directories which\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 287f1cb..f2b5d44 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1117,6 +1117,10 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\t\tstatus_format = STATUS_FORMAT_SHORT;\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.branch\")) {\n+\t\ts->show_branch = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.color\") || !strcmp(k, \"color.status\")) {\n \t\ts->use_color = git_config_colorbool(k, v);\n \t\treturn 0;\ndiff --git a/t/t7508-status.sh b/t/t7508-status.sh\nindex 9a07f15..958617a 100755\n--- a/t/t7508-status.sh\n+++ b/t/t7508-status.sh\n@@ -1366,6 +1366,33 @@ test_expect_success '\"status.short=false\" weaker than \"-s\"' '\n \ttest_cmp actual expected_short\n '\n \n+test_expect_success '\"status.branch=true\" same as \"-b\"' '\n+\tgit -c status.branch=true status -s >actual &&\n+\tgit status -sb >expected_branch &&\n+\ttest_cmp actual expected_branch\n+'\n+\n+test_expect_success '\"status.branch=true\" different from \"--no-branch\"' '\n+\tgit -c status.branch=true status -s >actual &&\n+\tgit status -s --no-branch  >expected_nobranch &&\n+\ttest_must_fail test_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=true\" weaker than \"--no-branch\"' '\n+\tgit -c status.branch=true status -s --no-branch >actual &&\n+\ttest_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=false\" same as \"--no-branch\"' '\n+\tgit -c status.branch=false status -s >actual &&\n+\ttest_cmp actual expected_nobranch\n+'\n+\n+test_expect_success '\"status.branch=false\" weaker than \"-b\"' '\n+\tgit -c status.branch=false status -sb >actual &&\n+\ttest_cmp actual expected_branch\n+'\n+\n test_expect_success '\"Back to environment of test by default\"' '\n \tgit config status.showUntrackedFiles yes\n '\n-- \n1.7.8\n\n"},{"id":"220301","messageId":"vpqehcaf0wr.fsf@anie.imag.fr","threadId":"16566","inReplyTo":"1370877184-19409-1-git-send-email-y","subject":"Re: v3 [PATCH 1/2] status: introduce status.short to enable --short by default","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-06-10T15:20:04Z","receivedAt":"2013-06-10T15:20:04Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"y@ensimag.imag.fr writes:\n\n> To: y@ensimag.imag.fr\n\nCommon mistake, but you're not supposed to answer \"y\" when you're\nprompted for an email ;-).\n\nset sendemail.from to avoid this.\n\n> +test_expect_success '\"Setup of environment of test\"' '\n\nSame problem as v2.\n\n> +test_expect_success '\"Back to environment of test by default\"' '\n> +\tgit config status.showUntrackedFiles yes\n\nSame.\n\nProbably something went wrong during your rebase.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"220334","messageId":"7vvc5lj13j.fsf@alter.siamese.dyndns.org","threadId":"16566","inReplyTo":"vpqehcaf0wr.fsf@anie.imag.fr","subject":"Re: v3 [PATCH 1/2] status: introduce status.short to enable --short by default","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-10T18:02:24Z","receivedAt":"2013-06-10T18:02:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> y@ensimag.imag.fr writes:\n>\n>> To: y@ensimag.imag.fr\n>\n> Common mistake, but you're not supposed to answer \"y\" when you're\n> prompted for an email ;-).\n\nDidn't we introduce safety against this in v1.7.12.1 and later?  Is\nthe new release taking more than 9 months to percolate, or are there\nstill other codepaths that allow this to happen that we need to add\nfurther safety?\n"},{"id":"233889","messageId":"1391002395-8665-1-git-send-email-gitter.spiros@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH 3/3] builtin/blame.c: reduce scope of variables","fromName":"Elia Pinto","fromEmail":"gitter.spiros@gmail.com","sentAt":"2014-01-29T13:33:15Z","receivedAt":"2014-01-29T13:33:15Z","isPatch":true,"sender":{"key":"gitter.spiros@gmail.com","avatar":"https://avatars.githubusercontent.com/u/158490?v=4"},"body":"Signed-off-by: Elia Pinto <gitter.spiros@gmail.com>\n---\n builtin/blame.c |    6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex e44a6bb..967a7c6 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1580,14 +1580,14 @@ static const char *format_time(unsigned long time, const char *tz_str,\n \t\t\t       int show_raw_time)\n {\n \tstatic char time_buf[128];\n-\tconst char *time_str;\n-\tint time_len;\n-\tint tz;\n \n \tif (show_raw_time) {\n \t\tsnprintf(time_buf, sizeof(time_buf), \"%lu %s\", time, tz_str);\n \t}\n \telse {\n+\t\tconst char *time_str;\n+\t\tint time_len;\n+\t\tint tz;\n \t\ttz = atoi(tz_str);\n \t\ttime_str = show_date(time, tz, blame_date_mode);\n \t\ttime_len = strlen(time_str);\n-- \n1.7.10.4\n"},{"id":"236119","messageId":"BLU0-SMTP1988500CEC43BD4C57254AB91890@phx.gbl","threadId":"16566","inReplyTo":"y","subject":"[PATCH] use strchrnul() in place of strchr() and strlen()","fromName":"Rohit Mani","fromEmail":"rohit.mani@outlook.com","sentAt":"2014-03-05T19:29:02Z","receivedAt":"2014-03-05T19:29:02Z","isPatch":true,"sender":{"key":"rohit.mani@outlook.com","avatar":null},"body":"Avoid scanning strings twice, once with strchr() and then with\nstrlen(), by using strchrnul(). Update the conditional expressions\ninvolving the return value of strchrnul() with a check for '\\0'.\n\nSigned-off-by: Rohit Mani <rohit.mani@outlook.com>\n---\nI plan to apply for the GSoC.\n\n archive.c        |    4 ++--\n cache-tree.c     |   16 +++++++---------\n diff.c           |    9 +++------\n fast-import.c    |   35 ++++++++++++++---------------------\n match-trees.c    |   11 ++++-------\n parse-options.c  |    5 +----\n pretty.c         |    5 ++---\n remote-testsvn.c |    4 ++--\n ws.c             |    7 ++-----\n 9 files changed, 37 insertions(+), 59 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 346f3b2..d196215 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -259,8 +259,8 @@ static void parse_treeish_arg(const char **argv,\n \t/* Remotes are only allowed to fetch actual refs */\n \tif (remote) {\n \t\tchar *ref = NULL;\n-\t\tconst char *colon = strchr(name, ':');\n-\t\tint refnamelen = colon ? colon - name : strlen(name);\n+\t\tconst char *colon = strchrnul(name, ':');\n+\t\tint refnamelen = colon - name;\n \n \t\tif (!dwim_ref(name, refnamelen, sha1, &ref))\n \t\t\tdie(\"no such ref: %.*s\", refnamelen, name);\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 0bbec43..21a13cf 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -121,11 +121,11 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)\n \n \tif (!it)\n \t\treturn;\n-\tslash = strchr(path, '/');\n+\tslash = strchrnul(path, '/');\n \tit->entry_count = -1;\n-\tif (!slash) {\n+\tif (*slash == '\\0') {\n \t\tint pos;\n-\t\tnamelen = strlen(path);\n+\t\tnamelen = slash - path;\n \t\tpos = subtree_pos(it, path, namelen);\n \t\tif (0 <= pos) {\n \t\t\tcache_tree_free(&it->down[pos]->cache_tree);\n@@ -554,9 +554,7 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \t\tconst char *slash;\n \t\tstruct cache_tree_sub *sub;\n \n-\t\tslash = strchr(path, '/');\n-\t\tif (!slash)\n-\t\t\tslash = path + strlen(path);\n+\t\tslash = strchrnul(path, '/');\n \t\t/* between path and slash is the name of the\n \t\t * subtree to look for.\n \t\t */\n@@ -564,10 +562,10 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \t\tif (!sub)\n \t\t\treturn NULL;\n \t\tit = sub->cache_tree;\n-\t\tif (slash)\n-\t\t\twhile (*slash && *slash == '/')\n+\t\tif (*slash != '\\0')\n+\t\t\twhile (*slash != '\\0' && *slash == '/')\n \t\t\t\tslash++;\n-\t\tif (!slash || !*slash)\n+\t\tif (*slash == '\\0' || !*slash)\n \t\t\treturn it; /* prefix ended with slashes */\n \t\tpath = slash;\n \t}\ndiff --git a/diff.c b/diff.c\nindex e800666..83a039b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3365,14 +3365,11 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va\n \tif (c != '-')\n \t\treturn 0;\n \targ++;\n-\teq = strchr(arg, '=');\n-\tif (eq)\n-\t\tlen = eq - arg;\n-\telse\n-\t\tlen = strlen(arg);\n+\teq = strchrnul(arg, '=');\n+\tlen = eq - arg;\n \tif (!len || strncmp(arg, arg_long, len))\n \t\treturn 0;\n-\tif (eq) {\n+\tif (*eq != '\\0') {\n \t\tint n;\n \t\tchar *end;\n \t\tif (!isdigit(*++eq))\ndiff --git a/fast-import.c b/fast-import.c\nindex 4fd18a3..449595d 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -1485,14 +1485,12 @@ static int tree_content_set(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n+\n \tif (!n)\n \t\tdie(\"Empty path component found in input\");\n-\tif (!slash1 && !S_ISDIR(mode) && subtree)\n+\tif (*slash1 == '\\0' && !S_ISDIR(mode) && subtree)\n \t\tdie(\"Non-directories cannot have subtrees\");\n \n \tif (!root->tree)\n@@ -1501,7 +1499,7 @@ static int tree_content_set(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (!slash1) {\n+\t\t\tif (*slash1 == '\\0') {\n \t\t\t\tif (!S_ISDIR(mode)\n \t\t\t\t\t\t&& e->versions[1].mode == mode\n \t\t\t\t\t\t&& !hashcmp(e->versions[1].sha1, sha1))\n@@ -1552,7 +1550,7 @@ static int tree_content_set(\n \te->versions[0].mode = 0;\n \thashclr(e->versions[0].sha1);\n \tt->entries[t->entry_count++] = e;\n-\tif (slash1) {\n+\tif (*slash1 != '\\0') {\n \t\te->tree = new_tree_content(8);\n \t\te->versions[1].mode = S_IFDIR;\n \t\ttree_content_set(e, slash1 + 1, sha1, mode, subtree);\n@@ -1576,11 +1574,8 @@ static int tree_content_remove(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n \n \tif (!root->tree)\n \t\tload_tree(root);\n@@ -1594,7 +1589,7 @@ static int tree_content_remove(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (slash1 && !S_ISDIR(e->versions[1].mode))\n+\t\t\tif (*slash1 != '\\0' && !S_ISDIR(e->versions[1].mode))\n \t\t\t\t/*\n \t\t\t\t * If p names a file in some subdirectory, and a\n \t\t\t\t * file or symlink matching the name of the\n@@ -1602,7 +1597,7 @@ static int tree_content_remove(\n \t\t\t\t * exist and need not be deleted.\n \t\t\t\t */\n \t\t\t\treturn 1;\n-\t\t\tif (!slash1 || !S_ISDIR(e->versions[1].mode))\n+\t\t\tif (*slash1 == '\\0' || !S_ISDIR(e->versions[1].mode))\n \t\t\t\tgoto del_entry;\n \t\t\tif (!e->tree)\n \t\t\t\tload_tree(e);\n@@ -1644,11 +1639,9 @@ static int tree_content_get(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n+\n \tif (!n && !allow_root)\n \t\tdie(\"Empty path component found in input\");\n \n@@ -1664,7 +1657,7 @@ static int tree_content_get(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (!slash1)\n+\t\t\tif (*slash1 == '\\0')\n \t\t\t\tgoto found_entry;\n \t\t\tif (!S_ISDIR(e->versions[1].mode))\n \t\t\t\treturn 0;\ndiff --git a/match-trees.c b/match-trees.c\nindex 7873cde..7cca0f6 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -182,13 +182,10 @@ static int splice_tree(const unsigned char *hash1,\n \tenum object_type type;\n \tint status;\n \n-\tsubpath = strchr(prefix, '/');\n-\tif (!subpath)\n-\t\ttoplen = strlen(prefix);\n-\telse {\n-\t\ttoplen = subpath - prefix;\n+\tsubpath = strchrnul(prefix, '/');\n+\ttoplen = subpath - prefix;\n+\tif (*subpath != '\\0')\n \t\tsubpath++;\n-\t}\n \n \tbuf = read_sha1_file(hash1, &type, &sz);\n \tif (!buf)\n@@ -215,7 +212,7 @@ static int splice_tree(const unsigned char *hash1,\n \tif (!rewrite_here)\n \t\tdie(\"entry %.*s not found in tree %s\",\n \t\t    toplen, prefix, sha1_to_hex(hash1));\n-\tif (subpath) {\n+\tif (*subpath != '\\0') {\n \t\tstatus = splice_tree(rewrite_here, subpath, hash2, subtree);\n \t\tif (status)\n \t\t\treturn status;\ndiff --git a/parse-options.c b/parse-options.c\nindex 7b8d3fa..a5fa0b8 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -223,13 +223,10 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,\n                           const struct option *options)\n {\n \tconst struct option *all_opts = options;\n-\tconst char *arg_end = strchr(arg, '=');\n+\tconst char *arg_end = strchrnul(arg, '=');\n \tconst struct option *abbrev_option = NULL, *ambiguous_option = NULL;\n \tint abbrev_flags = 0, ambiguous_flags = 0;\n \n-\tif (!arg_end)\n-\t\targ_end = arg + strlen(arg);\n-\n \tfor (; options->type != OPTION_END; options++) {\n \t\tconst char *rest, *long_name = options->long_name;\n \t\tint flags = 0, opt_flags = 0;\ndiff --git a/pretty.c b/pretty.c\nindex 87db08b..b1f60f9 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -549,14 +549,13 @@ static char *get_header(const struct commit *commit, const char *msg,\n \tconst char *line = msg;\n \n \twhile (line) {\n-\t\tconst char *eol = strchr(line, '\\n'), *next;\n+\t\tconst char *eol = strchrnul(line, '\\n'), *next;\n \n \t\tif (line == eol)\n \t\t\treturn NULL;\n-\t\tif (!eol) {\n+\t\tif (*eol == '\\0') {\n \t\t\twarning(\"malformed commit (header is missing newline): %s\",\n \t\t\t\tsha1_to_hex(commit->object.sha1));\n-\t\t\teol = line + strlen(line);\n \t\t\tnext = NULL;\n \t\t} else\n \t\t\tnext = eol + 1;\ndiff --git a/remote-testsvn.c b/remote-testsvn.c\nindex 078f1ff..6be55cb 100644\n--- a/remote-testsvn.c\n+++ b/remote-testsvn.c\n@@ -78,8 +78,8 @@ static int parse_rev_note(const char *msg, struct rev_note *res)\n \tsize_t len;\n \n \twhile (*msg) {\n-\t\tend = strchr(msg, '\\n');\n-\t\tlen = end ? end - msg : strlen(msg);\n+\t\tend = strchrnul(msg, '\\n');\n+\t\tlen = end - msg;\n \n \t\tkey = \"Revision-number: \";\n \t\tif (starts_with(msg, key)) {\ndiff --git a/ws.c b/ws.c\nindex b498d75..ea4b2b1 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -33,11 +33,8 @@ unsigned parse_whitespace_rule(const char *string)\n \t\tint negated = 0;\n \n \t\tstring = string + strspn(string, \", \\t\\n\\r\");\n-\t\tep = strchr(string, ',');\n-\t\tif (!ep)\n-\t\t\tlen = strlen(string);\n-\t\telse\n-\t\t\tlen = ep - string;\n+\t\tep = strchrnul(string, ',');\n+\t\tlen = ep - string;\n \n \t\tif (*string == '-') {\n \t\t\tnegated = 1;\n-- \n1.7.9.5\n"},{"id":"236197","messageId":"xmqqzjl3vyii.fsf@gitster.dls.corp.google.com","threadId":"16566","inReplyTo":"BLU0-SMTP1988500CEC43BD4C57254AB91890@phx.gbl","subject":"Re: [PATCH] use strchrnul() in place of strchr() and strlen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-06T20:54:29Z","receivedAt":"2014-03-06T20:54:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rohit Mani <rohit.mani@outlook.com> writes:\n\n> Avoid scanning strings twice, once with strchr() and then with\n> strlen(), by using strchrnul(). Update the conditional expressions\n> involving the return value of strchrnul() with a check for '\\0'.\n>\n> Signed-off-by: Rohit Mani <rohit.mani@outlook.com>\n> ---\n\nNicely done.  I am not sure if you need to say the \"update the\nconditional...\", which is a logical consequence of such a conversion\nand goes without saying, though.\n\n>  cache-tree.c     |   16 +++++++---------\n\nThis part may overlap with other topics in flight, but I expect the\nconflict resolution would be trivial.\n\n> diff --git a/cache-tree.c b/cache-tree.c\n> index 0bbec43..21a13cf 100644\n> --- a/cache-tree.c\n> +++ b/cache-tree.c\n> @@ -121,11 +121,11 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)\n>  \n>  \tif (!it)\n>  \t\treturn;\n> -\tslash = strchr(path, '/');\n> +\tslash = strchrnul(path, '/');\n>  \tit->entry_count = -1;\n> -\tif (!slash) {\n> +\tif (*slash == '\\0') {\n\nLet's just say\n\n\tif (!*slash)\n\ninstead; it is more idiomatic (I won't repeat this for other hunks).\n\n>  \t\tint pos;\n> -\t\tnamelen = strlen(path);\n> +\t\tnamelen = slash - path;\n\nAfter this \"if (!*slash)\", we compute \"namelen = slash-path\".\nPerhaps we can lose this assignment and the other one by hoisting it\nup before \"if (!*slash)\"?\n\n> @@ -564,10 +562,10 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n> +\t\tif (*slash == '\\0' || !*slash)\n\nHuh?  \"The byte pointed at by 'slash' is NUL, or it is NUL\"???\n\nOther than that, looks good to me.\n\nThanks.\n"},{"id":"236317","messageId":"BLU0-SMTP956BEABA437FB370F76953918A0@phx.gbl","threadId":"16566","inReplyTo":"y","subject":"[GSoC][PATCH v2] use strchrnul() in place of strchr() and strlen()","fromName":"Rohit Mani","fromEmail":"rohit.mani@outlook.com","sentAt":"2014-03-08T06:48:31Z","receivedAt":"2014-03-08T06:48:31Z","isPatch":true,"sender":{"key":"rohit.mani@outlook.com","avatar":null},"body":"Avoid scanning strings twice, once with strchr() and then with\nstrlen(), by using strchrnul().\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Rohit Mani <rohit.mani@outlook.com>\n---\nPATCH v2 Updated commit description\n\tUpdated code according to suggestions [1]\n[1]\thttp://article.gmane.org/gmane.comp.version-control.git/243550\n\t\n archive.c        |    4 ++--\n cache-tree.c     |   15 ++++++---------\n diff.c           |    9 +++------\n fast-import.c    |   35 +++++++++++++----------------------\n match-trees.c    |   11 ++++-------\n parse-options.c  |    5 +----\n pretty.c         |    5 ++---\n remote-testsvn.c |    4 ++--\n ws.c             |    7 ++-----\n 9 files changed, 35 insertions(+), 60 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 346f3b2..d196215 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -259,8 +259,8 @@ static void parse_treeish_arg(const char **argv,\n \t/* Remotes are only allowed to fetch actual refs */\n \tif (remote) {\n \t\tchar *ref = NULL;\n-\t\tconst char *colon = strchr(name, ':');\n-\t\tint refnamelen = colon ? colon - name : strlen(name);\n+\t\tconst char *colon = strchrnul(name, ':');\n+\t\tint refnamelen = colon - name;\n \n \t\tif (!dwim_ref(name, refnamelen, sha1, &ref))\n \t\t\tdie(\"no such ref: %.*s\", refnamelen, name);\ndiff --git a/cache-tree.c b/cache-tree.c\nindex 0bbec43..2130f32 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -121,11 +121,11 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)\n \n \tif (!it)\n \t\treturn;\n-\tslash = strchr(path, '/');\n+\tslash = strchrnul(path, '/');\n+\tnamelen = slash - path;\n \tit->entry_count = -1;\n-\tif (!slash) {\n+\tif (!*slash) {\n \t\tint pos;\n-\t\tnamelen = strlen(path);\n \t\tpos = subtree_pos(it, path, namelen);\n \t\tif (0 <= pos) {\n \t\t\tcache_tree_free(&it->down[pos]->cache_tree);\n@@ -143,7 +143,6 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)\n \t\t}\n \t\treturn;\n \t}\n-\tnamelen = slash - path;\n \tdown = find_subtree(it, path, namelen, 0);\n \tif (down)\n \t\tcache_tree_invalidate_path(down->cache_tree, slash + 1);\n@@ -554,9 +553,7 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \t\tconst char *slash;\n \t\tstruct cache_tree_sub *sub;\n \n-\t\tslash = strchr(path, '/');\n-\t\tif (!slash)\n-\t\t\tslash = path + strlen(path);\n+\t\tslash = strchrnul(path, '/');\n \t\t/* between path and slash is the name of the\n \t\t * subtree to look for.\n \t\t */\n@@ -564,10 +561,10 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat\n \t\tif (!sub)\n \t\t\treturn NULL;\n \t\tit = sub->cache_tree;\n-\t\tif (slash)\n+\t\tif (*slash)\n \t\t\twhile (*slash && *slash == '/')\n \t\t\t\tslash++;\n-\t\tif (!slash || !*slash)\n+\t\tif (!*slash)\n \t\t\treturn it; /* prefix ended with slashes */\n \t\tpath = slash;\n \t}\ndiff --git a/diff.c b/diff.c\nindex e800666..cb6b98b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3365,14 +3365,11 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va\n \tif (c != '-')\n \t\treturn 0;\n \targ++;\n-\teq = strchr(arg, '=');\n-\tif (eq)\n-\t\tlen = eq - arg;\n-\telse\n-\t\tlen = strlen(arg);\n+\teq = strchrnul(arg, '=');\n+\tlen = eq - arg;\n \tif (!len || strncmp(arg, arg_long, len))\n \t\treturn 0;\n-\tif (eq) {\n+\tif (*eq) {\n \t\tint n;\n \t\tchar *end;\n \t\tif (!isdigit(*++eq))\ndiff --git a/fast-import.c b/fast-import.c\nindex 4fd18a3..dbbf6b0 100644\n--- a/fast-import.c\n+++ b/fast-import.c\n@@ -1485,14 +1485,11 @@ static int tree_content_set(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n \tif (!n)\n \t\tdie(\"Empty path component found in input\");\n-\tif (!slash1 && !S_ISDIR(mode) && subtree)\n+\tif (!*slash1 && !S_ISDIR(mode) && subtree)\n \t\tdie(\"Non-directories cannot have subtrees\");\n \n \tif (!root->tree)\n@@ -1501,7 +1498,7 @@ static int tree_content_set(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (!slash1) {\n+\t\t\tif (!*slash1) {\n \t\t\t\tif (!S_ISDIR(mode)\n \t\t\t\t\t\t&& e->versions[1].mode == mode\n \t\t\t\t\t\t&& !hashcmp(e->versions[1].sha1, sha1))\n@@ -1552,7 +1549,7 @@ static int tree_content_set(\n \te->versions[0].mode = 0;\n \thashclr(e->versions[0].sha1);\n \tt->entries[t->entry_count++] = e;\n-\tif (slash1) {\n+\tif (*slash1) {\n \t\te->tree = new_tree_content(8);\n \t\te->versions[1].mode = S_IFDIR;\n \t\ttree_content_set(e, slash1 + 1, sha1, mode, subtree);\n@@ -1576,12 +1573,9 @@ static int tree_content_remove(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n-\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n+\t\n \tif (!root->tree)\n \t\tload_tree(root);\n \n@@ -1594,7 +1588,7 @@ static int tree_content_remove(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (slash1 && !S_ISDIR(e->versions[1].mode))\n+\t\t\tif (*slash1 && !S_ISDIR(e->versions[1].mode))\n \t\t\t\t/*\n \t\t\t\t * If p names a file in some subdirectory, and a\n \t\t\t\t * file or symlink matching the name of the\n@@ -1602,7 +1596,7 @@ static int tree_content_remove(\n \t\t\t\t * exist and need not be deleted.\n \t\t\t\t */\n \t\t\t\treturn 1;\n-\t\t\tif (!slash1 || !S_ISDIR(e->versions[1].mode))\n+\t\t\tif (!*slash1 || !S_ISDIR(e->versions[1].mode))\n \t\t\t\tgoto del_entry;\n \t\t\tif (!e->tree)\n \t\t\t\tload_tree(e);\n@@ -1644,11 +1638,8 @@ static int tree_content_get(\n \tunsigned int i, n;\n \tstruct tree_entry *e;\n \n-\tslash1 = strchr(p, '/');\n-\tif (slash1)\n-\t\tn = slash1 - p;\n-\telse\n-\t\tn = strlen(p);\n+\tslash1 = strchrnul(p, '/');\n+\tn = slash1 - p;\n \tif (!n && !allow_root)\n \t\tdie(\"Empty path component found in input\");\n \n@@ -1664,7 +1655,7 @@ static int tree_content_get(\n \tfor (i = 0; i < t->entry_count; i++) {\n \t\te = t->entries[i];\n \t\tif (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {\n-\t\t\tif (!slash1)\n+\t\t\tif (!*slash1)\n \t\t\t\tgoto found_entry;\n \t\t\tif (!S_ISDIR(e->versions[1].mode))\n \t\t\t\treturn 0;\ndiff --git a/match-trees.c b/match-trees.c\nindex 7873cde..e80b4af 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -182,13 +182,10 @@ static int splice_tree(const unsigned char *hash1,\n \tenum object_type type;\n \tint status;\n \n-\tsubpath = strchr(prefix, '/');\n-\tif (!subpath)\n-\t\ttoplen = strlen(prefix);\n-\telse {\n-\t\ttoplen = subpath - prefix;\n+\tsubpath = strchrnul(prefix, '/');\n+\ttoplen = subpath - prefix;\n+\tif (*subpath)\n \t\tsubpath++;\n-\t}\n \n \tbuf = read_sha1_file(hash1, &type, &sz);\n \tif (!buf)\n@@ -215,7 +212,7 @@ static int splice_tree(const unsigned char *hash1,\n \tif (!rewrite_here)\n \t\tdie(\"entry %.*s not found in tree %s\",\n \t\t    toplen, prefix, sha1_to_hex(hash1));\n-\tif (subpath) {\n+\tif (*subpath) {\n \t\tstatus = splice_tree(rewrite_here, subpath, hash2, subtree);\n \t\tif (status)\n \t\t\treturn status;\ndiff --git a/parse-options.c b/parse-options.c\nindex 7b8d3fa..a5fa0b8 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -223,13 +223,10 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,\n                           const struct option *options)\n {\n \tconst struct option *all_opts = options;\n-\tconst char *arg_end = strchr(arg, '=');\n+\tconst char *arg_end = strchrnul(arg, '=');\n \tconst struct option *abbrev_option = NULL, *ambiguous_option = NULL;\n \tint abbrev_flags = 0, ambiguous_flags = 0;\n \n-\tif (!arg_end)\n-\t\targ_end = arg + strlen(arg);\n-\n \tfor (; options->type != OPTION_END; options++) {\n \t\tconst char *rest, *long_name = options->long_name;\n \t\tint flags = 0, opt_flags = 0;\ndiff --git a/pretty.c b/pretty.c\nindex 87db08b..99ba8ae 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -549,14 +549,13 @@ static char *get_header(const struct commit *commit, const char *msg,\n \tconst char *line = msg;\n \n \twhile (line) {\n-\t\tconst char *eol = strchr(line, '\\n'), *next;\n+\t\tconst char *eol = strchrnul(line, '\\n'), *next;\n \n \t\tif (line == eol)\n \t\t\treturn NULL;\n-\t\tif (!eol) {\n+\t\tif (!*eol) {\n \t\t\twarning(\"malformed commit (header is missing newline): %s\",\n \t\t\t\tsha1_to_hex(commit->object.sha1));\n-\t\t\teol = line + strlen(line);\n \t\t\tnext = NULL;\n \t\t} else\n \t\t\tnext = eol + 1;\ndiff --git a/remote-testsvn.c b/remote-testsvn.c\nindex 078f1ff..6be55cb 100644\n--- a/remote-testsvn.c\n+++ b/remote-testsvn.c\n@@ -78,8 +78,8 @@ static int parse_rev_note(const char *msg, struct rev_note *res)\n \tsize_t len;\n \n \twhile (*msg) {\n-\t\tend = strchr(msg, '\\n');\n-\t\tlen = end ? end - msg : strlen(msg);\n+\t\tend = strchrnul(msg, '\\n');\n+\t\tlen = end - msg;\n \n \t\tkey = \"Revision-number: \";\n \t\tif (starts_with(msg, key)) {\ndiff --git a/ws.c b/ws.c\nindex b498d75..ea4b2b1 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -33,11 +33,8 @@ unsigned parse_whitespace_rule(const char *string)\n \t\tint negated = 0;\n \n \t\tstring = string + strspn(string, \", \\t\\n\\r\");\n-\t\tep = strchr(string, ',');\n-\t\tif (!ep)\n-\t\t\tlen = strlen(string);\n-\t\telse\n-\t\t\tlen = ep - string;\n+\t\tep = strchrnul(string, ',');\n+\t\tlen = ep - string;\n \n \t\tif (*string == '-') {\n \t\t\tnegated = 1;\n-- \n1.7.9.5\n"},{"id":"236412","messageId":"xmqq1tyarrox.fsf@gitster.dls.corp.google.com","threadId":"16566","inReplyTo":"BLU0-SMTP956BEABA437FB370F76953918A0@phx.gbl","subject":"Re: [GSoC][PATCH v2] use strchrnul() in place of strchr() and strlen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-10T15:36:46Z","receivedAt":"2014-03-10T15:36:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rohit Mani <rohit.mani@outlook.com> writes:\n\n> Avoid scanning strings twice, once with strchr() and then with\n> strlen(), by using strchrnul().\n\nThanks. The patch looks good.\n"},{"id":"236438","messageId":"BLU0-SMTP15143B8ABC984B63BB4F391D5740@phx.gbl","threadId":"16566","inReplyTo":"y","subject":"[PATCH][GSOC2014]","fromName":"TamerTas","fromEmail":"tamertas@outlook.com","sentAt":"2014-03-10T19:04:21Z","receivedAt":"2014-03-10T19:04:21Z","isPatch":true,"sender":{"key":"tamertas@outlook.com","avatar":"https://gravatar.com/avatar/618df903c9a2fa12e76148ca7a106e3236594de6002a190c70e1ace5c30e0e40?d=mp&s=160"},"body":"I inspected branch.c:install_branch_config() and since\nthe conditionals cover all the possibilities, I decided\nthat making the code table-driven would be much cleaner\nand shorter. I've ran the tests and they all passed. \nReimplementation using \"git am\" also didn't have any problems.\nPlease let me know if there is any problems that I missed.\nI would also love to learn if you think that the code was better\nbefore and why it was better.\n\nTamerTas (1):\n  changed logical chain in branch.c to lookup tables\n\n branch.c |   31 ++++++++-----------------------\n 1 file changed, 8 insertions(+), 23 deletions(-)\n\n-- \n1.7.9.5\n"},{"id":"236437","messageId":"BLU0-SMTP15131ED34F192C9871ACC0CD5740@phx.gbl","threadId":"16566","inReplyTo":"1394478262-17911-1-git-send-email-tamertas@outlook.com","subject":"[PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"TamerTas","fromEmail":"tamertas@outlook.com","sentAt":"2014-03-10T19:04:22Z","receivedAt":"2014-03-10T19:04:22Z","isPatch":true,"sender":{"key":"tamertas@outlook.com","avatar":"https://gravatar.com/avatar/618df903c9a2fa12e76148ca7a106e3236594de6002a190c70e1ace5c30e0e40?d=mp&s=160"},"body":"\nSigned-off-by: TamerTas <tamertas@outlook.com>\n---\n branch.c |   31 ++++++++-----------------------\n 1 file changed, 8 insertions(+), 23 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 723a36b..397edd3 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -50,6 +50,9 @@ static int should_setup_rebase(const char *origin)\n void install_branch_config(int flag, const char *local, const char *origin, const char *remote)\n {\n \tconst char *shortname = remote + 11;\n+\tconst char *location[] = {\"local\", \"remote\"};\n+\tconst char *type[] = {\"branch\", \"ref\"};\n+\n \tint remote_is_branch = starts_with(remote, \"refs/heads/\");\n \tstruct strbuf key = STRBUF_INIT;\n \tint rebasing = should_setup_rebase(origin);\n@@ -77,29 +80,11 @@ void install_branch_config(int flag, const char *local, const char *origin, cons\n \tstrbuf_release(&key);\n \n \tif (flag & BRANCH_CONFIG_VERBOSE) {\n-\t\tif (remote_is_branch && origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s.\"),\n-\t\t\t\t  local, shortname, origin);\n-\t\telse if (remote_is_branch && !origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track local branch %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track local branch %s.\"),\n-\t\t\t\t  local, shortname);\n-\t\telse if (!remote_is_branch && origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track remote ref %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track remote ref %s.\"),\n-\t\t\t\t  local, remote);\n-\t\telse if (!remote_is_branch && !origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track local ref %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track local ref %s.\"),\n-\t\t\t\t  local, remote);\n-\t\telse\n-\t\t\tdie(\"BUG: impossible combination of %d and %p\",\n-\t\t\t    remote_is_branch, origin);\n+        \n+        printf_ln(rebasing ?\n+              _(\"Branch %s set up to track %s %s %s by rebasing.\") :\n+              _(\"Branch %s set up to track %s %s %s.\"),\n+              local, location[!origin], type[remote_is_branch], remote);\n \t}\n }\n \n-- \n1.7.9.5\n"},{"id":"236459","messageId":"531E2917.1000908@googlemail.com","threadId":"16566","inReplyTo":"BLU0-SMTP15131ED34F192C9871ACC0CD5740@phx.gbl","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Stefan Beller","fromEmail":"stefanbeller@gmail.com","sentAt":"2014-03-10T21:05:27Z","receivedAt":"2014-03-10T21:05:27Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On 10.03.2014 20:04, TamerTas wrote:\n> \n> Signed-off-by: TamerTas <tamertas@outlook.com>\n> ---\n>  branch.c |   31 ++++++++-----------------------\n>  1 file changed, 8 insertions(+), 23 deletions(-)\n> \n> diff --git a/branch.c b/branch.c\n> index 723a36b..397edd3 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -50,6 +50,9 @@ static int should_setup_rebase(const char *origin)\n>  void install_branch_config(int flag, const char *local, const char *origin, const char *remote)\n>  {\n>  \tconst char *shortname = remote + 11;\n> +\tconst char *location[] = {\"local\", \"remote\"};\n> +\tconst char *type[] = {\"branch\", \"ref\"};\n> +\n>  \tint remote_is_branch = starts_with(remote, \"refs/heads/\");\n>  \tstruct strbuf key = STRBUF_INIT;\n>  \tint rebasing = should_setup_rebase(origin);\n> @@ -77,29 +80,11 @@ void install_branch_config(int flag, const char *local, const char *origin, cons\n>  \tstrbuf_release(&key);\n>  \n>  \tif (flag & BRANCH_CONFIG_VERBOSE) {\n> -\t\tif (remote_is_branch && origin)\n> -\t\t\tprintf_ln(rebasing ?\n> -\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s by rebasing.\") :\n> -\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s.\"),\n> -\t\t\t\t  local, shortname, origin);\n> -\t\telse if (remote_is_branch && !origin)\n> -\t\t\tprintf_ln(rebasing ?\n> -\t\t\t\t  _(\"Branch %s set up to track local branch %s by rebasing.\") :\n> -\t\t\t\t  _(\"Branch %s set up to track local branch %s.\"),\n> -\t\t\t\t  local, shortname);\n> -\t\telse if (!remote_is_branch && origin)\n> -\t\t\tprintf_ln(rebasing ?\n> -\t\t\t\t  _(\"Branch %s set up to track remote ref %s by rebasing.\") :\n> -\t\t\t\t  _(\"Branch %s set up to track remote ref %s.\"),\n> -\t\t\t\t  local, remote);\n> -\t\telse if (!remote_is_branch && !origin)\n> -\t\t\tprintf_ln(rebasing ?\n> -\t\t\t\t  _(\"Branch %s set up to track local ref %s by rebasing.\") :\n> -\t\t\t\t  _(\"Branch %s set up to track local ref %s.\"),\n> -\t\t\t\t  local, remote);\n> -\t\telse\n> -\t\t\tdie(\"BUG: impossible combination of %d and %p\",\n> -\t\t\t    remote_is_branch, origin);\n> +        \n> +        printf_ln(rebasing ?\n> +              _(\"Branch %s set up to track %s %s %s by rebasing.\") :\n> +              _(\"Branch %s set up to track %s %s %s.\"),\n> +              local, location[!origin], type[remote_is_branch], remote);\n>  \t}\n>  }\n>  \n> \n\nThis one is neat, I'd favor this one as opposed to the one posted\nearlier today.\n\nAcked-by: Stefan Beller <stefanbeller@googlemail.com>\n"},{"id":"236463","messageId":"CAPig+cS3H_8gZOuyro_Vt0ay3xphvsB92zFgwFfCdgKsXjU0Gg@mail.gmail.com","threadId":"16566","inReplyTo":"BLU0-SMTP15131ED34F192C9871ACC0CD5740@phx.gbl","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-10T21:25:52Z","receivedAt":"2014-03-10T21:25:52Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Mar 10, 2014 at 3:04 PM, TamerTas <tamertas@outlook.com> wrote:\n> Signed-off-by: TamerTas <tamertas@outlook.com>\n\nThanks for the submission. It appears to be well executed. Read below\nfor a concern about the approach taken.\n\n> ---\n>  branch.c |   31 ++++++++-----------------------\n>  1 file changed, 8 insertions(+), 23 deletions(-)\n>\n> diff --git a/branch.c b/branch.c\n> index 723a36b..397edd3 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -50,6 +50,9 @@ static int should_setup_rebase(const char *origin)\n>  void install_branch_config(int flag, const char *local, const char *origin, const char *remote)\n>  {\n>         const char *shortname = remote + 11;\n> +       const char *location[] = {\"local\", \"remote\"};\n> +       const char *type[] = {\"branch\", \"ref\"};\n> +\n>         int remote_is_branch = starts_with(remote, \"refs/heads/\");\n>         struct strbuf key = STRBUF_INIT;\n>         int rebasing = should_setup_rebase(origin);\n> @@ -77,29 +80,11 @@ void install_branch_config(int flag, const char *local, const char *origin, cons\n>         strbuf_release(&key);\n>\n>         if (flag & BRANCH_CONFIG_VERBOSE) {\n> -               if (remote_is_branch && origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track remote branch %s from %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track remote branch %s from %s.\"),\n> -                                 local, shortname, origin);\n> -               else if (remote_is_branch && !origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track local branch %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track local branch %s.\"),\n> -                                 local, shortname);\n> -               else if (!remote_is_branch && origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track remote ref %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track remote ref %s.\"),\n> -                                 local, remote);\n> -               else if (!remote_is_branch && !origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track local ref %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track local ref %s.\"),\n> -                                 local, remote);\n> -               else\n> -                       die(\"BUG: impossible combination of %d and %p\",\n> -                           remote_is_branch, origin);\n> +\n> +        printf_ln(rebasing ?\n> +              _(\"Branch %s set up to track %s %s %s by rebasing.\") :\n> +              _(\"Branch %s set up to track %s %s %s.\"),\n> +              local, location[!origin], type[remote_is_branch], remote);\n\nThe GSoC microproject talks about the _() function used for\ninternationalization and the limitations it places on string\ncomposition. This change assumes, probably incorrectly, that strings\n\"local\", \"remote\", \"branch\" and \"ref\" do not need to be localized.\nEven allowing internationalization of them (via N_() in the location[]\nand type[] tables) might not be sufficient since grammatical rules\ndiffer from language to language.\n\n>         }\n>  }\n>\n> --\n> 1.7.9.5\n"},{"id":"236466","messageId":"1394488063227-7605372.post@n2.nabble.com","threadId":"16566","inReplyTo":"CAPig+cS3H_8gZOuyro_Vt0ay3xphvsB92zFgwFfCdgKsXjU0Gg@mail.gmail.com","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Tamer TAS","fromEmail":"tamertas@outlook.com","sentAt":"2014-03-10T21:47:43Z","receivedAt":"2014-03-10T21:47:43Z","isPatch":true,"sender":{"key":"tamertas@outlook.com","avatar":"https://gravatar.com/avatar/618df903c9a2fa12e76148ca7a106e3236594de6002a190c70e1ace5c30e0e40?d=mp&s=160"},"body":"Eric Sunshine wrote\n> Even allowing internationalization of them (via N_() in the location[]\n> and type[] tables) might not be sufficient since grammatical rules\n> differ from language to language.\n\nI didn't fully understand what you meant by that. Since they were being\ninternationalized before using _() in the if-else bodies, wouldn't it\nproduce\nthe same output if I were to use the same method for location[] and type[]\ntables?\n\n\n\n\n--\nView this message in context: http://git.661346.n2.nabble.com/PATCH-GSOC2014-changed-logical-chain-in-branch-c-to-lookup-tables-tp7605343p7605372.html\nSent from the git mailing list archive at Nabble.com.\n"},{"id":"236468","messageId":"CAPig+cTOZrQY1azPCmzdrfut0S_-8ZDoob3cEqT7Z1kDpj+PQw@mail.gmail.com","threadId":"16566","inReplyTo":"1394488063227-7605372.post@n2.nabble.com","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-10T22:39:05Z","receivedAt":"2014-03-10T22:39:05Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Mar 10, 2014 at 5:47 PM, Tamer TAS <tamertas@outlook.com> wrote:\n> Eric Sunshine wrote\n>> Even allowing internationalization of them (via N_() in the location[]\n>> and type[] tables) might not be sufficient since grammatical rules\n>> differ from language to language.\n>\n> I didn't fully understand what you meant by that. Since they were being\n> internationalized before using _() in the if-else bodies, wouldn't it\n> produce\n> the same output if I were to use the same method for location[] and type[]\n> tables?\n\nIn the original code, the person translating the text has the full context:\n\n    \"Branch %s set up to track remote ref %s.\"\n\nWith your revision, the translator has to translate standalone words\n\"local\", \"remote\", \"branch\", \"ref\" without context about how they are\nused. He then has to translate:\n\n    \"Branch %s set up to track %s %s %s.\"\n\nwithout context of the words being inserted, which, depending upon\ngrammatical rules of the language can result in a different (and\nperhaps worse) translation than the original full-context translation.\n\nSection 4.3 of the GNU gettext manual [1] explains the issues in more\ndetail. I urge you to read it. The upshot is that translators fare\nbest when handed full sentences.\n\nNote also that your change effectively reverts d53a35032a67 [2], which\ndid away with the sort of string composition used in your patch.\n\n[1]: http://www.gnu.org/software/gettext/manual/gettext.html#Preparing-Strings\n[2]: https://github.com/git/git/commit/d53a35032a67fde5b59c8a6a66e0466837cbaf1e\n"},{"id":"236496","messageId":"1394537604079-7605407.post@n2.nabble.com","threadId":"16566","inReplyTo":"CAPig+cTOZrQY1azPCmzdrfut0S_-8ZDoob3cEqT7Z1kDpj+PQw@mail.gmail.com","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Tamer TAS","fromEmail":"tamertas@outlook.com","sentAt":"2014-03-11T11:33:24Z","receivedAt":"2014-03-11T11:33:24Z","isPatch":true,"sender":{"key":"tamertas@outlook.com","avatar":"https://gravatar.com/avatar/618df903c9a2fa12e76148ca7a106e3236594de6002a190c70e1ace5c30e0e40?d=mp&s=160"},"body":"Eric Sunshine wrote\n> On Mon, Mar 10, 2014 at 5:47 PM, Tamer TAS &lt;\n\n> tamertas@\n\n> &gt; wrote:\n> \n> Section 4.3 of the GNU gettext manual [1] explains the issues in more\n> detail. I urge you to read it. The upshot is that translators fare\n> best when handed full sentences.\n> \n> Note also that your change effectively reverts d53a35032a67 [2], which\n> did away with the sort of string composition used in your patch.\n\nEric thank you for your constructive feedbacks.\nI read the section 4.3 of GNU gettext manual and also checked the commit you\nmentioned.\nIt seems like that my previous changes were not internationalization\ncompatible.\nIn order for a table-driven change to be compatible, the sentences has to be\nmeaningful and not tokenized.\nI made the following change to the branch.c in order for the function to be\nboth table-driven and\ninternationalization compatible. Let me know if there are any oversights on\nmy part.\n\nSigned-off-by: TamerTas <tamertas@outlook.com>\n---\n branch.c |   39 ++++++++++++++++-----------------------\n 1 file changed, 16 insertions(+), 23 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 723a36b..4c04638 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -50,10 +50,25 @@ static int should_setup_rebase(const char *origin)\n void install_branch_config(int flag, const char *local, const char *origin,\nconst char *remote)\n {\n \tconst char *shortname = remote + 11;\n+    const char *setup_messages[] = {\n+\t\t_(\"Branch %s set up to track remote branch %s from %s.\"),\n+\t\t_(\"Branch %s set up to track local branch %s.\"),\n+\t\t_(\"Branch %s set up to track remote ref %s.\"),\n+\t\t_(\"Branch %s set up to track local ref %s.\"),\n+\t\t_(\"Branch %s set up to track remote branch %s from %s by rebasing.\"),\n+\t\t_(\"Branch %s set up to track local branch %s by rebasing.\"),\n+\t\t_(\"Branch %s set up to track remote ref %s by rebasing.\"),\n+\t\t_(\"Branch %s set up to track local ref %s by rebasing.\")\n+\t}; \n+\n \tint remote_is_branch = starts_with(remote, \"refs/heads/\");\n \tstruct strbuf key = STRBUF_INIT;\n \tint rebasing = should_setup_rebase(origin);\n \n+    int msg_index = (!!origin           >> 0) +\n+\t\t\t\t\t(!!remote_is_branch >> 1) +\n+\t\t\t\t\t(!!rebasing         >> 2);\n+   \n \tif (remote_is_branch\n \t    && !strcmp(local, shortname)\n \t    && !origin) {\n@@ -77,29 +92,7 @@ void install_branch_config(int flag, const char *local,\nconst char *origin, cons\n \tstrbuf_release(&key);\n \n \tif (flag & BRANCH_CONFIG_VERBOSE) {\n-\t\tif (remote_is_branch && origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s by rebasing.\")\n:\n-\t\t\t\t  _(\"Branch %s set up to track remote branch %s from %s.\"),\n-\t\t\t\t  local, shortname, origin);\n-\t\telse if (remote_is_branch && !origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track local branch %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track local branch %s.\"),\n-\t\t\t\t  local, shortname);\n-\t\telse if (!remote_is_branch && origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track remote ref %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track remote ref %s.\"),\n-\t\t\t\t  local, remote);\n-\t\telse if (!remote_is_branch && !origin)\n-\t\t\tprintf_ln(rebasing ?\n-\t\t\t\t  _(\"Branch %s set up to track local ref %s by rebasing.\") :\n-\t\t\t\t  _(\"Branch %s set up to track local ref %s.\"),\n-\t\t\t\t  local, remote);\n-\t\telse\n-\t\t\tdie(\"BUG: impossible combination of %d and %p\",\n-\t\t\t    remote_is_branch, origin);\n+\t\tprintf_ln(setup_messages[msg_index], local, remote);\n \t}\n }\n \n-- \n1.7.9.5\n\n\n\n--\nView this message in context: http://git.661346.n2.nabble.com/PATCH-GSOC2014-changed-logical-chain-in-branch-c-to-lookup-tables-tp7605343p7605407.html\nSent from the git mailing list archive at Nabble.com.\n"},{"id":"236533","messageId":"CAPig+cSWw+3v8oCV2=kG_ASLPvg9J4S_FNg5UUFKXrFf2Lcb8w@mail.gmail.com","threadId":"16566","inReplyTo":"1394537604079-7605407.post@n2.nabble.com","subject":"Re: [PATCH][GSOC2014] changed logical chain in branch.c to lookup tables","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2014-03-11T20:32:52Z","receivedAt":"2014-03-11T20:32:52Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"Thanks for the resubmission. Comments below.\n\nOn Tue, Mar 11, 2014 at 7:33 AM, Tamer TAS <tamertas@outlook.com> wrote:\n> Subject: changed logical chain in branch.c to lookup tables\n\nUse imperative tone: \"change\" rather than \"changed\"\n\nPrefix the message with the part of the project you are touching. So,\nfor instance, you might write:\n\n    Subject: install_branch_config: change logical chain to lookup table\n\n> Eric Sunshine wrote\n>> On Mon, Mar 10, 2014 at 5:47 PM, Tamer TAS &lt;\n>\n>> tamertas@\n>\n>> &gt; wrote:\n>>\n>> Section 4.3 of the GNU gettext manual [1] explains the issues in more\n>> detail. I urge you to read it. The upshot is that translators fare\n>> best when handed full sentences.\n>>\n>> Note also that your change effectively reverts d53a35032a67 [2], which\n>> did away with the sort of string composition used in your patch.\n>\n> Eric thank you for your constructive feedbacks.\n> I read the section 4.3 of GNU gettext manual and also checked the commit you\n> mentioned.\n> It seems like that my previous changes were not internationalization\n> compatible.\n> In order for a table-driven change to be compatible, the sentences has to be\n> meaningful and not tokenized.\n> I made the following change to the branch.c in order for the function to be\n> both table-driven and\n> internationalization compatible. Let me know if there are any oversights on\n> my part.\n\nThis commentary is relevant to the ongoing email thread but not\nsuitable for the permanent commit message. Place it below the \"---\"\nline just under your sign-off.\n\n> Signed-off-by: TamerTas <tamertas@outlook.com>\n> ---\n\nIt's considerate to reviewers to provide a link to the previous\nattempt, like this [1]. This area below the \"---\" line is the\nappropriate place to do so. Likewise, explain how this version differs\nfrom the previous one.\n\n[1]: http://thread.gmane.org/gmane.comp.version-control.git/243793\n\n>  branch.c |   39 ++++++++++++++++-----------------------\n>  1 file changed, 16 insertions(+), 23 deletions(-)\n>\n> diff --git a/branch.c b/branch.c\n> index 723a36b..4c04638 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -50,10 +50,25 @@ static int should_setup_rebase(const char *origin)\n>  void install_branch_config(int flag, const char *local, const char *origin,\n> const char *remote)\n\nYour patch is whitespace damaged, probably due to being pasted into\nyour email. \"git send-email\" avoids such problems.\n\nIndentation is also broken. Use tabs for indentation, and set tab\nwidth to 8 in your editor, as explained in\nDocumentation/CodingGuidelines.\n\n>  {\n>         const char *shortname = remote + 11;\n> +    const char *setup_messages[] = {\n> +               _(\"Branch %s set up to track remote branch %s from %s.\"),\n> +               _(\"Branch %s set up to track local branch %s.\"),\n> +               _(\"Branch %s set up to track remote ref %s.\"),\n> +               _(\"Branch %s set up to track local ref %s.\"),\n> +               _(\"Branch %s set up to track remote branch %s from %s by rebasing.\"),\n> +               _(\"Branch %s set up to track local branch %s by rebasing.\"),\n> +               _(\"Branch %s set up to track remote ref %s by rebasing.\"),\n> +               _(\"Branch %s set up to track local ref %s by rebasing.\")\n> +       };\n\nOn this project, arrays are usually (though not consistently) named in\nsingular form (for instance setup_message[]) so that a reference to a\nsingle item, such as setup_message[42], reads more grammatically\ncorrect.\n\nIt's not correct to use _() inside the initializer list. Instead use\nN_(). Read section 4.7 of the GNU gettext manual [2] for an\nexplanation. You will still need to use _() at the point where you\nextract the message from the array.\n\n[2]: http://www.gnu.org/software/gettext/manual/gettext.html#Special-cases\n\n>         int remote_is_branch = starts_with(remote, \"refs/heads/\");\n>         struct strbuf key = STRBUF_INIT;\n>         int rebasing = should_setup_rebase(origin);\n>\n> +    int msg_index = (!!origin           >> 0) +\n> +                                       (!!remote_is_branch >> 1) +\n> +                                       (!!rebasing         >> 2);\n\nAre you sure this is correct? As I read it, msg_index will only ever\nhave a value of 0 or 1, which is unlikely what you intended.\n\n>         if (remote_is_branch\n>             && !strcmp(local, shortname)\n>             && !origin) {\n> @@ -77,29 +92,7 @@ void install_branch_config(int flag, const char *local,\n> const char *origin, cons\n>         strbuf_release(&key);\n>\n>         if (flag & BRANCH_CONFIG_VERBOSE) {\n> -               if (remote_is_branch && origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track remote branch %s from %s by rebasing.\")\n> :\n> -                                 _(\"Branch %s set up to track remote branch %s from %s.\"),\n> -                                 local, shortname, origin);\n> -               else if (remote_is_branch && !origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track local branch %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track local branch %s.\"),\n> -                                 local, shortname);\n> -               else if (!remote_is_branch && origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track remote ref %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track remote ref %s.\"),\n> -                                 local, remote);\n> -               else if (!remote_is_branch && !origin)\n> -                       printf_ln(rebasing ?\n> -                                 _(\"Branch %s set up to track local ref %s by rebasing.\") :\n> -                                 _(\"Branch %s set up to track local ref %s.\"),\n> -                                 local, remote);\n> -               else\n> -                       die(\"BUG: impossible combination of %d and %p\",\n> -                           remote_is_branch, origin);\n> +               printf_ln(setup_messages[msg_index], local, remote);\n\nThis can't be correct.\n\nIn the original code, all of the printf_ln() invocations received\n'local' as the first argument, but only two of them received 'remote',\nyet you are passing 'remote' on every invocation.\n\nWorse, the first and fifths items in your setup_messages[] table\nexpect three arguments (they have three %s's), but you're passing only\ntwo, which will surely lead to a crash.\n\nAlso, wrap _() around setup_messages[msg_index], as noted above.\n\n>         }\n>  }\n>\n> --\n> 1.7.9.5\n"},{"id":"236983","messageId":"1395145875-5072-1-git-send-email-movchan.pv@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH][GSOC2014] add: Rewrite run_add_interactive to use struct argv_array","fromName":"Movchan Pavel","fromEmail":"movchan.pv@gmail.com","sentAt":"2014-03-18T12:31:14Z","receivedAt":"2014-03-18T12:31:14Z","isPatch":true,"sender":{"key":"movchan.pv@gmail.com","avatar":null},"body":"Origin code are code with own realisation argv array editing.\nIt was changed, and code modified for using unified argv-array\nrealisation from argv-array.h.\nCommit for Google Summer of Code 2014\n\nSigned-off-by: Movchan Pavel <movchan.pv@gmail.com>\n---\n builtin/add.c |   21 ++++++++++-----------\n 1 file changed, 10 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 4b045ba..258b491 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -15,6 +15,7 @@\n #include \"diffcore.h\"\n #include \"revision.h\"\n #include \"bulk-checkin.h\"\n+#include \"argv-array.h\"\n \n static const char * const builtin_add_usage[] = {\n \tN_(\"git add [options] [--] <pathspec>...\"),\n@@ -141,23 +142,21 @@ static void refresh(int verbose, const struct pathspec *pathspec)\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst struct pathspec *pathspec)\n {\n-\tint status, ac, i;\n-\tconst char **args;\n+\tint status, i;\n+\tstruct argv_array *argv = ARGV_ARRAY_INIT;\n \n-\targs = xcalloc(sizeof(const char *), (pathspec->nr + 6));\n-\tac = 0;\n-\targs[ac++] = \"add--interactive\";\n+\targv_array_push(argv, \"add--interactive\");\n \tif (patch_mode)\n-\t\targs[ac++] = patch_mode;\n+\t\targv_array_push(argv, patch_mode);\n \tif (revision)\n-\t\targs[ac++] = revision;\n-\targs[ac++] = \"--\";\n+\t\targv_array_push(argv, revision);\n+\targv_array_push(argv, \"--\");\n \tfor (i = 0; i < pathspec->nr; i++)\n \t\t/* pass original pathspec, to be re-parsed */\n-\t\targs[ac++] = pathspec->items[i].original;\n+\t\targv_array_push(argv, pathspec->items[i].original);\n \n-\tstatus = run_command_v_opt(args, RUN_GIT_CMD);\n-\tfree(args);\n+\tstatus = run_command_v_opt(argv->argv, RUN_GIT_CMD);\n+\targv_array_clear(argv);\n \treturn status;\n }\n \n-- \n1.7.10.4\n"},{"id":"237004","messageId":"xmqq1txz8f0w.fsf@gitster.dls.corp.google.com","threadId":"16566","inReplyTo":"1395145875-5072-1-git-send-email-movchan.pv@gmail.com","subject":"Re: [PATCH][GSOC2014] add: Rewrite run_add_interactive to use struct argv_array","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-03-18T19:47:59Z","receivedAt":"2014-03-18T19:47:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Movchan Pavel <movchan.pv@gmail.com> writes:\n\n> Origin code are code with own realisation argv array editing.\n> It was changed, and code modified for using unified argv-array\n> realisation from argv-array.h.\n> Commit for Google Summer of Code 2014\n>\n> Signed-off-by: Movchan Pavel <movchan.pv@gmail.com>\n> ---\n\nThanks.  \"Commit for ...\" is not something we would want to see in\n\"git log\" output, though.\n\n>  builtin/add.c |   21 ++++++++++-----------\n>  1 file changed, 10 insertions(+), 11 deletions(-)\n>\n> diff --git a/builtin/add.c b/builtin/add.c\n> index 4b045ba..258b491 100644\n> --- a/builtin/add.c\n> +++ b/builtin/add.c\n> @@ -15,6 +15,7 @@\n>  #include \"diffcore.h\"\n>  #include \"revision.h\"\n>  #include \"bulk-checkin.h\"\n> +#include \"argv-array.h\"\n>  \n>  static const char * const builtin_add_usage[] = {\n>  \tN_(\"git add [options] [--] <pathspec>...\"),\n> @@ -141,23 +142,21 @@ static void refresh(int verbose, const struct pathspec *pathspec)\n>  int run_add_interactive(const char *revision, const char *patch_mode,\n>  \t\t\tconst struct pathspec *pathspec)\n>  {\n> -\tint status, ac, i;\n> -\tconst char **args;\n> +\tint status, i;\n> +\tstruct argv_array *argv = ARGV_ARRAY_INIT;\n\nWhere does that pointer point at and who allocated the piece of\nmemory used by it?\n\nSee http://thread.gmane.org/gmane.comp.version-control.git/244151\nfor an example solution.\n\n>  \n> -\targs = xcalloc(sizeof(const char *), (pathspec->nr + 6));\n> -\tac = 0;\n> -\targs[ac++] = \"add--interactive\";\n> +\targv_array_push(argv, \"add--interactive\");\n>  \tif (patch_mode)\n> -\t\targs[ac++] = patch_mode;\n> +\t\targv_array_push(argv, patch_mode);\n>  \tif (revision)\n> -\t\targs[ac++] = revision;\n> -\targs[ac++] = \"--\";\n> +\t\targv_array_push(argv, revision);\n> +\targv_array_push(argv, \"--\");\n>  \tfor (i = 0; i < pathspec->nr; i++)\n>  \t\t/* pass original pathspec, to be re-parsed */\n> -\t\targs[ac++] = pathspec->items[i].original;\n> +\t\targv_array_push(argv, pathspec->items[i].original);\n>  \n> -\tstatus = run_command_v_opt(args, RUN_GIT_CMD);\n> -\tfree(args);\n> +\tstatus = run_command_v_opt(argv->argv, RUN_GIT_CMD);\n> +\targv_array_clear(argv);\n>  \treturn status;\n>  }\n"},{"id":"288830","messageId":"1465494374-10417-1-git-send-email-Lars.Vogel@vogella.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] Use \"working tree\" instead of \"working directory\" for git status","fromName":"Lars Vogel","fromEmail":"lars.vogel@gmail.com","sentAt":"2016-06-09T17:46:14Z","receivedAt":"2016-06-16T02:19:50Z","isPatch":true,"sender":{"key":"lars.vogel@gmail.com","avatar":"https://avatars.githubusercontent.com/u/139910?v=4"},"body":"Working directory can be easily confused with the current directory.\nIn one of my patches I already updated the usage of working directory\nwith working tree for the man page but I noticed that git status also\nuses this incorrect term.\n---\n po/bg.po    | 2 +-\n po/ca.po    | 2 +-\n po/de.po    | 2 +-\n po/fr.po    | 2 +-\n po/git.pot  | 2 +-\n po/ko.po    | 2 +-\n po/ru.po    | 2 +-\n po/sv.po    | 2 +-\n po/vi.po    | 2 +-\n po/zh_CN.po | 2 +-\n wt-status.c | 2 +-\n 11 files changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/po/bg.po b/po/bg.po\nindex ac6f103..9390c81 100644\n--- a/po/bg.po\n+++ b/po/bg.po\n@@ -2295,7 +2295,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"липсват каквито и да е промени, работното дърво е чисто\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/ca.po b/po/ca.po\nindex 46000d7..f54f137 100644\n--- a/po/ca.po\n+++ b/po/ca.po\n@@ -2318,7 +2318,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"no hi ha res a cometre, directori de treball net\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/de.po b/po/de.po\nindex 0eadf34..a14fe92 100644\n--- a/po/de.po\n+++ b/po/de.po\n@@ -2356,7 +2356,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"nichts zu committen, Arbeitsverzeichnis unverändert\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/fr.po b/po/fr.po\nindex 55ca387..627315c 100644\n--- a/po/fr.po\n+++ b/po/fr.po\n@@ -2394,7 +2394,7 @@ msgstr \"rien à valider (utilisez -u pour afficher les fichiers non suivis)\\n\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"rien à valider, la copie de travail est propre\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/git.pot b/po/git.pot\nindex 72ef798..2957091 100644\n--- a/po/git.pot\n+++ b/po/git.pot\n@@ -2192,7 +2192,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"\"\n \n #: wt-status.c:1642\ndiff --git a/po/ko.po b/po/ko.po\nindex 3ff3b9b..9520e04 100644\n--- a/po/ko.po\n+++ b/po/ko.po\n@@ -2313,7 +2313,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"커밋할 사항 없음, 작업 폴더 깨끗함\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/ru.po b/po/ru.po\nindex c0a838b..6fe8c09 100644\n--- a/po/ru.po\n+++ b/po/ru.po\n@@ -2233,7 +2233,7 @@ msgstr \"нечего коммитить (используйте опцию «-u\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"нечего коммитить, нет изменений в рабочем каталоге\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/sv.po b/po/sv.po\nindex 32bcaba..801a816 100644\n--- a/po/sv.po\n+++ b/po/sv.po\n@@ -2289,7 +2289,7 @@ msgstr \"inget att checka in (använd -u för att visa ospårade filer)\\n\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"inget att checka in, arbetskatalogen ren\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/vi.po b/po/vi.po\nindex edd8e29..18fbdef 100644\n--- a/po/vi.po\n+++ b/po/vi.po\n@@ -2306,7 +2306,7 @@ msgstr \"\"\n \n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"không có gì để chuyển giao, thư mục làm việc sạch sẽ\\n\"\n \n #: wt-status.c:1642\ndiff --git a/po/zh_CN.po b/po/zh_CN.po\nindex a6b06f9..b76f2c0 100644\n--- a/po/zh_CN.po\n+++ b/po/zh_CN.po\n@@ -2421,7 +2421,7 @@ msgstr \"无文件要提交（使用 -u 显示未跟踪的文件）\\n\"\n #  译者：中文字符串拼接，可删除前导空格\n #: wt-status.c:1535\n #, c-format\n-msgid \"nothing to commit, working directory clean\\n\"\n+msgid \"nothing to commit, working tree clean\\n\"\n msgstr \"无文件要提交，干净的工作区\\n\"\n \n #  译者：注意保持句尾空格\ndiff --git a/wt-status.c b/wt-status.c\nindex 4f27bd6..4ce4e35 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1554,7 +1554,7 @@ void wt_status_print(struct wt_status *s)\n \t\t\telse\n \t\t\t\tprintf(_(\"nothing to commit\\n\"));\n \t\t} else\n-\t\t\tprintf(_(\"nothing to commit, working directory clean\\n\"));\n+\t\t\tprintf(_(\"nothing to commit, working tree clean\\n\"));\n \t}\n }\n \n-- \n2.8.1\n"},{"id":"288831","messageId":"CAPig+cR6j=L5P_i=D_6Q8Dfmu0jew9QDnRQTXjCJYQ9AUszz1g@mail.gmail.com","threadId":"16566","inReplyTo":"1465494374-10417-1-git-send-email-Lars.Vogel@vogella.com","subject":"Re: [PATCH] Use \"working tree\" instead of \"working directory\" for git status","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-09T17:55:37Z","receivedAt":"2016-06-16T02:19:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jun 9, 2016 at 1:46 PM, Lars Vogel <lars.vogel@gmail.com> wrote:\n> Working directory can be easily confused with the current directory.\n> In one of my patches I already updated the usage of working directory\n> with working tree for the man page but I noticed that git status also\n> uses this incorrect term.\n\nMissing sign-off.\n\n> ---\n>  po/bg.po    | 2 +-\n>  po/ca.po    | 2 +-\n>  po/de.po    | 2 +-\n>  po/fr.po    | 2 +-\n>  po/git.pot  | 2 +-\n>  po/ko.po    | 2 +-\n>  po/ru.po    | 2 +-\n>  po/sv.po    | 2 +-\n>  po/vi.po    | 2 +-\n>  po/zh_CN.po | 2 +-\n>  wt-status.c | 2 +-\n\nDon't bother updating the .po files; that's the job of the\ntranslators. Your patch should touch only wt-status.c.\n\n>  11 files changed, 11 insertions(+), 11 deletions(-)\n"},{"id":"288834","messageId":"CACA4a_Gzw5OXRc-ZaP78KOWW7nWzyN7KfWf_Gq_7vr+1HC-g0w@mail.gmail.com","threadId":"16566","inReplyTo":"CAPig+cR6j=L5P_i=D_6Q8Dfmu0jew9QDnRQTXjCJYQ9AUszz1g@mail.gmail.com","subject":"Re: [PATCH] Use \"working tree\" instead of \"working directory\" for git status","fromName":"Lars Vogel","fromEmail":"lars.vogel@vogella.com","sentAt":"2016-06-09T18:21:03Z","receivedAt":"2016-06-16T02:19:50Z","isPatch":true,"sender":{"key":"lars.vogel@vogella.com","avatar":"https://gravatar.com/avatar/5e501b3827120cf228798a08717c5eed466d50fa22b12a8ce82ea15ce11cc38d?d=mp&s=160"},"body":"Thanks updated patch on its way.\n\nOn Thu, Jun 9, 2016 at 7:55 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Thu, Jun 9, 2016 at 1:46 PM, Lars Vogel <lars.vogel@gmail.com> wrote:\n>> Working directory can be easily confused with the current directory.\n>> In one of my patches I already updated the usage of working directory\n>> with working tree for the man page but I noticed that git status also\n>> uses this incorrect term.\n>\n> Missing sign-off.\n>\n>> ---\n>>  po/bg.po    | 2 +-\n>>  po/ca.po    | 2 +-\n>>  po/de.po    | 2 +-\n>>  po/fr.po    | 2 +-\n>>  po/git.pot  | 2 +-\n>>  po/ko.po    | 2 +-\n>>  po/ru.po    | 2 +-\n>>  po/sv.po    | 2 +-\n>>  po/vi.po    | 2 +-\n>>  po/zh_CN.po | 2 +-\n>>  wt-status.c | 2 +-\n>\n> Don't bother updating the .po files; that's the job of the\n> translators. Your patch should touch only wt-status.c.\n>\n>>  11 files changed, 11 insertions(+), 11 deletions(-)\n\n\n\n-- \nEclipse Platform UI and e4 project co-lead\nCEO vogella GmbH\n\nHaindaalwisch 17a, 22395 Hamburg\nAmtsgericht Hamburg: HRB 127058\nGeschäftsführer: Lars Vogel, Jennifer Nerlich de Vogel\nUSt-IdNr.: DE284122352\nFax (040) 5247 6322, Email: lars.vogel@vogella.com, Web: http://www.vogella.com\n"},{"id":"330189","messageId":"20171011180350.99888-1-takahito.ogawa@datagrid.co.jp","threadId":"16566","inReplyTo":"y","subject":"[PATCH 1/1] git-stash.txt: correct \"git stash\" behavior with no arguments","fromName":"Takahito Ogawa","fromEmail":"aiueogawa217@gmail.com","sentAt":"2017-10-11T18:03:50Z","receivedAt":"2017-10-11T18:04:17Z","isPatch":true,"sender":{"key":"aiueogawa217@gmail.com","avatar":null},"body":"\"git stash\" behavior without any arguments was changed in\n1ada5020b (\"stash: use stash_push for no verb form\", 2017-02-28).\nThis is equivalent to \"git stash push\" but document says\n\"git stash save\".\n\nCorrect it.\n\nSigned-off-by: Takahito Ogawa <takahito.ogawa@datagrid.co.jp>\n---\n Documentation/git-stash.txt | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 00f95fee1..63642c145 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -33,7 +33,7 @@ and reverts the working directory to match the `HEAD` commit.\n The modifications stashed away by this command can be listed with\n `git stash list`, inspected with `git stash show`, and restored\n (potentially on top of a different commit) with `git stash apply`.\n-Calling `git stash` without any arguments is equivalent to `git stash save`.\n+Calling `git stash` without any arguments is equivalent to `git stash push`.\n A stash is by default listed as \"WIP on 'branchname' ...\", but\n you can give a more descriptive message on the command line when\n you create one.\n-- \n2.13.1\n\n"},{"id":"330219","messageId":"xmqqr2u92nkr.fsf@gitster.mtv.corp.google.com","threadId":"16566","inReplyTo":"20171011180350.99888-1-takahito.ogawa@datagrid.co.jp","subject":"Re: [PATCH 1/1] git-stash.txt: correct \"git stash\" behavior with no arguments","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T00:53:56Z","receivedAt":"2017-10-12T00:54:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Takahito Ogawa <aiueogawa217@gmail.com> writes:\n\n> @@ -33,7 +33,7 @@ and reverts the working directory to match the `HEAD` commit.\n>  The modifications stashed away by this command can be listed with\n>  `git stash list`, inspected with `git stash show`, and restored\n>  (potentially on top of a different commit) with `git stash apply`.\n> -Calling `git stash` without any arguments is equivalent to `git stash save`.\n> +Calling `git stash` without any arguments is equivalent to `git stash push`.\n\nHmph.  Is there any difference between\n\n\tgit stash save\n\tgit stash push\n\nwithout any other argument?  Aren't they equivalent to\n\n\tgit stash\n\nwithout any argument, which is what this sentence explains?\n\n"},{"id":"330233","messageId":"CAC2Jkr+zFy9q4Xnzxcqj6rq9+TOs2sO9J5Wx9ccd3oiY4N20uA@mail.gmail.com","threadId":"16566","inReplyTo":"CAC2Jkr+ZDUxd9JcKvVwZqCHVW+Bpp2CbdyehwgKV_Ru-Uqxo8A@mail.gmail.com","subject":"Re: [PATCH 1/1] git-stash.txt: correct \"git stash\" behavior with no arguments","fromName":"小川恭史","fromEmail":"aiueogawa217@gmail.com","sentAt":"2017-10-12T03:52:36Z","receivedAt":"2017-10-12T03:52:42Z","isPatch":true,"sender":{"key":"aiueogawa217@gmail.com","avatar":null},"body":"As you point,\n\n    git stash\n\nwithout any argument is equivalent to both of\n\n    git stash save\n    git stash push\n\n. The original sentence is correct.\n\n2017-10-12 12:31 GMT+09:00 小川恭史 <aiueogawa217@gmail.com>:\n> As you point,\n>\n>     git stash\n>\n> without any argument is equivalent to both of\n> git stash save and\n>\n>\n> 2017-10-12 9:53 GMT+09:00 Junio C Hamano <gitster@pobox.com>:\n>> Takahito Ogawa <aiueogawa217@gmail.com> writes:\n>>\n>>> @@ -33,7 +33,7 @@ and reverts the working directory to match the `HEAD`\n>>> commit.\n>>>  The modifications stashed away by this command can be listed with\n>>>  `git stash list`, inspected with `git stash show`, and restored\n>>>  (potentially on top of a different commit) with `git stash apply`.\n>>> -Calling `git stash` without any arguments is equivalent to `git stash\n>>> save`.\n>>> +Calling `git stash` without any arguments is equivalent to `git stash\n>>> push`.\n>>\n>> Hmph.  Is there any difference between\n>>\n>>         git stash save\n>>         git stash push\n>>\n>> without any other argument?  Aren't they equivalent to\n>>\n>>         git stash\n>>\n>> without any argument, which is what this sentence explains?\n>>\n>\n"},{"id":"330234","messageId":"CAC2JkrL01G-j+Dxbog9Lq95OKsLpVv6OjV=qg4h0-nwneZ+Oog@mail.gmail.com","threadId":"16566","inReplyTo":"CAC2Jkr+zFy9q4Xnzxcqj6rq9+TOs2sO9J5Wx9ccd3oiY4N20uA@mail.gmail.com","subject":"Re: [PATCH 1/1] git-stash.txt: correct \"git stash\" behavior with no arguments","fromName":"小川恭史","fromEmail":"aiueogawa217@gmail.com","sentAt":"2017-10-12T03:54:32Z","receivedAt":"2017-10-12T03:54:37Z","isPatch":true,"sender":{"key":"aiueogawa217@gmail.com","avatar":null},"body":"As you point,\n\n    git stash\n\nwithout any argument is equivalent to both of\n\n    git stash save\n    git stash push\n\n. The original sentence is correct.\n"},{"id":"330257","messageId":"xmqqsheozo3o.fsf@gitster.mtv.corp.google.com","threadId":"16566","inReplyTo":"CAC2JkrL01G-j+Dxbog9Lq95OKsLpVv6OjV=qg4h0-nwneZ+Oog@mail.gmail.com","subject":"Re: [PATCH 1/1] git-stash.txt: correct \"git stash\" behavior with no arguments","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-10-12T09:56:11Z","receivedAt":"2017-10-12T09:56:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"小川恭史 <aiueogawa217@gmail.com> writes:\n\n> As you point,\n>\n>     git stash\n>\n> without any argument is equivalent to both of\n>\n>     git stash save\n>     git stash push\n>\n> . The original sentence is correct.\n\nOK.  \n\nNote that I was merely reacting to \"Correct it.\" in your\njustification for the change, which made it sound like 'save' was\nincorrect.\n\nAs a part of a move to deprecate 'save' and nudge users towards\n'push', the change does make sense.  I just wanted to make sure the\nchange and its motivation are presented with honesty ;-).\n\n"},{"id":"367012","messageId":"20190117192909.26064-1-brandon1024.br@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] Allow usage of --gpg-sign flag in commit-tree builtin.","fromName":"Brandon Richardson","fromEmail":"brandon1024.br@gmail.com","sentAt":"2019-01-17T19:29:09Z","receivedAt":"2019-01-17T19:29:21Z","isPatch":true,"sender":{"key":"brandon1024.br@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22732449?v=4"},"body":"Signed-off-by: Brandon Richardson <brandon1024.br@gmail.com>\n---\nHi,\n\nThis is my first contribution, so please bear with me. All feedback\nis appreciated.\n\nRan into this issue while writing a signed commit object manually.\nHere are the steps I followed to replicate the issue:\n\nmkdir test && cd test\ngit init\necho 'test' > test.txt\ngit hash-object -w test.txt\ngit update-index --add --cacheinfo 100644 <blob hash> test.txt\ngit write-tree\ngit commit-tree --gpg-sign -m 'test commit msg' <tree obj hash>\n\nThanks,\nBrandon\n\n builtin/commit-tree.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/commit-tree.c b/builtin/commit-tree.c\nindex 9ec36a82b..9a06594f6 100644\n--- a/builtin/commit-tree.c\n+++ b/builtin/commit-tree.c\n@@ -66,7 +66,9 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (skip_prefix(arg, \"-S\", &sign_commit))\n+\t\tif (skip_prefix(arg, \"-S\", &sign_commit) ||\n+\t\t\tskip_prefix(arg, \"--gpg-sign=\", &sign_commit) ||\n+\t\t\tskip_prefix(arg, \"--gpg-sign\", &sign_commit))\n \t\t\tcontinue;\n \n \t\tif (!strcmp(arg, \"--no-gpg-sign\")) {\n-- \n2.20.1\n\n"},{"id":"367022","messageId":"CAN0heSo_XVK67urjgCVGh23UvzhrquojBjNnp_P2NgS7eCUd1Q@mail.gmail.com","threadId":"16566","inReplyTo":"20190117192909.26064-1-brandon1024.br@gmail.com","subject":"Re: [PATCH] Allow usage of --gpg-sign flag in commit-tree builtin.","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-01-17T20:24:43Z","receivedAt":"2019-01-17T20:24:59Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"Hi Brandon,\n\nWelcome to the list! :-)\n\nOn Thu, 17 Jan 2019 at 20:30, Brandon Richardson\n<brandon1024.br@gmail.com> wrote:\n\n> Subject: [PATCH] Allow usage of --gpg-sign flag in commit-tree builtin.\n\nGood find!\n\nMaybe \"commit-tree: handle --gpg-sign\" which looks more like what we\nsee in `git log --oneline builtin/commit-tree.c`.\n\n> Signed-off-by: Brandon Richardson <brandon1024.br@gmail.com>\n\nIt could be worthwhile to note in the proposed commit message that this\noption is actually documented, since 55ca3f99ae (\"commit-tree: add and\ndocument --no-gpg-sign\", 2013-12-13), so it's clearly an omission that\nit's not implemented.\n\n> Here are the steps I followed to replicate the issue:\n>\n> mkdir test && cd test\n> git init\n> echo 'test' > test.txt\n> git hash-object -w test.txt\n> git update-index --add --cacheinfo 100644 <blob hash> test.txt\n> git write-tree\n> git commit-tree --gpg-sign -m 'test commit msg' <tree obj hash>\n\nHave you looked at turning this into a test in the t/ directory?\nGrepping a little suggests that t7510-signed-commit.sh might be a good\nspot. A test would make the patch more obviously correct, and would make\nsure that this doesn't regress in the future.\n\n> -               if (skip_prefix(arg, \"-S\", &sign_commit))\n> +               if (skip_prefix(arg, \"-S\", &sign_commit) ||\n> +                       skip_prefix(arg, \"--gpg-sign=\", &sign_commit) ||\n> +                       skip_prefix(arg, \"--gpg-sign\", &sign_commit))\n>                         continue;\n\nThis would match \"--gpg-signfoo\", which seems like a bug. The first two\n`skip_prefix()` calls look ok. Then maybe\n\n  if (!strcmp(arg, \"--gpg-sign\")) {\n          sign_commit = \"\";\n          continue;\n  }\n\nThat's entirely untested, though.\n\nThanks for looking into this, rather than just side-stepping it. ;-)\n\nMartin\n"},{"id":"367041","messageId":"20190118010918.43705-1-brandon1024.br@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[PATCH] commit-tree: add missing --gpg-sign flag","fromName":"Brandon Richardson","fromEmail":"brandon1024.br@gmail.com","sentAt":"2019-01-18T01:09:18Z","receivedAt":"2019-01-18T01:09:29Z","isPatch":true,"sender":{"key":"brandon1024.br@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22732449?v=4"},"body":"Add --gpg-sign option in commit-tree, which was documented, but not\nimplemented, in 55ca3f99ae.\n\nSigned-off-by: Brandon Richardson <brandon1024.br@gmail.com>\n---\n\nThanks Martin for the tips and suggestions!\n\n builtin/commit-tree.c    | 8 +++++++-\n t/t7510-signed-commit.sh | 4 +++-\n 2 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/commit-tree.c b/builtin/commit-tree.c\nindex 9ec36a82b..a51b2c8d7 100644\n--- a/builtin/commit-tree.c\n+++ b/builtin/commit-tree.c\n@@ -66,7 +66,13 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (skip_prefix(arg, \"-S\", &sign_commit))\n+\t\tif(!strcmp(arg, \"--gpg-sign\")) {\n+\t\t    skip_prefix(arg, \"--gpg-sign\", &sign_commit);\n+\t\t    continue;\n+\t\t}\n+\n+\t\tif (skip_prefix(arg, \"-S\", &sign_commit) ||\n+\t\t\tskip_prefix(arg, \"--gpg-sign=\", &sign_commit))\n \t\t\tcontinue;\n \n \t\tif (!strcmp(arg, \"--no-gpg-sign\")) {\ndiff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\nindex 86d3f93fa..efc136eaf 100755\n--- a/t/t7510-signed-commit.sh\n+++ b/t/t7510-signed-commit.sh\n@@ -51,7 +51,9 @@ test_expect_success GPG 'create signed commits' '\n \t# commit.gpgsign is still on but this must not be signed\n \tgit tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) &&\n \t# explicit -S of course must sign.\n-\tgit tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree})\n+\tgit tag tenth-signed $(echo 10 | git commit-tree -S HEAD^{tree})\n+\t# --gpg-sign must sign.\n+\tgit tag eleventh-signed $(echo 11 | git commit-tree --gpg-sign HEAD^{tree})\n '\n \n test_expect_success GPG 'verify and show signatures' '\n-- \n2.20.1\n\n"},{"id":"367052","messageId":"CAN0heSrE3mOnjyrzw76Gp6wBuq62wgM6jBV1u+vn3b6ySm0EDQ@mail.gmail.com","threadId":"16566","inReplyTo":"20190118010918.43705-1-brandon1024.br@gmail.com","subject":"Re: [PATCH] commit-tree: add missing --gpg-sign flag","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2019-01-18T07:11:16Z","receivedAt":"2019-01-18T07:11:32Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"Hi Brandon,\n\nOn Fri, 18 Jan 2019 at 02:12, Brandon Richardson\n<brandon1024.br@gmail.com> wrote:\n>\n> Add --gpg-sign option in commit-tree, which was documented, but not\n> implemented, in 55ca3f99ae.\n>\n> Signed-off-by: Brandon Richardson <brandon1024.br@gmail.com>\n> ---\n\nThank you for an updated version!\n\n>  builtin/commit-tree.c    | 8 +++++++-\n>  t/t7510-signed-commit.sh | 4 +++-\n\nAh, a test, nice. :-)\n\n> -               if (skip_prefix(arg, \"-S\", &sign_commit))\n> +               if(!strcmp(arg, \"--gpg-sign\")) {\n> +                   skip_prefix(arg, \"--gpg-sign\", &sign_commit);\n\nI personally find this a bit convoluted, compared to just assigning \"\".\nMaybe there are reasons for doing it this way that I don't see?\n\n> +                   continue;\n> +               }\n> +\n> +               if (skip_prefix(arg, \"-S\", &sign_commit) ||\n> +                       skip_prefix(arg, \"--gpg-sign=\", &sign_commit))\n>                         continue;\n\nLooks good.\n\n> --- a/t/t7510-signed-commit.sh\n> +++ b/t/t7510-signed-commit.sh\n> @@ -51,7 +51,9 @@ test_expect_success GPG 'create signed commits' '\n>         # commit.gpgsign is still on but this must not be signed\n>         git tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) &&\n>         # explicit -S of course must sign.\n> -       git tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree})\n> +       git tag tenth-signed $(echo 10 | git commit-tree -S HEAD^{tree})\n> +       # --gpg-sign must sign.\n> +       git tag eleventh-signed $(echo 11 | git commit-tree --gpg-sign HEAD^{tree})\n>  '\n\nThanks for providing a test, and thanks for fixing the \"9\"/\"10\"\ncopy-paste error. (You might want to remark \"Fix a 9/10 typo while we're\nhere.\" in the commit message, especially since that line requires\nediting anyway, see next paragraph.)\n\nAll of the commands above are suffixed with \"&&\" which means that the\nshell only keeps executing as long as the commands succeed. If any of\nthose 20-30 commands fails, the shell will jump out of the &&-chain and\nland ... at this line you're adding. If that one succeeds, the test will\nbe reported as succeeding. So please add a \"&&\" to the \"10\" line.\n\nAll of that said ... if I add the missing \"&&\" and run your test on the\n*old* implementation, it still succeeds. The reason is that I grepped\nfor the \"best\" place to put this, and directed you to a part of the test\nsuite where it might be a bit too easy to copy existing code and end\nup with something non-ideal. Sorry about that. :-(\n\nWhat happens is that git commit-tree reports \"fatal: Not a valid object\nname --gpg-sign\", then we go on to happily execute `git tag\neleventh-signed` where we've just substituted the empty string produced\nby git commit-tree. The verifications will succeed, because there's\nalready a signature there... (BTW, the verifications happen further\ndown, so you'll want to add \"eleventh-signed\" to the list of tags\nthere.)\n\nOne way of making the test more robust might be to add a brand new\ncommit, similar to how it's done earlier in the test.\n\nIt's not your fault that you fell into this trap. If you want to look\ninto making this more robust -- and try running the test before and after\nyour fix -- great! If you feel it's out of your comfort zone or interest\nrange, just let me know and I could try to take it from here.\n\n>  test_expect_success GPG 'verify and show signatures' '\n\nBTW, here's that test where the signatures are verified.\n\nMartin\n"},{"id":"367102","messageId":"CAETBDP7BcENA3Cjbj6y_5L3WGHx1wmMwkagx_CuRr5gAz9wYVA@mail.gmail.com","threadId":"16566","inReplyTo":"CAN0heSrE3mOnjyrzw76Gp6wBuq62wgM6jBV1u+vn3b6ySm0EDQ@mail.gmail.com","subject":"Re: [PATCH] commit-tree: add missing --gpg-sign flag","fromName":"Brandon Richardson","fromEmail":"brandon1024.br@gmail.com","sentAt":"2019-01-18T15:06:36Z","receivedAt":"2019-01-18T15:06:52Z","isPatch":true,"sender":{"key":"brandon1024.br@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22732449?v=4"},"body":"Hi Martin,\n\nThanks again for your comments and patience, I appreciate it :-)\n\n> > -               if (skip_prefix(arg, \"-S\", &sign_commit))\n> > +               if(!strcmp(arg, \"--gpg-sign\")) {\n> > +                   skip_prefix(arg, \"--gpg-sign\", &sign_commit);\n>\n> I personally find this a bit convoluted, compared to just assigning \"\".\n> Maybe there are reasons for doing it this way that I don't see?\n\nYou're right, it is a bit convoluted. The reason I opted to do it this way is\nbecause I wanted to reuse part of \"arg\" by simply advancing the pointer,\nbut this is silly and wastes cycles, and after thinking about it a bit more\nI realized how silly it was.\n\n> All of the commands above are suffixed with \"&&\" which means that the\n> shell only keeps executing as long as the commands succeed. If any of\n> those 20-30 commands fails, the shell will jump out of the &&-chain and\n> land ... at this line you're adding. If that one succeeds, the test will\n> be reported as succeeding. So please add a \"&&\" to the \"10\" line.\n\nWoops, I can't believe I missed that.\n\n> All of that said ... if I add the missing \"&&\" and run your test on the\n> *old* implementation, it still succeeds. The reason is that I grepped\n> for the \"best\" place to put this, and directed you to a part of the test\n> suite where it might be a bit too easy to copy existing code and end\n> up with something non-ideal. Sorry about that. :-(\n>\n> What happens is that git commit-tree reports \"fatal: Not a valid object\n> name --gpg-sign\", then we go on to happily execute `git tag\n> eleventh-signed` where we've just substituted the empty string produced\n> by git commit-tree. The verifications will succeed, because there's\n> already a signature there... (BTW, the verifications happen further\n> down, so you'll want to add \"eleventh-signed\" to the list of tags\n> there.)\n\nNo need to apologize, I jumped the gun. I'm going to look into putting\ntogether a more robust test for this change.\n\nBrandon\n"},{"id":"367142","messageId":"xmqqy37hiss5.fsf@gitster-ct.c.googlers.com","threadId":"16566","inReplyTo":"20190118010918.43705-1-brandon1024.br@gmail.com","subject":"Re: [PATCH] commit-tree: add missing --gpg-sign flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-18T21:02:02Z","receivedAt":"2019-01-18T21:02:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Richardson <brandon1024.br@gmail.com> writes:\n\n> -\t\tif (skip_prefix(arg, \"-S\", &sign_commit))\n> +\t\tif(!strcmp(arg, \"--gpg-sign\")) {\n\nStyle.  \"if (!strcmp(arg, \"--gpg-sign\")) {\"\n\n> +\t\t    skip_prefix(arg, \"--gpg-sign\", &sign_commit);\n> +\t\t    continue;\n\nTechnically, skipping the prefix S of string S will make us point at\nan empty substring at the end.  So from that point of view,\nskip_prefix(arg, \"--gpg-sign\", &sign_commit) is not incorrect\nper-se, but it is highly misleading.  We have already determined\nthat the user gave us \"--gpg-sign\" option without anything after it,\nso we want to summon the \"use the default key\" behaviour by giving\nan empty string to sign_commit.\n\nAn explicit assignment\n\n\tsign_commit = \"\";\n\nwould be a lot more readable and make the intent a lot more clear.\n\n> +\t\t}\n> +\n> +\t\tif (skip_prefix(arg, \"-S\", &sign_commit) ||\n> +\t\t\tskip_prefix(arg, \"--gpg-sign=\", &sign_commit))\n\nThis side is OK.  \"-S\" gives us an empty string, but \"-Skeyid\" gives\nus \"keyid\" in sign_commit.\n\n>  \t\t\tcontinue;\n>  \n>  \t\tif (!strcmp(arg, \"--no-gpg-sign\")) {\n> diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh\n> index 86d3f93fa..efc136eaf 100755\n> --- a/t/t7510-signed-commit.sh\n> +++ b/t/t7510-signed-commit.sh\n> @@ -51,7 +51,9 @@ test_expect_success GPG 'create signed commits' '\n>  \t# commit.gpgsign is still on but this must not be signed\n>  \tgit tag ninth-unsigned $(echo 9 | git commit-tree HEAD^{tree}) &&\n>  \t# explicit -S of course must sign.\n> -\tgit tag tenth-signed $(echo 9 | git commit-tree -S HEAD^{tree})\n> +\tgit tag tenth-signed $(echo 10 | git commit-tree -S HEAD^{tree})\n> +\t# --gpg-sign must sign.\n> +\tgit tag eleventh-signed $(echo 11 | git commit-tree --gpg-sign HEAD^{tree})\n>  '\n>  \n>  test_expect_success GPG 'verify and show signatures' '\n"},{"id":"375484","messageId":"20190514092834.2003-1-worldhello.net@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[L10N] Kickoff for Git 2.22.0 round #1","fromName":"Jiang Xin","fromEmail":"worldhello.net@gmail.com","sentAt":"2019-05-14T09:28:34Z","receivedAt":"2019-05-14T09:28:55Z","isPatch":false,"sender":{"key":"worldhello.net@gmail.com","avatar":"https://avatars.githubusercontent.com/u/183860?v=4"},"body":"Hi,\n\nGit v2.22.0-rc0 has been released, and it's time to start new round of git l10n.\nThis time there are 270 updated messages need to be translated since last\nupdate.  Let's start new round of git l10n based on the following commit:\n\n\n    l10n: git.pot: v2.22.0 round 1 (270 new, 56 removed)\n    \n    Generate po/git.pot from v2.22.0-rc0 for git v2.22.0 l10n round 1.\n    \n    Signed-off-by: Jiang Xin <worldhello.net@gmail.com>\n\nYou can get it from the usual place:\n\n    https://github.com/git-l10n/git-po/\n\nAs how to update your XX.po and help to translate Git, please see\n\"Updating a XX.po file\" and other sections in \"po/README\" file.\n\n--\nJiang Xin\n"},{"id":"513133","messageId":"20250227015804.100880-1-worldhello.net@gmail.com","threadId":"16566","inReplyTo":"y","subject":"[L10N] Kickoff for Git 2.49.0","fromName":"Jiang Xin","fromEmail":"worldhello.net@gmail.com","sentAt":"2025-02-27T01:57:59Z","receivedAt":"2025-02-27T01:58:09Z","isPatch":false,"sender":{"key":"worldhello.net@gmail.com","avatar":"https://avatars.githubusercontent.com/u/183860?v=4"},"body":"Hi,\n\nGit 2.49.0-rc0 has been released, and it's time to start a new round of\ngit l10n.  This time there are 35 updated messages need to be translated\nsince the last release. Please send your pull request to the l10n\ncoordinator's repository below before this update window closes on\nThu, 13 Mar 2025.\n\n    https://github.com/git-l10n/git-po/\n\nThe following description of our l10n workflow is from the \"po/README.md\"\nfile.\n\n\n## The \"po/git.pot\" file is a generated file, no longer in the repository\n\nThe l10n coordinator does not need to generate the \"po/git.pot\" file every\ntime to start a new l10n workflow, and there is no \"po/git.pot\" file at all.\n\nEveryone can generate the \"po/git.pot\" file with the command below:\n\n    make po/git.pot\n\nBut we can also forget about it. By updating our corresponding \"po/XX.po\"\nfile, the \"po/git.pot\" file is automatically generated.\n\n\n## Update the \"po/XX.po\" file, and start to translate\n\nBefore updating the \"po/XX.po\" file, l10n contributors should pull the latest\ncommits from the master branch of \"git.git\". E.g.:\n\n    git pull --rebase git@github.com:git/git.git master\n\nThen update the cooresponding \"po/XX.po\" file using the following command:\n\n    make po-update PO_FILE=po/XX.po\n\nTranslate the uptodate \"po/XX.po\" file, and create a new commit.\n\n\n## Refine your commits, send pull requests\n\nIn the \"po/XX.po\" file, there are location lines in comments like below:\n\n    #: add-interactive.c:535 add-interactive.c:836 reset.c:136 sequencer.c:3505\n    #: sequencer.c:3970 sequencer.c:4127 builtin/rebase.c:1261\n    #: builtin/rebase.c:1671\n\nThese comments with file locations are useful for l10n contributors to locate\nthe context easily during translation. But these file locations introduce a\nlot of noise and will consume a lot of repository storage. Therefore, we\nshould remove these file locations from the \"po/XX.po\" file.\n\nTo remove file locations in the \"po/XX.po\" file, you can use one of the\nfollowing two ways, but don't switch back and forth.\n\n * Keep the filenames, only remove locations (need gettext 0.19 and above):\n\n        msgcat --add-location=file po/XX.po >po/XX.po.new\n        mv po/XX.po.new po/XX.po\n\n * Remove both filenames and locations:\n\n        msgcat --no-location po/XX.po >po/XX.po.new\n        mv po/XX.po.new po/XX.po\n\nAfter squashing trivial commits and removing file locations in the \"po/XX.po\"\nfile, send pull request to the l10n coordinator's repository below:\n\n    https://github.com/git-l10n/git-po/\n\n\n## Resolve errors found by the l10n CI pipeline for the pull request\n\nA helper program hosted on \"https://github.com/git-l10n/git-po-helper\" can\nhelp git l10n coordinator and git l10n contributors to check the conventions\nof git l10n contributions, and it is also used in GitHub actions as l10n CI\npipeline to validate each pull request in the \"git-l10n/git-po\" repository.\nPlease fix the issues found by the helper program.\n\n\n** Please note: The update window will close on Thu, 13 Mar 2025. **\n\n\n--\nJiang Xin\n"}]}