Repository navigation
Expand file tree
/
Copy pathwt-copy-from-base
More file actions
executable file
·149 lines (125 loc) · 3.59 KB
/
Copy pathwt-copy-from-base
File metadata and controls
executable file
·149 lines (125 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF' >&2
Usage: wt-copy-from-base <base_worktree_path> <worktree_path>
Copies the base worktree working directory into the target worktree, excluding
.git. Prefers fast copy-on-write (CoW) methods when available.
On macOS, prefers `cp -c` (clonefile); on Linux, prefers `cp --reflink=auto`.
Env:
WT_COPY_EXCLUDES Space-separated paths to exclude (e.g. "node_modules .cache").
EOF
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if [[ $# -ne 2 ]]; then
usage
exit 2
fi
base_worktree_path="$1"
worktree_path="$2"
if [[ ! -d "$base_worktree_path" ]]; then
echo "wt-copy-from-base: base_worktree_path is not a directory: $base_worktree_path" >&2
exit 2
fi
if [[ ! -d "$worktree_path" ]]; then
echo "wt-copy-from-base: worktree_path is not a directory: $worktree_path" >&2
exit 2
fi
# Excludes are interpreted relative to the worktree root.
excludes=(.git)
if [[ -n "${WT_COPY_EXCLUDES:-}" ]]; then
# shellcheck disable=SC2206
excludes+=(${WT_COPY_EXCLUDES})
fi
# New git worktrees already contain a .git file/dir; never overwrite it.
clean_target_except_git() {
find "$worktree_path" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
}
copy_with_ditto_clones() {
command -v ditto >/dev/null 2>&1 || return 1
ditto --help 2>&1 | grep -q -- '--clones' || return 1
clean_target_except_git
# On APFS, --clones uses copy-on-write. On other filesystems, it falls back to a normal copy.
local -a args
args=(--clones)
local exclude
for exclude in "${excludes[@]}"; do
args+=(--exclude "$exclude")
done
ditto "${args[@]}" "$base_worktree_path/" "$worktree_path/" || return 1
}
copy_with_macos_clonefile() {
[[ "$(uname -s)" == "Darwin" ]] || return 1
command -v cp >/dev/null 2>&1 || return 1
clean_target_except_git
shopt -s dotglob nullglob
local item
local base_name
local exclude
for item in "$base_worktree_path"/*; do
base_name="$(basename "$item")"
for exclude in "${excludes[@]}"; do
[[ "$base_name" == "$exclude" ]] && continue 2
done
# `-c` uses clonefile(2) (copy-on-write) when available (e.g. APFS on the same volume).
cp -c -R -p "$item" "$worktree_path/" || return 1
done
}
copy_with_ditto() {
command -v ditto >/dev/null 2>&1 || return 1
clean_target_except_git
local -a args
args=()
local exclude
for exclude in "${excludes[@]}"; do
args+=(--exclude "$exclude")
done
ditto "${args[@]}" "$base_worktree_path/" "$worktree_path/" || return 1
}
copy_with_cp_reflink() {
command -v cp >/dev/null 2>&1 || return 1
cp --help 2>/dev/null | grep -q -- '--reflink' || return 1
clean_target_except_git
shopt -s dotglob nullglob
local item
local base_name
local exclude
for item in "$base_worktree_path"/*; do
base_name="$(basename "$item")"
for exclude in "${excludes[@]}"; do
[[ "$base_name" == "$exclude" ]] && continue 2
done
cp -a --reflink=auto "$item" "$worktree_path/" || return 1
done
}
copy_with_rsync() {
command -v rsync >/dev/null 2>&1 || return 1
clean_target_except_git
local -a args
args=(-a)
local exclude
for exclude in "${excludes[@]}"; do
args+=(--exclude "$exclude")
done
rsync "${args[@]}" "$base_worktree_path/" "$worktree_path/" || return 1
}
if copy_with_macos_clonefile; then
exit 0
fi
if copy_with_ditto_clones; then
exit 0
fi
if copy_with_ditto; then
exit 0
fi
if copy_with_cp_reflink; then
exit 0
fi
if copy_with_rsync; then
exit 0
fi
echo "wt-copy-from-base: no supported copy method found (need macOS cp, ditto, GNU cp with reflink, or rsync)" >&2
exit 127