Index: lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java	(revision 1301045)
+++ lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java	(working copy)
@@ -924,6 +924,7 @@
     writer.addDocument(new Document());
     writer.prepareCommit();
     assertFalse(DirectoryReader.indexExists(dir));
+    writer.commit();
     writer.close();
     assertTrue(DirectoryReader.indexExists(dir));
     dir.close();
Index: lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java	(revision 1301045)
+++ lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java	(working copy)
@@ -1801,4 +1801,54 @@
     w.close();
     dir.close();
   }
+
+  // LUCENE-3872
+  public void testPrepareCommitThenClose() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriter w = new IndexWriter(dir,
+                                    new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+
+    w.prepareCommit();
+    try {
+      w.close();
+      fail("should have hit exception");
+    } catch (IllegalStateException ise) {
+      // expected
+    }
+    w.commit();
+    w.close();
+    IndexReader r = IndexReader.open(dir);
+    assertEquals(0, r.maxDoc());
+    r.close();
+    dir.close();
+  }
+
+  // LUCENE-3872
+  public void testPrepareCommitThenRollback() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriter w = new IndexWriter(dir,
+                                    new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+
+    w.prepareCommit();
+    w.rollback();
+    assertFalse(DirectoryReader.indexExists(dir));
+    dir.close();
+  }
+
+  // LUCENE-3872
+  public void testPrepareCommitThenRollback2() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriter w = new IndexWriter(dir,
+                                    new IndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)));
+
+    w.commit();
+    w.addDocument(new Document());
+    w.prepareCommit();
+    w.rollback();
+    assertTrue(DirectoryReader.indexExists(dir));
+    IndexReader r = IndexReader.open(dir);
+    assertEquals(0, r.maxDoc());
+    r.close();
+    dir.close();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(revision 1301045)
+++ lucene/core/src/java/org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -834,6 +834,11 @@
   private void closeInternal(boolean waitForMerges) throws CorruptIndexException, IOException {
 
     try {
+
+      if (pendingCommit != null) {
+        throw new IllegalStateException("cannot close: prepareCommit was already called with no corresponding call to commit");
+      }
+
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "now flush at close waitForMerges=" + waitForMerges);
       }
@@ -2358,7 +2363,7 @@
    *  #rollback()} to revert the commit and undo all changes
    *  done since the writer was opened.</p>
    *
-   *  You can also just call {@link #commit(Map)} directly
+   *  <p>You can also just call {@link #commit(Map)} directly
    *  without prepareCommit first in which case that method
    *  will internally call prepareCommit.
    *
Index: lucene/CHANGES.txt
===================================================================
--- lucene/CHANGES.txt	(revision 1301045)
+++ lucene/CHANGES.txt	(working copy)
@@ -933,6 +933,10 @@
 * LUCENE-3841: Fix CloseableThreadLocal to also purge stale entries on
   get(); this fixes certain cases where we were holding onto objects
   for dead threads for too long (Matthew Bellew, Mike McCandless)
+
+* LUCENE-3872: IndexWriter.close() now throws IllegalStateException if
+  you call it after calling prepareCommit() without calling commit()
+  first.  (Tim Bogaert via Mike McCandless)
     
 Optimizations
 
