forked from Kristian-Tan/git-worktree-relative
-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-worktree-relative.sh
executable file
·186 lines (155 loc) · 8.03 KB
/
git-worktree-relative.sh
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#! /bin/bash
OPTIND=1 # Reset in case getopts has been used previously in the shell.
# boilerplate
set -o errexit # exit when any command return non-zero exit code
set -o nounset # exit when using undeclared variables
exit_on_error() {
if test $# -eq 1; then
echo ">>> $1"
fi
exit 1
}
# default argument value
worktree_target=""
repository_target=""
verbose=0
dry_run=0
# read arguments from getopts
while getopts "hw:r:vd" opt; do
case "$opt" in
h)
cat << EOF
usage: [-w worktree_target] [-r repository_target] [-v] [-d]
-w worktree_target = directory of worktree to be made relative (will default to current directory if not supplied)
-r repository_target = directory of repository (including worktree directory inside .git, will be read from {worktree_target}/.git file if not supplied)
-v = verbose
-d = dry_run (do not write any change, use with verbose to show what this script do)
example:
1) repository in /home/myuser/repo/myproject ; worktree in /home/myuser/www/myproject ; worktree is connected with repository (link is not broken)
cd /home/myuser/www/myproject
git-worktree-relative
OR
git-worktree-relative -w /home/myuser/www/myproject
2) repository in /home/myuser/repo/myproject ; worktree in /home/myuser/www/myproject ; worktree is NOT connected with repository (link broken)
cd /home/myuser/www/myproject
git-worktree-relative -r /home/myuser/repo/myproject/.git/worktrees/myproject
OR
git-worktree-relative -w /home/myuser/www/myproject -r /home/myuser/repo/myproject/.git/worktrees/myproject
to detect if link is broken, run command 'git status' in worktree directory
IMPORTANT: if -r option is used, make sure to include worktree directory inside .git directory
CORRECT EXAMPLE: -r /home/myuser/repo/myproject/.git/worktrees/myprojectworktree
WRONG EXAMPLE: -r /home/myuser/repo/myproject
WHY IS IT IMPORTANT: a git worktree may have different name in .git directory, for example consider this setup
main repository: /home/myuser/repo/myproject (contains .git directory)
worktree 1 (for development, checked out for branch 'development'): /home/myuser/www/myproject (contains .git file)
worktree 2 (for production, checked out for branch 'production'): /var/www/html/myproject (contains .git file)
RESULT:
file /home/myuser/www/myproject/.git contains 'gitdir: /home/myuser/repo/myproject' (development branch)
file /var/www/html/myproject/.git contains 'gitdir: /home/myuser/repo/myproject' (production branch)
directory /home/myuser/repo/myproject/.git/worktrees contains 2 directory called 'myproject' and 'myproject1'
file /home/myuser/repo/myproject/.git/worktrees/myproject/gitdir contains '/home/myuser/www/myproject/.git'
file /home/myuser/repo/myproject/.git/worktrees/myproject1/gitdir contains '/var/www/html/myproject/.git'
EOF
exit 0
;;
w) worktree_target=$OPTARG
;;
r) repository_target=$OPTARG
;;
d) dry_run=1
;;
v) verbose=1
;;
esac
done
# declare verbose output function
# @param string $1
# Input string that should be printed if verbose is on
verbose_output()
{
if test $verbose -eq 1; then
{ printf '>>> %s ' "$@"; echo; } 1>&2
fi
}
if test "$worktree_target" = ""; then
verbose_output
verbose_output "fill argument with default value if empty"
worktree_target=`pwd`
fi
if test "$repository_target" = ""; then
verbose_output
verbose_output 'read content of file in "$worktree_target/.git", it should contain "gitdir: /home/kristian/repos/myrepo/.git/worktrees/myrepo_worktree1"'
verbose_output " \$ repository_target=\"\$(cat "$worktree_target"/.git)\""
if ! repository_target=$(cat "$worktree_target"/.git); then
echo 1>&2 "Could not read $worktree_target/.git, is this a worktree?"
exit 1
fi
verbose_output
verbose_output 'replace "gitdir: " with ""'
verbose_output " \$ repository_target=\"\${repository_target/gitdir: /}\""
repository_target="${repository_target/gitdir: /}"
verbose_output
verbose_output 'get absolute path of repository (with .git/{wtname})'
verbose_output " \$ repository_target=\`readlink -f "$repository_target"\`"
repository_target=`readlink -f "$repository_target"`
fi
verbose_output
verbose_output 'worktree_link_content should contain "/home/kristian/repos/myrepo/.git/worktrees/myrepo_worktree1"'
verbose_output " \$ worktree_link_content=\$repository_target"
worktree_link_content=$repository_target
string_slash_dot_git_slash="/.git/"
verbose_output
verbose_output 'remove all string after "/.git/", should contain "/home/kristian/repos/myrepo"'
verbose_output " \$ repository_target=\"\${worktree_link_content%%$string_slash_dot_git_slash*}\""
repository_target="${worktree_link_content%%$string_slash_dot_git_slash*}"
verbose_output
verbose_output 'reverse string for worktree_link_content'
verbose_output " \$ worktree_link_content_reversed=\`echo \"$worktree_link_content\" | rev\`"
worktree_link_content_reversed=`echo "$worktree_link_content" | rev`
verbose_output
verbose_output 'reverse string for string_slash_dot_git_slash'
verbose_output " \$ string_slash_dot_git_slash_reversed=\`echo \"$string_slash_dot_git_slash\" | rev\`"
string_slash_dot_git_slash_reversed=`echo "$string_slash_dot_git_slash" | rev`
verbose_output
verbose_output 'remove all string after (reversed)'
verbose_output " \$ worktree_name_inside_repository_reversed=\"\${worktree_link_content_reversed%%$string_slash_dot_git_slash_reversed*}\""
worktree_name_inside_repository_reversed="${worktree_link_content_reversed%%$string_slash_dot_git_slash_reversed*}"
verbose_output
verbose_output 'reverse back: should contain "worktrees/myrepo_worktree1"'
verbose_output " \$ worktree_name_inside_repository=\`echo \"$worktree_name_inside_repository_reversed\" | rev\`"
worktree_name_inside_repository=`echo "$worktree_name_inside_repository_reversed" | rev`
verbose_output
verbose_output 'get absolute path of repository'
verbose_output " \$ absolute_repository=\`readlink -f "$repository_target"\`"
absolute_repository=`readlink -f "$repository_target"`
verbose_output
verbose_output 'get absolute path of worktree'
verbose_output " \$ absolute_worktree=\`readlink -f "$worktree_target"\`"
absolute_worktree=`readlink -f "$worktree_target"`
verbose_output
verbose_output 'get relative path from worktree to repo'
verbose_output " \$ path_worktree_to_repo=\`realpath --relative-to="$absolute_worktree" "$absolute_repository"\`"
path_worktree_to_repo=`realpath --relative-to="$absolute_worktree" "$absolute_repository"`
verbose_output
verbose_output 'get relative path from repo to worktree'
verbose_output " \$ path_repo_to_worktree=\`realpath --relative-to="$absolute_repository" "$absolute_worktree"\`"
path_repo_to_worktree=`realpath --relative-to="$absolute_repository" "$absolute_worktree"`
#sed -i "s+$absolute_repository+$path_worktree_to_repo+g" "$absolute_worktree/.git" # replace with sed: before=absolute path to repository, after=relative path to repository, location={worktree}/.git file
#sed -i "s+$absolute_worktree+$path_repo_to_worktree+g" "$worktree_link_content/gitdir" # replace with sed: before=absolute path to worktree, after=relative path to worktree, location={repo}/.git/worktrees/{wtname}/gitdir file
# use echo instead of sed to write directly into file content
verbose_output
verbose_output 'overwrite {worktree_target}/.git file'
verbose_output " \$ echo \"gitdir: $path_worktree_to_repo/.git/$worktree_name_inside_repository\" > \"$absolute_worktree/.git\""
if test $dry_run -eq 0; then
echo "gitdir: $path_worktree_to_repo/.git/$worktree_name_inside_repository" > "$absolute_worktree/.git"
else
verbose_output "dry run: not running operation"
fi
verbose_output
verbose_output 'overwrite {repo}/.git/worktrees/{wtname}/gitdir file'
verbose_output " \$ echo \"$path_repo_to_worktree/.git\" > \"$worktree_link_content/gitdir\""
if test $dry_run -eq 0; then
echo "$path_repo_to_worktree/.git" > "$worktree_link_content/gitdir"
else
verbose_output "dry run: not running operation"
fi