I recently moved a directory from one repo to another, and preserved both commit history and last-modified times.
Summarizing what I've learned from a few different sources. These commands should be run in Linux; there is likely a PowerShell equivalent but it was not forthcoming. (Speaking of sharing a repo across platforms, adding a .gitattributes with * text=auto to handle newline characters has worked well for me).
Let's say we want to move "dir/directory_src" of "repo_one" to "dir/directory_dest" of "repo_two".
First let's move all files from directory_src to directory_dest and remove all files outside of directory_dest.
git clone https://servername/repo_one
# intentionally prevent ourselves from pushing changes
git remote rm origin
# run the following and look at the resulting filenames to see if they look correct
git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-"
# rewrite history so that "dir/directory_src" moves to "dir/directory_dest"
git filter-branch --index-filter 'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
git filter-branch --index-filter 'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && if [ -f "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' HEAD
(move or delete the directory .git/refs/original)
# run the following and look that files in dir/directory_dest aren't included
git ls-files | egrep -v ^dir/directory_dest/
# rewrite history and remove all other files
git filter-branch --tree-filter 'rm -rf $(git ls-files | egrep -v ^dir/directory_dest/)' -- --all
# delete empty commits (optional)
git filter-branch --commit-filter 'git_commit_non_empty_tree "$@"' HEAD
Now let's copy the files into repo_two.
git clone https://servername/repo_two
git remote add from_repo_one ~/repos/repo_one
git pull from_repo_one master
git remote rm from_repo_one
# after confirming that everything looks right,
# run git push.
"repo_two" should now contain "dir/directory_dest" and all of its contents.
Note: the tree filter removal script will leave filenames that contain spaces or tabs.