diff --git a/Commands.hs b/Commands.hs
index a403a5a48a..f28b3e72b9 100644
--- a/Commands.hs
+++ b/Commands.hs
@@ -26,20 +26,21 @@ import qualified Remotes
  - actions to be run in the Annex monad. -}
 parseCmd :: [String] -> IO ([Flag], [Annex ()])
 parseCmd argv = do
-	(flags, files) <- getopt
-	case (length files) of
+	(flags, params) <- getopt
+	case (length params) of
 		0 -> error header
 		_ -> do
-			let c = lookupCmd (files !! 0)
-			if (0 == length c)
-				then ret flags defaultCmd files
-				else ret flags (snd $ c !! 0) $ drop 1 files
+			let (cmd, locs) = takeCmd params $ lookupCmd (params !! 0)
+			files <- mapM recurseFiles locs
+			return (flags, map cmd $ foldl (++) [] files)
 	where
-		ret flags cmd files = return (flags, makeactions cmd files)
-		makeactions cmd files = map cmd files
 		getopt = case getOpt Permute options argv of
 			(flags, nonopts, []) -> return (flags, nonopts)
 			(_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))
+		takeCmd files cmds =
+			if (0 == length cmds)
+				then (defaultCmd, files)
+				else ((snd $ cmds !! 0), drop 1 files)
 		lookupCmd cmd = filter (\(c, a) -> c == cmd) cmds
 		cmds =	[ ("add", addCmd)
 			, ("get", getCmd)
diff --git a/TODO b/TODO
index ddd0761658..8cb77fe9fb 100644
--- a/TODO
+++ b/TODO
@@ -2,6 +2,8 @@
 
 * --push/--pull/--want
 
+* isn't pull the same as get?
+
 * recurse on directories
 
 * how to handle git mv file?
diff --git a/Utility.hs b/Utility.hs
index e04b44e6ff..8005fd17cc 100644
--- a/Utility.hs
+++ b/Utility.hs
@@ -7,12 +7,14 @@ module Utility (
 	parentDir,
 	relPathCwdToDir,
 	relPathDirToDir,
+	recurseFiles,
 ) where
 
 import System.IO
 import System.Posix.IO
 import Data.String.Utils
 import System.Path
+import System.IO.HVFS
 import System.FilePath
 import System.Directory
 
@@ -87,3 +89,15 @@ relPathDirToDir from to =
 		dotdots = take ((length pfrom) - numcommon) $ repeat ".."
 		numcommon = length $ common
 		path = join s $ dotdots ++ uncommon
+
+{- Recursively returns all files and symlinks (to anything) in the specified
+ - path. If the path is a file, returns only it. Does not follow symlinks to
+ - directories. -}
+recurseFiles :: FilePath -> IO [FilePath]
+recurseFiles path = do
+	find <- recurseDirStat SystemFS path
+	return $ filesOnly find
+	where
+		filesOnly l = map (\(f,s) -> f) $ filter isFile l
+		isFile (f, HVFSStatEncap s) =
+			vIsRegularFile s || vIsSymbolicLink s
diff --git a/git-annex.mdwn b/git-annex.mdwn
index 1261a196fe..1922a1b63c 100644
--- a/git-annex.mdwn
+++ b/git-annex.mdwn
@@ -42,6 +42,11 @@ Enough broad picture, here's how it actually looks:
 * `git annex $file` is a shorthand. If the file
   is already known, it does `git annex get`, otherwise it does `git annex add`.
 
+Oh yeah, "$file" in the above can be any number of files, or directories.
+git-annex automatically recurses into directories, but skips files that are
+checked into git (as well as skipping `.git` itself), so "git annex ." works
+fine.
+
 ## copies
 
 git-annex can be configured to try to keep N copies of a file's content