Mcuboot hack to prevent `newt upgrade` failure

For projects using mcuboot, `newt upgrade` has been failing.  The
error message is:

    Error: Error updating "mcuboot": error: The following untracked working
    tree files would be overwritten by checkout:
            ext/mbedtls/include/mbedtls/asn1.h
            ext/mbedtls/include/mbedtls/bignum.h
            ext/mbedtls/include/mbedtls/check_config.h
            ext/mbedtls/include/mbedtls/config.h
            ext/mbedtls/include/mbedtls/ecdsa.h
            ext/mbedtls/include/mbedtls/ecp.h
            ext/mbedtls/include/mbedtls/md.h
            ext/mbedtls/include/mbedtls/oid.h
            ext/mbedtls/include/mbedtls/pk.h
            ext/mbedtls/include/mbedtls/platform.h
            ext/mbedtls/include/mbedtls/platform_util.h
            ext/mbedtls/include/mbedtls/threading.h
    Please move or remove them before you switch branches.
    Aborting

The problem is caused by a recent change to mcuboot - a normal directory
was replaced with a submodule.  Git is unable to transition from a
post-replace commit to a pre-replace commit because some files in the
submodule used to exist in the mcuboot repo itself.

This commit implements a pair of hacks to workaround this problem.  Upon
performing a checkout operation on the mcuboot repo:

1. If newt detects the above error message, it removes the `ext/mbedtls`
directory and tries again.

2. If newt detects a different warning message (`unable to rmdir
'sim/mcuboot-sys/mbedtls': Directory not empty`), it removes
`sim/mcuboot-sys/mbedtls`.

The second hack is required when transitioning from pre-replace to
post-replace.  In this case, the checkout operation succeeds, but an
orphaned directory is left behind, making the repo look dirty.

This is expected to be a short-term fix.  When we have a general fix
for this issue, this hack can be removed.
diff --git a/newt/downloader/downloader.go b/newt/downloader/downloader.go
index a5af7bf..8b3a607 100644
--- a/newt/downloader/downloader.go
+++ b/newt/downloader/downloader.go
@@ -365,6 +365,87 @@
 	return gd.init(path)
 }
 
+// untrackedFilesFromCheckoutErr collects the list of untracked files that
+// prevented a checkout from succeeding.  It returns nil if the provided error
+// does not indicate that untracked files are in the way.
+func untrackedFilesFromCheckoutErr(err error) []string {
+	var files []string
+
+	text := err.Error()
+	lines := strings.Split(text, "\n")
+
+	collecting := false
+	for _, line := range lines {
+		if !collecting {
+			if strings.Contains(line,
+				"The following untracked working tree files would "+
+					"be overwritten by checkout:") {
+				collecting = true
+			}
+		} else {
+			if strings.Contains(line, "Please move or remove them before") {
+				collecting = false
+			} else {
+				files = append(files, strings.TrimSpace(line))
+			}
+		}
+	}
+
+	return files
+}
+
+// applyMcubootPreHack attempts to clean up the mcuboot repo so that the
+// subsequent checkout operation will succeed.  This hack is required because a
+// directory in the mcuboot repo was replaced with a submodule.  Git is unable
+// to transition from a post-replace commit to a pre-replace commit because
+// some files in the submodule used to exist in the mcuboot repo itself.  If
+// this issue is detected, this function deletes the submodule directory so
+// that the checkout operation can be attempted again.
+func applyMcubootPreHack(repoDir string, err error) error {
+	if !strings.HasSuffix(repoDir, "repos/mcuboot") {
+		// Not the mcuboot repo.
+		return err
+	}
+
+	// Check for an "untracked files" error.
+	files := untrackedFilesFromCheckoutErr(err)
+	if len(files) == 0 {
+		// Not a hackable error.
+		return err
+	}
+
+	for _, file := range files {
+		if !strings.HasPrefix(file, "ext/mbedtls") {
+			return err
+		}
+	}
+
+	path := repoDir + "/ext/mbedtls"
+	log.Debugf("applying mcuboot hack: removing %s", path)
+	os.RemoveAll(path)
+	return nil
+}
+
+// applyMcubootPostHack attempts to clean up the mcuboot repo so that the
+// subsequent checkout operation will succeed.  This hack is required because a
+// directory in the mcuboot repo was replaced with a submodule.  This hack
+// should be applied after a successful checkout from a pre-replace commit to a
+// post-replace commit.  This function deletes an orphan directory left behind
+// by the checkout operation.
+func applyMcubootPostHack(repoDir string, output string) {
+	if !strings.HasSuffix(repoDir, "repos/mcuboot") {
+		// Not the mcuboot repo.
+		return
+	}
+
+	// Check for a "unable to rmdir" warning (pre- to post- submodule move).
+	if strings.Contains(output, "unable to rmdir 'sim/mcuboot-sys/mbedtls'") {
+		path := repoDir + "/sim/mcuboot-sys/mbedtls"
+		log.Debugf("applying mcuboot hack: removing %s", path)
+		os.RemoveAll(path)
+	}
+}
+
 func (gd *GenericDownloader) Checkout(repoDir string, commit string) error {
 	// Get the hash corresponding to the commit in case the caller specified a
 	// branch or tag.  We always want to check out a hash and end up in a
@@ -380,8 +461,17 @@
 		hash,
 	}
 
-	if _, err := executeGitCommand(repoDir, cmd, true); err != nil {
-		return err
+	o, err := executeGitCommand(repoDir, cmd, true)
+	if err != nil {
+		if err := applyMcubootPreHack(repoDir, err); err != nil {
+			return err
+		}
+
+		if _, err := executeGitCommand(repoDir, cmd, true); err != nil {
+			return err
+		}
+	} else {
+		applyMcubootPostHack(repoDir, string(o))
 	}
 
 	// Always initialize and update submodules on checkout.  This prevents the