Allow --dest to be an absolute path
This means it might live outside of --root.
This commit is contained in:
parent
d8bd262b87
commit
4fc67f5f5f
|
|
@ -67,7 +67,7 @@ var flSubmodules = pflag.String("submodules", envString("GIT_SYNC_SUBMODULES", "
|
||||||
var flRoot = pflag.String("root", envString("GIT_SYNC_ROOT", ""),
|
var flRoot = pflag.String("root", envString("GIT_SYNC_ROOT", ""),
|
||||||
"the root directory for git-sync operations, under which --link will be created")
|
"the root directory for git-sync operations, under which --link will be created")
|
||||||
var flLink = pflag.String("link", envString("GIT_SYNC_LINK", ""),
|
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", ""),
|
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)")
|
"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),
|
var flPeriod = pflag.Duration("period", envDuration("GIT_SYNC_PERIOD", 10*time.Second),
|
||||||
|
|
@ -328,10 +328,10 @@ func main() {
|
||||||
parts := strings.Split(strings.Trim(*flRepo, "/"), "/")
|
parts := strings.Split(strings.Trim(*flRepo, "/"), "/")
|
||||||
*flLink = parts[len(parts)-1]
|
*flLink = parts[len(parts)-1]
|
||||||
}
|
}
|
||||||
if strings.Contains(*flLink, "/") {
|
if !filepath.IsAbs(*flLink) {
|
||||||
handleError(log, true, "ERROR: --link must not contain '/'")
|
*flLink = filepath.Join(*flRoot, *flLink)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(*flLink, ".") {
|
if strings.HasPrefix(filepath.Base(*flLink), ".") {
|
||||||
handleError(log, true, "ERROR: --link must not start with '.'")
|
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
|
// directory and cleans up the previous worktree. If there was a previous
|
||||||
// worktree, this returns the path to it.
|
// worktree, this returns the path to it.
|
||||||
func (git *repoSync) UpdateSymlink(ctx context.Context, newDir string) (string, error) {
|
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
|
// Get currently-linked repo directory (to be removed), unless it doesn't exist
|
||||||
linkPath := filepath.Join(git.root, git.link)
|
oldWorktreePath, err := filepath.EvalSymlinks(git.link)
|
||||||
oldWorktreePath, err := filepath.EvalSymlinks(linkPath)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return "", fmt.Errorf("error accessing current worktree: %v", 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
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error converting to relative path: %v", err)
|
return "", fmt.Errorf("error converting to relative path: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tmplink = "tmp-link"
|
const tmplink = "tmp-link"
|
||||||
git.log.V(1).Info("creating tmp symlink", "root", git.root, "dst", newDirRelative, "src", tmplink)
|
git.log.V(1).Info("creating tmp symlink", "root", linkDir, "dst", newDirRelative, "src", tmplink)
|
||||||
if _, err := git.run.Run(ctx, git.root, "ln", "-snf", newDirRelative, tmplink); err != nil {
|
if _, err := git.run.Run(ctx, linkDir, "ln", "-snf", newDirRelative, tmplink); err != nil {
|
||||||
return "", fmt.Errorf("error creating symlink: %v", err)
|
return "", fmt.Errorf("error creating symlink: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
git.log.V(1).Info("renaming symlink", "root", git.root, "oldName", tmplink, "newName", git.link)
|
git.log.V(1).Info("renaming symlink", "root", linkDir, "oldName", tmplink, "newName", linkFile)
|
||||||
if _, err := git.run.Run(ctx, git.root, "mv", "-T", tmplink, git.link); err != nil {
|
if _, err := git.run.Run(ctx, linkDir, "mv", "-T", tmplink, linkFile); err != nil {
|
||||||
return "", fmt.Errorf("error replacing symlink: %v", err)
|
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()
|
askpassCount.WithLabelValues(metricKeySuccess).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
target := filepath.Join(git.root, git.link)
|
gitRepoPath := filepath.Join(git.root, ".git")
|
||||||
gitRepoPath := filepath.Join(target, ".git")
|
|
||||||
var hash string
|
var hash string
|
||||||
_, err := os.Stat(gitRepoPath)
|
_, err := os.Stat(gitRepoPath)
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -1646,11 +1653,14 @@ OPTIONS
|
||||||
--http-bind). (default: false)
|
--http-bind). (default: false)
|
||||||
|
|
||||||
--link <string>, $GIT_SYNC_LINK
|
--link <string>, $GIT_SYNC_LINK
|
||||||
The name of the final symlink (under --root) which will point to the
|
The path to at which to create a symlink which points to the
|
||||||
current git worktree. This must be a filename, not a path, and may
|
current git directory, at the currently synced SHA. This may be an
|
||||||
not start with a period. The destination of this link (i.e.
|
absolute path or a relative path, in which case it is relative to
|
||||||
readlink()) is the currently checked out SHA. (default: the leaf
|
--root. The last path element is the name of the link and must not
|
||||||
dir of --repo)
|
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
|
--man
|
||||||
Print this manual and exit.
|
Print this manual and exit.
|
||||||
|
|
|
||||||
42
test_e2e.sh
42
test_e2e.sh
|
|
@ -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, but not ar ref we need
|
||||||
## FIXME: test when repo is valid git, and is already correct
|
## 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
|
# Test default-branch syncing
|
||||||
##############################################
|
##############################################
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue