Merge branch 'vsco-fix-log-reopen-with-inotify'
diff --git a/tail_test.go b/tail_test.go
index 8dcc02c..38d6b84 100644
--- a/tail_test.go
+++ b/tail_test.go
@@ -343,13 +343,21 @@
 		"test.txt",
 		Config{Follow: true, ReOpen: true, Poll: poll})
 	content := []string{"hello", "world", "more", "data", "endofworld"}
-	go tailTest.ReadLines(tail, content)
+	go tailTest.VerifyTailOutput(tail, content, false)
 
-	// deletion must trigger reopen
-	<-time.After(delay)
-	tailTest.RemoveFile("test.txt")
-	<-time.After(delay)
-	tailTest.CreateFile("test.txt", "more\ndata\n")
+	if poll {
+		// deletion must trigger reopen
+		<-time.After(delay)
+		tailTest.RemoveFile("test.txt")
+		<-time.After(delay)
+		tailTest.CreateFile("test.txt", "more\ndata\n")
+	} else {
+		// In inotify mode, fsnotify is currently unable to deliver notifications
+		// about deletion of open files, so we are not testing file deletion.
+		// (see https://github.com/fsnotify/fsnotify/issues/194 for details).
+		<-time.After(delay)
+		tailTest.AppendToFile("test.txt", "more\ndata\n")
+	}
 
 	// rename must trigger reopen
 	<-time.After(delay)
@@ -366,7 +374,34 @@
 	// Do not bother with stopping as it could kill the tomb during
 	// the reading of data written above. Timings can vary based on
 	// test environment.
-	tail.Cleanup()
+	tailTest.Cleanup(tail, false)
+}
+
+func TestInotify_WaitForCreateThenMove(t *testing.T) {
+	tailTest := NewTailTest("wait-for-create-then-reopen", t)
+	os.Remove(tailTest.path + "/test.txt") // Make sure the file does NOT exist.
+
+	tail := tailTest.StartTail(
+		"test.txt",
+		Config{Follow: true, ReOpen: true, Poll: false})
+
+	content := []string{"hello", "world", "endofworld"}
+	go tailTest.VerifyTailOutput(tail, content, false)
+
+	time.Sleep(50 * time.Millisecond)
+	tailTest.CreateFile("test.txt", "hello\nworld\n")
+	time.Sleep(50 * time.Millisecond)
+	tailTest.RenameFile("test.txt", "test.txt.rotated")
+	time.Sleep(50 * time.Millisecond)
+	tailTest.CreateFile("test.txt", "endofworld\n")
+	time.Sleep(50 * time.Millisecond)
+	tailTest.RemoveFile("test.txt.rotated")
+	tailTest.RemoveFile("test.txt")
+
+	// Do not bother with stopping as it could kill the tomb during
+	// the reading of data written above. Timings can vary based on
+	// test environment.
+	tailTest.Cleanup(tail, false)
 }
 
 func reSeek(t *testing.T, poll bool) {
@@ -426,6 +461,13 @@
 	}
 }
 
+func (t TailTest) AppendToFile(name string, contents string) {
+	err := ioutil.WriteFile(t.path+"/"+name, []byte(contents), 0600|os.ModeAppend)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 func (t TailTest) RemoveFile(name string) {
 	err := os.Remove(t.path + "/" + name)
 	if err != nil {
diff --git a/watch/inotify_tracker.go b/watch/inotify_tracker.go
index 4f85217..d68ab61 100644
--- a/watch/inotify_tracker.go
+++ b/watch/inotify_tracker.go
@@ -108,28 +108,10 @@
 		delete(shared.done, winfo.fname)
 		close(done)
 	}
-
-	fname := winfo.fname
-	if winfo.isCreate() {
-		// Watch for new files to be created in the parent directory.
-		fname = filepath.Dir(fname)
-	}
-	shared.watchNums[fname]--
-	watchNum := shared.watchNums[fname]
-	if watchNum == 0 {
-		delete(shared.watchNums, fname)
-	}
 	shared.mux.Unlock()
 
-	// If we were the last ones to watch this file, unsubscribe from inotify.
-	// This needs to happen after releasing the lock because fsnotify waits
-	// synchronously for the kernel to acknowledge the removal of the watch
-	// for this file, which causes us to deadlock if we still held the lock.
-	if watchNum == 0 {
-		return shared.watcher.Remove(fname)
-	}
 	shared.remove <- winfo
-	return nil
+	return <-shared.error
 }
 
 // Events returns a channel to which FileEvents corresponding to the input filename
@@ -155,6 +137,8 @@
 
 	if shared.chans[winfo.fname] == nil {
 		shared.chans[winfo.fname] = make(chan fsnotify.Event)
+	}
+	if shared.done[winfo.fname] == nil {
 		shared.done[winfo.fname] = make(chan bool)
 	}
 
@@ -164,47 +148,50 @@
 		fname = filepath.Dir(fname)
 	}
 
+	var err error
 	// already in inotify watch
-	if shared.watchNums[fname] > 0 {
-		shared.watchNums[fname]++
-		if winfo.isCreate() {
-			shared.watchNums[winfo.fname]++
-		}
-		return nil
+	if shared.watchNums[fname] == 0 {
+		err = shared.watcher.Add(fname)
 	}
-
-	err := shared.watcher.Add(fname)
 	if err == nil {
 		shared.watchNums[fname]++
-		if winfo.isCreate() {
-			shared.watchNums[winfo.fname]++
-		}
 	}
 	return err
 }
 
 // removeWatch calls fsnotify.RemoveWatch for the input filename and closes the
 // corresponding events channel.
-func (shared *InotifyTracker) removeWatch(winfo *watchInfo) {
+func (shared *InotifyTracker) removeWatch(winfo *watchInfo) error {
 	shared.mux.Lock()
-	defer shared.mux.Unlock()
 
 	ch := shared.chans[winfo.fname]
-	if ch == nil {
-		return
+	if ch != nil {
+		delete(shared.chans, winfo.fname)
+		close(ch)
 	}
 
-	delete(shared.chans, winfo.fname)
-	close(ch)
+	fname := winfo.fname
+	if winfo.isCreate() {
+		// Watch for new files to be created in the parent directory.
+		fname = filepath.Dir(fname)
+	}
+	shared.watchNums[fname]--
+	watchNum := shared.watchNums[fname]
+	if watchNum == 0 {
+		delete(shared.watchNums, fname)
+	}
+	shared.mux.Unlock()
 
-	if !winfo.isCreate() {
-		return
+	var err error
+	// If we were the last ones to watch this file, unsubscribe from inotify.
+	// This needs to happen after releasing the lock because fsnotify waits
+	// synchronously for the kernel to acknowledge the removal of the watch
+	// for this file, which causes us to deadlock if we still held the lock.
+	if watchNum == 0 {
+		err = shared.watcher.Remove(fname)
 	}
 
-	shared.watchNums[winfo.fname]--
-	if shared.watchNums[winfo.fname] == 0 {
-		delete(shared.watchNums, winfo.fname)
-	}
+	return err
 }
 
 // sendEvent sends the input event to the appropriate Tail.
@@ -239,7 +226,7 @@
 			shared.error <- shared.addWatch(winfo)
 
 		case winfo := <-shared.remove:
-			shared.removeWatch(winfo)
+			shared.error <- shared.removeWatch(winfo)
 
 		case event, open := <-shared.watcher.Events:
 			if !open {