Allow --dest to be an absolute path

This means it might live outside of --root.
This commit is contained in:
Tim Hockin 2022-01-22 23:39:56 -08:00
parent d8bd262b87
commit 4fc67f5f5f
2 changed files with 88 additions and 36 deletions

View File

@ -67,7 +67,7 @@ var flSubmodules = pflag.String("submodules", envString("GIT_SYNC_SUBMODULES", "
var flRoot = pflag.String("root", envString("GIT_SYNC_ROOT", ""),
"the root directory for git-sync operations, under which --link will be created")
var flLink = pflag.String("link", envString("GIT_SYNC_LINK", ""),
"the name of a symlink, under --root, which points to a directory in which --repo is checked out (defaults to the leaf dir of --repo)")
"the path (absolute or relative to --root) at which to create a symlink to the directory holding the checked-out files (defaults to the leaf dir of --repo)")
var flErrorFile = pflag.String("error-file", envString("GIT_SYNC_ERROR_FILE", ""),
"the name of a file into which errors will be written under --root (defaults to \"\", disabling error reporting)")
var flPeriod = pflag.Duration("period", envDuration("GIT_SYNC_PERIOD", 10*time.Second),
@ -328,10 +328,10 @@ func main() {
parts := strings.Split(strings.Trim(*flRepo, "/"), "/")
*flLink = parts[len(parts)-1]
}
if strings.Contains(*flLink, "/") {
handleError(log, true, "ERROR: --link must not contain '/'")
if !filepath.IsAbs(*flLink) {
*flLink = filepath.Join(*flRoot, *flLink)
}
if strings.HasPrefix(*flLink, ".") {
if strings.HasPrefix(filepath.Base(*flLink), ".") {
handleError(log, true, "ERROR: --link must not start with '.'")
}
@ -829,28 +829,36 @@ func addUser() error {
// directory and cleans up the previous worktree. If there was a previous
// worktree, this returns the path to it.
func (git *repoSync) UpdateSymlink(ctx context.Context, newDir string) (string, error) {
linkDir, linkFile := filepath.Split(git.link)
// Make sure the link directory exists. We do this here, rather than at
// startup because it might be under --root and that gets wiped in some
// circumstances.
if err := os.MkdirAll(filepath.Dir(linkDir), os.FileMode(int(0755))); err != nil {
return "", fmt.Errorf("error making symlink dir: %v", err)
}
// Get currently-linked repo directory (to be removed), unless it doesn't exist
linkPath := filepath.Join(git.root, git.link)
oldWorktreePath, err := filepath.EvalSymlinks(linkPath)
oldWorktreePath, err := filepath.EvalSymlinks(git.link)
if err != nil && !os.IsNotExist(err) {
return "", fmt.Errorf("error accessing current worktree: %v", err)
}
// newDir is absolute, so we need to change it to a relative path. This is
// so it can be volume-mounted at another path and the symlink still works.
newDirRelative, err := filepath.Rel(git.root, newDir)
newDirRelative, err := filepath.Rel(linkDir, newDir)
if err != nil {
return "", fmt.Errorf("error converting to relative path: %v", err)
}
const tmplink = "tmp-link"
git.log.V(1).Info("creating tmp symlink", "root", git.root, "dst", newDirRelative, "src", tmplink)
if _, err := git.run.Run(ctx, git.root, "ln", "-snf", newDirRelative, tmplink); err != nil {
git.log.V(1).Info("creating tmp symlink", "root", linkDir, "dst", newDirRelative, "src", tmplink)
if _, err := git.run.Run(ctx, linkDir, "ln", "-snf", newDirRelative, tmplink); err != nil {
return "", fmt.Errorf("error creating symlink: %v", err)
}
git.log.V(1).Info("renaming symlink", "root", git.root, "oldName", tmplink, "newName", git.link)
if _, err := git.run.Run(ctx, git.root, "mv", "-T", tmplink, git.link); err != nil {
git.log.V(1).Info("renaming symlink", "root", linkDir, "oldName", tmplink, "newName", linkFile)
if _, err := git.run.Run(ctx, linkDir, "mv", "-T", tmplink, linkFile); err != nil {
return "", fmt.Errorf("error replacing symlink: %v", err)
}
@ -1188,8 +1196,7 @@ func (git *repoSync) SyncRepo(ctx context.Context) (bool, string, error) {
askpassCount.WithLabelValues(metricKeySuccess).Inc()
}
target := filepath.Join(git.root, git.link)
gitRepoPath := filepath.Join(target, ".git")
gitRepoPath := filepath.Join(git.root, ".git")
var hash string
_, err := os.Stat(gitRepoPath)
switch {
@ -1646,11 +1653,14 @@ OPTIONS
--http-bind). (default: false)
--link <string>, $GIT_SYNC_LINK
The name of the final symlink (under --root) which will point to the
current git worktree. This must be a filename, not a path, and may
not start with a period. The destination of this link (i.e.
readlink()) is the currently checked out SHA. (default: the leaf
dir of --repo)
The path to at which to create a symlink which points to the
current git directory, at the currently synced SHA. This may be an
absolute path or a relative path, in which case it is relative to
--root. The last path element is the name of the link and must not
start with a period. Consumers of the synced files should always
use this link. It is updated atomically and should always be
valid. The basename of the target of the link is the current SHA).
(default: the leaf dir of --repo)
--man
Print this manual and exit.

View File

@ -351,6 +351,48 @@ function e2e::head_once_root_exists_but_fails_sanity() {
## FIXME: test when repo is valid git, but not ar ref we need
## FIXME: test when repo is valid git, and is already correct
##############################################
# Test HEAD one-time with an absolute-path link
##############################################
function e2e::absolute_link() {
echo "$FUNCNAME" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME"
GIT_SYNC \
--one-time \
--repo="file://$REPO" \
--branch="$MAIN_BRANCH" \
--rev="HEAD" \
--root="$ROOT/root" \
--link="$ROOT/other/dir/link" \
>> "$1" 2>&1
assert_file_absent "$ROOT"/root/link
assert_link_exists "$ROOT"/other/dir/link
assert_file_exists "$ROOT"/other/dir/link/file
assert_file_eq "$ROOT"/other/dir/link/file "$FUNCNAME"
}
##############################################
# Test HEAD one-time with a subdir-path link
##############################################
function e2e::subdir_link() {
echo "$FUNCNAME" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME"
GIT_SYNC \
--one-time \
--repo="file://$REPO" \
--branch="$MAIN_BRANCH" \
--rev="HEAD" \
--root="$ROOT" \
--link="other/dir/link" \
>> "$1" 2>&1
assert_file_absent "$ROOT"/link
assert_link_exists "$ROOT"/other/dir/link
assert_file_exists "$ROOT"/other/dir/link/file
assert_file_eq "$ROOT"/other/dir/link/file "$FUNCNAME"
}
##############################################
# Test default-branch syncing
##############################################