diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
 
 #### Upcoming
 
+#### v0.4.1.0
+
+*Minor*
+
+* Added bash completion for `--mode` and arguments. ([#71](https://github.com/hjwylde/git-fmt/issues/71))
+
+*Revisions*
+
+* Changed path outputs to be relative to the root directory. ([#69](https://github.com/hjwylde/git-fmt/issues/69))
+* Fixed a bug where `--operate-on` didn't work in subdirectories. ([#69](https://github.com/hjwylde/git-fmt/issues/69))
+
 #### v0.4.0.0
 
 *Major*
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -81,5 +81,26 @@
 #### Configuration
 
 git-fmt delegates to omnifmt for configuration, see
-    [here](https://github.com/hjwylde/git-fmt#configuration) for documentation and examples.
+    [here](https://github.com/hjwylde/omnifmt#configuration) for documentation and examples.
+
+### Auto-completion
+
+Add the following (depending on your shell) to include support for auto-completion.
+
+**Bash:**
+
+```bash
+source <(git-fmt --bash-completion-script `which git-fmt`)
+```
+
+**zsh:**
+
+```zsh
+autoload -Uz bashcompinit && bashcompinit
+source <(git-fmt --bash-completion-script `which git-fmt`)
+```
+
+**NB:** auto-completion doesn't work well with git's command macro. I.e., `git fmt <TAB>` won't
+    work, but `git-fmt <TAB>` will. [#71](https://github.com/hjwylde/git-fmt/issues/71) will remain
+    open until this is addressed.
 
diff --git a/app/Git/Fmt/Options.hs b/app/Git/Fmt/Options.hs
--- a/app/Git/Fmt/Options.hs
+++ b/app/Git/Fmt/Options.hs
@@ -21,11 +21,12 @@
 import Data.Char    (isDigit)
 import Data.Version (showVersion)
 
+import Git.Fmt.Version as This
+
+import Omnifmt.Config            as Config
 import Options.Applicative
 import Options.Applicative.Types (readerAsk)
 
-import Git.Fmt.Version as This
-
 -- | Options.
 data Options = Options {
         optChatty    :: Chatty,
@@ -56,22 +57,24 @@
 
 -- | An optparse parser of a git-fmt command.
 gitFmtInfo :: ParserInfo Options
-gitFmtInfo = info (infoOptions <*> gitFmt) fullDesc
+gitFmtInfo = info (infoOptions <*> gitFmt) (fullDesc <> progDesc')
     where
-        infoOptions = helper <*> version <*> numericVersion
-        helper = abortOption ShowHelpText $ mconcat [
+        infoOptions     = helper <*> version <*> numericVersion
+        helper          = abortOption ShowHelpText $ mconcat [
             short 'h', hidden,
             help "Show this help text"
             ]
-        version = infoOption ("Version " ++ showVersion This.version) $ mconcat [
+        version         = infoOption ("Version " ++ showVersion This.version) $ mconcat [
             long "version", short 'V', hidden,
             help "Show this binary's version"
             ]
-        numericVersion = infoOption (showVersion This.version) $ mconcat [
+        numericVersion  = infoOption (showVersion This.version) $ mconcat [
             long "numeric-version", hidden,
             help "Show this binary's version (without the prefix)"
             ]
 
+        progDesc' = progDesc $ "Formats the paths given by applying the rules found in the root `" ++ Config.defaultFileName ++ "'"
+
 -- | An options parser.
 gitFmt :: Parser Options
 gitFmt = Options
@@ -92,6 +95,7 @@
     <*> modeOption (mconcat [
         long "mode", short 'm', metavar "MODE",
         value Normal, showDefaultWith $ const "normal",
+        completeWith ["normal", "dry-run", "diff"],
         help "Specify the mode as either `normal', `dry-run' or `diff'"
         ])
     <*> (
@@ -111,15 +115,16 @@
         help "Specify the number of threads to use"
         ])
     <*> many (strArgument $ mconcat [
-        metavar "-- PATHS..."
+        metavar "-- PATHS...",
+        action "file"
         ])
     where
-        natOption   = option $ readerAsk >>= \opt -> if all isDigit opt
-            then return $ Just (read opt :: Int)
-            else readerError $ "not a natural number `" ++ opt ++ "'"
         modeOption  = option $ readerAsk >>= \opt -> case opt of
             "normal"    -> return Normal
             "dry-run"   -> return DryRun
             "diff"      -> return Diff
             _           -> readerError $ "unrecognised mode `" ++ opt ++ "'"
+        natOption   = option $ readerAsk >>= \opt -> if all isDigit opt
+            then return $ Just (read opt :: Int)
+            else readerError $ "not a natural number `" ++ opt ++ "'"
 
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -10,6 +10,7 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 {-# OPTIONS_HADDOCK hide, prune #-}
 
+{-# LANGUAGE BangPatterns          #-}
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings     #-}
@@ -27,7 +28,7 @@
 import qualified Control.Monad.Parallel as Parallel
 import           Control.Monad.Reader
 
-import           Data.List.Extra    (dropEnd, intersect, linesBy, lower)
+import           Data.List.Extra    (dropEnd, intersect, linesBy, lower, nub)
 import           Data.Maybe         (fromJust, fromMaybe, isNothing)
 import           Data.Text          (Text)
 import qualified Data.Text          as T
@@ -39,7 +40,8 @@
 import Git.Fmt.Options
 import Git.Fmt.Pipes
 
-import Omnifmt.Config  as Config
+import Omnifmt.Config    as Config
+import Omnifmt.Directory
 import Omnifmt.Exit
 import Omnifmt.Pipes
 import Omnifmt.Process
@@ -51,7 +53,7 @@
 import qualified Pipes.Prelude    as Pipes
 import           Prelude          hiding (filter, log)
 
-import System.Directory.Extra
+import System.Directory.Extra hiding (withCurrentDirectory)
 import System.Exit
 import System.FilePath
 import System.IO
@@ -65,7 +67,7 @@
 
 main :: IO ()
 main = do
-    options     <- customExecParser gitFmtPrefs gitFmtInfo
+    !options    <- customExecParser gitFmtPrefs gitFmtInfo
     let chatty  = if optMode options == Diff then Quiet else optChatty options
 
     flip runLoggingT (log $ optChatty options) . filter chatty $ do
@@ -82,19 +84,19 @@
 
 handle :: (MonadIO m, MonadLogger m, MonadMask m, MonadParallel m, MonadReader Config m) => Options -> m ()
 handle options = do
-    rootDir         <- liftIO getCurrentDirectory
-    filePaths       <- runPanic $ if null (argPaths options)
+    rootDir             <- runPanic $ init <$> runProcess_ "git" ["rev-parse", "--show-toplevel"]
+    absFilePaths        <- runPanic $ filterM (liftIO . doesFileExist) =<< if null (argPaths options)
         then initialFilePaths options
         else liftM2 intersect (initialFilePaths options) (providedFilePaths options)
-    absFilePaths    <- filterM (liftIO . doesFileExist) filePaths >>= mapM (liftIO . canonicalizePath)
+    let relFilePaths    = nub $ map (makeRelative rootDir) absFilePaths
 
     numThreads <- liftIO getNumCapabilities >>= \numCapabilities ->
         return $ fromMaybe numCapabilities (optThreads options)
 
     (output, input) <- liftIO $ spawn unbounded
 
-    withSystemTempDirectory "git-fmt" $ \tmpDir -> do
-        runEffect $ each (map (\filePath -> omnifmt (makeRelative rootDir filePath) (tmpDir </> dropDrive filePath)) absFilePaths) >-> toOutput output
+    withCurrentDirectory rootDir . withSystemTempDirectory "git-fmt" $ \tmpDir -> do
+        runEffect $ each (map (\filePath -> omnifmt filePath (tmpDir </> filePath)) relFilePaths) >-> toOutput output
 
         liftIO performGC
 
@@ -102,16 +104,19 @@
             runEffect (fromInput input >-> pipeline >-> runner (optMode options)) >>
             liftIO performGC
 
+initialFilePaths :: (MonadError ExitCode m, MonadIO m, MonadLogger m) => Options -> m [FilePath]
+initialFilePaths options = do
+    rootDir <- init <$> runProcess_ "git" ["rev-parse", "--show-toplevel"]
+
+    map (rootDir </>) . linesBy (== '\0') <$> case optOperateOn options of
+        Tracked         -> runProcess_ "git" ["ls-files", "-z", "--full-name", rootDir]
+        Reference ref   -> runProcess_ "git" ["diff", ref, "--name-only", "-z"]
+
 providedFilePaths :: MonadIO m => Options -> m [FilePath]
-providedFilePaths options = concatMapM expandDirectory $ concatMap splitter (argPaths options)
+providedFilePaths options = concatMapM (liftIO . canonicalizePath >=> expandDirectory) $ concatMap splitter (argPaths options)
     where
         splitter = if optNull options then linesBy (== '\0') else (:[])
         expandDirectory path = ifM (liftIO $ doesDirectoryExist path) (liftIO $ listFilesRecursive path) (return [path])
-
-initialFilePaths :: (MonadError ExitCode m, MonadIO m, MonadLogger m) => Options -> m [FilePath]
-initialFilePaths options = linesBy (== '\0') <$> case optOperateOn options of
-    Tracked         -> runProcess_ "git" ["ls-files", "-z"]
-    Reference ref   -> runProcess_ "git" ["diff", ref, "--name-only", "-z"]
 
 pipeline :: (MonadIO m, MonadLogger m, MonadReader Config m) => Pipe (Status, FilePath, FilePath) (Status, FilePath, FilePath) m ()
 pipeline = checkFileSupported >-> createPrettyFile >-> runProgram >-> checkFilePretty
diff --git a/git-fmt.cabal b/git-fmt.cabal
--- a/git-fmt.cabal
+++ b/git-fmt.cabal
@@ -1,5 +1,5 @@
 name:           git-fmt
-version:        0.4.0.0
+version:        0.4.1.0
 
 author:         Henry J. Wylde
 maintainer:     public@hjwylde.com
@@ -34,6 +34,7 @@
 
     default-language: Haskell2010
     other-extensions:
+        BangPatterns
         FlexibleContexts
         MultiParamTypeClasses
         OverloadedStrings
@@ -50,6 +51,7 @@
         optparse-applicative >= 0.11,
         pipes >= 4.1,
         pipes-concurrency >= 2.0,
+        process >= 1.2,
         temporary >= 1.2,
         text >= 1.2,
         time >= 1.5
