Sometimes it’s helpful to copy a Git repository directly from one machine to another without using a Git host like GitHub. If you can SSH into the machine(s) involved, you can use Bash’s rsync command. Rsync’s defaults aren’t great for copying repos for several reasons, but a Bash script can fix that.
I named the script ,cp-repo (copy repo):
,cp-repo . staging:/home/chris/repos/url-shortener
This copies the current directory (.) to /home/chris/repos/url-shortener in a machine named staging as defined in an SSH config file. The first argument is the source, and the second is the destination. Files and folders ignored by Git are not copied. This command can also be used to make a local copy of a local repo by not specifying any remote host.
Here’s the contents of my ,cp-repo file:
#!/usr/bin/env bash
set -euo pipefail
# Copies a Git repo either locally or to/from another machine.
# Arguments: source and destination. Files and folders ignored by Git
# are not copied.
# https://chriswheeler.dev/posts/copy-repos-between-devices-using-bash/
if [ $# -ne 2 ]; then
echo "Error: expected two arguments: source and destination" >&2
exit 1
fi
rsync --recursive --compress --rsh=ssh --perms --times --group \
--exclude-from=<(git -C "$1" ls-files --exclude-standard --others --ignored --directory) \
"$1" "$2"
I learned most of how to write this by combining a few answers in this StackOverflow discussion.
--recursiverecursively copies all directories--compresscompresses the data before sending, making the transfer significantly faster--rsh=sshtells rsync to make the connection over SSH to ensure encryption--permspreserves permissions--timespreserves modification times--grouppreserves group--exclude-frommakes rsync ignore files & folders listed in a file<(command)is a process substitution; it’s replaced by the name of a file containing the command’s outputgit -C "$1"runs the Git command in the folder specified by$1git -C "$1" ls-files --exclude-standard --others --ignored --directorylists all files and folders in the repo that are being ignored by Git
I chose to start the ,cp-repo command’s name with a comma because that makes it much less likely to conflict with future commands as explained in Start all of your commands with a comma.