diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 0.4.7 (2025-03-11)
+- 'rename': only sed Main.hs if it exists
+- 'sdist': prefix "Running hlint" with '#'
+- 'upload': handle existing tarball from aborted build without tag
+- 'upload-haddock': add --force which removes existing haddock tarball
+
 ## 0.4.6 (2024-06-20)
 - 'new': add --license option
 - 'new': bump stack.yaml resolver to lts-21
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@
 ["src/Main.hs","data/template.cabal.tmpl","README.md","CHANGELOG.md","LICENSE","hkgr.cabal"]
 [(NoExec,"CHANGELOG.md"),(NoExec,"LICENSE"),(NoExec,"README.md"),(NoExec,"data/template.cabal.tmpl"),(NoExec,"hkgr.cabal"),(NoExec,"src/Main.hs")]
 Wrote tarball sdist to /home/petersen/github/hkgr/.hkgr/hkgr-0.4.tar.gz
+hackage.haskell.org password: ^C
 ```
 
 After fixing up, retag a new tarball with `--force` and upload candidate,
@@ -72,7 +73,7 @@
 `$ hkgr --version`
 
 ```
-0.4.6
+0.4.7
 ```
 `$ hkgr --help`
 
@@ -103,7 +104,7 @@
 ## Details
 
 ### tagdist
-`hkgr tagdist` makes a dist tarball from a git tag:
+`hkgr tagdist` creates a git tag and a sdist tarball from it:
 
 The `tagdist` command first reads the current package version
 (from the `.cabal` file in the current directory), and uses that to `git tag`.
@@ -126,6 +127,9 @@
 
 If sdist fails for some reason then hkgr tries to reset the tag.
 
+A cabal build of the tarball content is also attempted to catch any build errors
+from the package.
+
 Alternatively if you have already manually tagged a release with 'v' prefix
 you can use `--existing-tag` to create a dist tarball.
 
@@ -157,7 +161,7 @@
 ### new
 `hkgr new` creates a new project.
 
-If you don't pass a name it will try to check the current directory.
+If you don't pass a name it will try to check and use the current directory name.
 
 It uses `cabal init` to setup various files but replaces the .cabal file
 with a template stored in `~/.config/hkgr/template.cabal` which the user
diff --git a/hkgr.cabal b/hkgr.cabal
--- a/hkgr.cabal
+++ b/hkgr.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.0
 name:                hkgr
-version:             0.4.6
+version:             0.4.7
 synopsis:            Simple Hackage release workflow for package maintainers
 description:
             Hkgr (Hackager) is a tool to help make new releases of
@@ -13,16 +13,16 @@
 license-file:        LICENSE
 author:              Jens Petersen <juhpetersen@gmail.com>
 maintainer:          Jens Petersen <juhpetersen@gmail.com>
-copyright:           2019-2024  Jens Petersen
+copyright:           2019-2025  Jens Petersen
 category:            Util
 build-type:          Simple
 data-dir:            data
 data-files:          template.cabal.tmpl
 extra-doc-files:     README.md
                    , CHANGELOG.md
-tested-with:         GHC == 9.8 || == 9.6 || == 9.4 || == 9.2 || == 9.0
-                     || == 8.10 || == 8.8 || == 8.6 || == 8.4 || == 8.2
-                     || == 8.0
+tested-with:         GHC == 9.10 || == 9.8 || == 9.6 || == 9.4 || == 9.2 || == 9.0
+                     ||  == 8.10 || == 8.8 || == 8.6 || == 8.4 || == 8.2
+                     ||  == 8.0
 
 source-repository head
   type:                git
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -53,9 +53,11 @@
       <$> nobuildOpt
       <*> switchWith 'U' "no-upload" "Do not upload to Hackage"
     , Subcommand "upload-haddock" "Upload candidate documentation to Hackage" $
-      pure $ upHaddockCmd False
+      upHaddockCmd False
+      <$> forceOpt "rebuild doc tarball"
     , Subcommand "publish-haddock" "Publish documentation to Hackage" $
-      pure $ upHaddockCmd True
+      upHaddockCmd True
+      <$> forceOpt "rebuild doc tarball"
     , Subcommand "build" "Do a local pristine build from the tarball" $
       pure pristineBuildCmd
     , Subcommand "version" "Show the package version from .cabal file" $
@@ -139,13 +141,14 @@
   pkgid <- checkPackage True
   let tag = pkgidTag pkgid
   mtagHash <- cmdMaybe (git "rev-parse" [tag])
-  when (not force && isJust mtagHash) $ assertTagOnBranch tag
+  let tagexists = isJust mtagHash
+  when (not force && tagexists) $ assertTagOnBranch tag
   if existingtag
-    then if isNothing mtagHash
+    then if not tagexists
          then error' $ "tag" +-+ tag +-+ "does not exist"
          else sdist force noHlint nobuild pkgid
     else do
-    if isJust mtagHash && not force
+    if tagexists && not force
       then do
       headHash <- cmdOut (git "rev-parse" ["HEAD"])
       let onHead = Just headHash == mtagHash
@@ -159,7 +162,9 @@
       else do
       git_ "tag" $ ["--force" | force] ++ [tag]
       unless force $ putStrLn tag
-      sdist force noHlint nobuild pkgid `onException` do
+      -- actually always forced because: `== not (tagexists && not force)`
+      sdist (force || not tagexists) noHlint nobuild pkgid
+        `onException` do
         putStrLn "Resetting tag"
         if force
           then git_ "tag" ["--force", tag, fromJust mtagHash]
@@ -222,7 +227,7 @@
     unless noHlint $ do
       mhlint <- findExecutable "hlint"
       when (isJust mhlint) $ do
-        putStrLn "Running hlint"
+        putStrLn "# Running hlint"
         void $ P.runProcess $ P.proc "hlint" ["--no-summary", "."]
     let dest = takeDirectory $ cwd </> target
     unlessM (doesDirectoryExist dest) $
@@ -236,6 +241,11 @@
   pkgid <- getPackageId
   putStrLn $ packageVersion pkgid
 
+gitListTag :: String -> IO Bool
+gitListTag tag = do
+  res <- cmdOut $ git "tag" ["--list", tag]
+  return $ res == tag
+
 -- FIXME cabal install creates tarballs now
 uploadCmd :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> IO ()
 uploadCmd publish existingtag force noHlint nobuild noupload = do
@@ -243,8 +253,10 @@
   pkgid <- checkPackage False
   let tarball = sdistDir </> showPkgId pkgid <.> tarGzExt
       tag = pkgidTag pkgid
-  exists <- doesFileExist tarball
-  when (force || not exists) $
+  tagexists <- gitListTag tag
+  when (not tagexists && existingtag) $
+    error' $ tag +-+ "does not exist"
+  when (force || not tagexists) $
     tagDistCmd existingtag force noHlint nobuild
   assertTagOnBranch tag
   untagged <- cmdLines $ git "log" ["--pretty=reference", tag ++ "..HEAD"]
@@ -300,13 +312,17 @@
   when hide $ putChar '\n'
   return inp
 
-upHaddockCmd :: Bool -> IO ()
-upHaddockCmd publish = do
+upHaddockCmd :: Bool -> Bool -> IO ()
+upHaddockCmd publish force = do
   needProgram "cabal"
   userpassBS <- getUserPassword
-  _out <- P.readProcessInterleaved_ (P.setStdin (P.byteStringInput userpassBS) $ P.proc "cabal" ("upload" : "--documentation" : ["--publish" | publish]))
-
   pkgid <- getPackageId
+  when force $ do
+    let tarball = "dist" </> showPkgId pkgid ++ '-' : "docs" <.> tarGzExt
+    exists <- doesFileExist tarball
+    when exists $
+      removeFile tarball
+  _out <- P.readProcessInterleaved_ (P.setStdin (P.byteStringInput userpassBS) $ P.proc "cabal" ("upload" : "--documentation" : ["--publish" | publish]))
   putStrLn $ (if publish then "Published at" else "Uploaded to") +-+ "https://hackage.haskell.org/package/" ++ showPkgId pkgid ++ if publish then "" else "/candidate"
 
 cabal_ :: String -> [String] -> IO ()
@@ -453,7 +469,8 @@
   sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g", "s/Paths_" ++ replace "-" "_" oldname ++ "/Paths_" ++ replace "-" "_" newname ++ "/g"] $ oldname <.> "cabal"
   git_ "mv" [oldname <.> "cabal", newname <.> "cabal"]
   -- FIXME for all *.hs
-  sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g", "s/Paths_" ++ replace "-" "_" oldname ++ "/Paths_" ++ replace "-" "_" newname ++ "/g"] "src/Main.hs"
+  whenM (doesFileExist "src/Main.hs") $
+    sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g", "s/Paths_" ++ replace "-" "_" oldname ++ "/Paths_" ++ replace "-" "_" newname ++ "/g"] "src/Main.hs"
   sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g"] "README.md"
   sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g"] "ChangeLog.md"
   renameDirectory (".." </> oldname) (".." </> newname)
