commit - 2f63b34ce3d2db78052d19284337414845664b6f
commit + bd6aa3590530fb1684f2ca8ded49f431bf0e9a18
blob - 4f2a83243d33c26ba5cf655c2b3e82a61f8d5517
blob + 8d06a93d5428544c51f0757dc50b19a388cb67d8
--- lib/worktree.c
+++ lib/worktree.c
install_blob(struct got_worktree *worktree, const char *ondisk_path,
const char *path, mode_t te_mode, mode_t st_mode,
struct got_blob_object *blob, int restoring_missing_file,
- int reverting_versioned_file, struct got_repository *repo,
+ int reverting_versioned_file, int installing_bad_symlink,
+ struct got_repository *repo,
got_worktree_checkout_cb progress_cb, void *progress_arg);
static const struct got_error *
return install_blob(worktree, ondisk_path, path,
GOT_DEFAULT_FILE_MODE, st_mode, blob,
restoring_missing_file, reverting_versioned_file,
- repo, progress_cb, progress_arg);
+ 1, repo, progress_cb, progress_arg);
}
if (len > 0) {
/* Skip blob object header first time around. */
got_object_blob_rewind(blob);
err = install_blob(worktree, ondisk_path, path,
GOT_DEFAULT_FILE_MODE, st_mode, blob,
- restoring_missing_file, reverting_versioned_file,
+ restoring_missing_file, reverting_versioned_file, 1,
repo, progress_cb, progress_arg);
goto done;
}
got_object_blob_rewind(blob);
err = install_blob(worktree, ondisk_path, path,
GOT_DEFAULT_FILE_MODE, st_mode, blob,
- restoring_missing_file, reverting_versioned_file,
+ restoring_missing_file, reverting_versioned_file, 1,
repo, progress_cb, progress_arg);
goto done;
}
target_len);
if (err)
goto done;
- err = (*progress_cb)(progress_arg,
- GOT_STATUS_UPDATE, path);
+ if (progress_cb) {
+ err = (*progress_cb)(progress_arg,
+ GOT_STATUS_UPDATE, path);
+ }
goto done;
}
}
got_object_blob_rewind(blob);
err = install_blob(worktree, ondisk_path, path,
GOT_DEFAULT_FILE_MODE, st_mode, blob,
- restoring_missing_file, reverting_versioned_file,
+ restoring_missing_file, reverting_versioned_file, 1,
repo, progress_cb, progress_arg);
} else if (errno == ENOTDIR) {
err = got_error_path(ondisk_path,
err = got_error_from_errno3("symlink",
target_path, ondisk_path);
}
- } else
+ } else if (progress_cb)
err = (*progress_cb)(progress_arg, GOT_STATUS_ADD, path);
done:
free(resolved_path);
install_blob(struct got_worktree *worktree, const char *ondisk_path,
const char *path, mode_t te_mode, mode_t st_mode,
struct got_blob_object *blob, int restoring_missing_file,
- int reverting_versioned_file, struct got_repository *repo,
- got_worktree_checkout_cb progress_cb, void *progress_arg)
+ int reverting_versioned_file, int installing_bad_symlink,
+ struct got_repository *repo, got_worktree_checkout_cb progress_cb,
+ void *progress_arg)
{
const struct got_error *err = NULL;
int fd = -1;
return got_error_from_errno2("open",
ondisk_path);
} else if (errno == EEXIST) {
- if (!S_ISREG(st_mode)) {
+ if (!S_ISREG(st_mode) && !installing_bad_symlink) {
/* TODO file is obstructed; do something */
err = got_error_path(ondisk_path,
GOT_ERR_FILE_OBSTRUCTED);
return got_error_from_errno2("open", ondisk_path);
}
- if (restoring_missing_file)
- err = (*progress_cb)(progress_arg, GOT_STATUS_MISSING, path);
- else if (reverting_versioned_file)
- err = (*progress_cb)(progress_arg, GOT_STATUS_REVERT, path);
- else
- err = (*progress_cb)(progress_arg,
- update ? GOT_STATUS_UPDATE : GOT_STATUS_ADD, path);
- if (err)
- goto done;
+ if (progress_cb) {
+ if (restoring_missing_file)
+ err = (*progress_cb)(progress_arg, GOT_STATUS_MISSING,
+ path);
+ else if (reverting_versioned_file)
+ err = (*progress_cb)(progress_arg, GOT_STATUS_REVERT,
+ path);
+ else
+ err = (*progress_cb)(progress_arg,
+ update ? GOT_STATUS_UPDATE : GOT_STATUS_ADD, path);
+ if (err)
+ goto done;
+ }
hdrlen = got_object_blob_get_hdrlen(blob);
do {
goto done;
} else {
err = install_blob(worktree, ondisk_path, path, te->mode,
- sb.st_mode, blob, status == GOT_STATUS_MISSING, 0,
+ sb.st_mode, blob, status == GOT_STATUS_MISSING, 0, 0,
repo, progress_cb, progress_arg);
if (err)
goto done;
err = install_blob(a->worktree, ondisk_path, path2,
/* XXX get this from parent tree! */
GOT_DEFAULT_FILE_MODE,
- sb.st_mode, blob2, 0, 0, repo,
+ sb.st_mode, blob2, 0, 0, 0, repo,
a->progress_cb, a->progress_arg);
if (err)
goto done;
} else {
err = install_blob(a->worktree, ondisk_path, ie->path,
te ? te->mode : GOT_DEFAULT_FILE_MODE,
- got_fileindex_perms_to_st(ie), blob, 0, 1,
+ got_fileindex_perms_to_st(ie), blob, 0, 1, 0,
a->repo, a->progress_cb, a->progress_arg);
if (err)
goto done;
goto done;
}
err = got_object_blob_create(&ct->blob_id, ondisk_path, repo);
- free(ondisk_path);
- if (err)
+ if (err) {
+ free(ondisk_path);
goto done;
+ }
+
+ /*
+ * When comitting a symlink we convert "bad" symlinks (those
+ * which point outside the work tree or into .got) to regular
+ * files. This way, the post-commit work tree state matches
+ * a fresh checkout of the tree which was committed.
+ */
+ if (S_ISLNK(get_ct_file_mode(ct))) {
+ struct got_blob_object *blob;
+ err = got_object_open_as_blob(&blob, repo, ct->blob_id,
+ PATH_MAX);
+ if (err) {
+ free(ondisk_path);
+ goto done;
+ }
+ err = install_symlink(worktree, ondisk_path, ct->path,
+ get_ct_file_mode(ct), GOT_DEFAULT_FILE_MODE, blob,
+ 0, 0, repo, NULL, NULL);
+ got_object_blob_close(blob);
+ if (err) {
+ free(ondisk_path);
+ goto done;
+ }
+ }
+ free(ondisk_path);
}
/* Recursively write new tree objects. */
blob - 9bf3085d0b6f3078e98d58aaff1f4e4e4525bff9
blob + 45cd97ff9844d5541bd8f229053371132ae23913
--- regress/cmdline/commit.sh
+++ regress/cmdline/commit.sh
test_done "$testroot" "$ret"
}
-function test_commit_symlink {
- local testroot=`test_init commit_symlink`
+function check_symlinks {
+ local wtpath="$1"
+ if ! [ -h $wtpath/alpha.link ]; then
+ echo "alpha.link is not a symlink"
+ return 1
+ fi
- got checkout $testroot/repo $testroot/wt > /dev/null
+ readlink $wtpath/alpha.link > $testroot/stdout
+ echo "alpha" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
- test_done "$testroot" "$ret"
+ diff -u $testroot/stdout.expected $testroot/stdout
return 1
fi
- (cd $testroot/wt && ln -s alpha alpha.link)
- (cd $testroot/wt && ln -s epsilon epsilon.link)
- (cd $testroot/wt && ln -s /etc/passwd passwd.link)
- (cd $testroot/wt && ln -s ../beta epsilon/beta.link)
- (cd $testroot/wt && ln -s nonexistent nonexistent.link)
- (cd $testroot/wt && got add alpha.link epsilon.link passwd.link \
- epsilon/beta.link nonexistent.link > /dev/null)
-
- (cd $testroot/wt && got commit -m 'test commit_symlink' > $testroot/stdout)
-
- local head_rev=`git_show_head $testroot/repo`
- echo "A alpha.link" > $testroot/stdout.expected
- echo "A epsilon.link" >> $testroot/stdout.expected
- echo "A nonexistent.link" >> $testroot/stdout.expected
- echo "A passwd.link" >> $testroot/stdout.expected
- echo "A epsilon/beta.link" >> $testroot/stdout.expected
- echo "Created commit $head_rev" >> $testroot/stdout.expected
+ if ! [ -h $wtpath/epsilon.link ]; then
+ echo "epsilon.link is not a symlink"
+ return 1
+ fi
+ readlink $wtpath/epsilon.link > $testroot/stdout
+ echo "epsilon" > $testroot/stdout.expected
cmp -s $testroot/stdout.expected $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
diff -u $testroot/stdout.expected $testroot/stdout
- test_done "$testroot" "$ret"
return 1
fi
- got checkout $testroot/repo $testroot/wt2 > /dev/null
+ if [ -h $wtpath/passwd.link ]; then
+ echo -n "passwd.link is a symlink and points outside of work tree: " >&2
+ readlink $wtpath/passwd.link >&2
+ return 1
+ fi
+
+ echo -n "/etc/passwd" > $testroot/content.expected
+ cp $wtpath/passwd.link $testroot/content
ret="$?"
if [ "$ret" != "0" ]; then
- test_done "$testroot" "$ret"
+ echo "cp command failed unexpectedly" >&2
return 1
fi
- if ! [ -h $testroot/wt2/alpha.link ]; then
- echo "alpha.link is not a symlink"
- test_done "$testroot" "1"
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
return 1
fi
- readlink $testroot/wt2/alpha.link > $testroot/stdout
- echo "alpha" > $testroot/stdout.expected
+ readlink $wtpath/epsilon/beta.link > $testroot/stdout
+ echo "../beta" > $testroot/stdout.expected
cmp -s $testroot/stdout.expected $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
diff -u $testroot/stdout.expected $testroot/stdout
- test_done "$testroot" "$ret"
return 1
fi
- if ! [ -h $testroot/wt2/epsilon.link ]; then
- echo "epsilon.link is not a symlink"
- test_done "$testroot" "1"
- return 1
- fi
-
- readlink $testroot/wt2/epsilon.link > $testroot/stdout
- echo "epsilon" > $testroot/stdout.expected
+ readlink $wtpath/nonexistent.link > $testroot/stdout
+ echo "nonexistent" > $testroot/stdout.expected
cmp -s $testroot/stdout.expected $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
diff -u $testroot/stdout.expected $testroot/stdout
- test_done "$testroot" "$ret"
return 1
fi
- if [ -h $testroot/wt2/passwd.link ]; then
- echo -n "passwd.link symlink points outside of work tree: " >&2
- readlink $testroot/wt2/passwd.link >&2
- test_done "$testroot" "1"
- return 1
- fi
+ return 0
+}
- echo -n "/etc/passwd" > $testroot/content.expected
- cp $testroot/wt2/passwd.link $testroot/content
+function test_commit_symlink {
+ local testroot=`test_init commit_symlink`
- cmp -s $testroot/content.expected $testroot/content
+ got checkout $testroot/repo $testroot/wt > /dev/null
ret="$?"
if [ "$ret" != "0" ]; then
- diff -u $testroot/content.expected $testroot/content
test_done "$testroot" "$ret"
return 1
fi
- readlink $testroot/wt2/epsilon/beta.link > $testroot/stdout
- echo "../beta" > $testroot/stdout.expected
+ (cd $testroot/wt && ln -s alpha alpha.link)
+ (cd $testroot/wt && ln -s epsilon epsilon.link)
+ (cd $testroot/wt && ln -s /etc/passwd passwd.link)
+ (cd $testroot/wt && ln -s ../beta epsilon/beta.link)
+ (cd $testroot/wt && ln -s nonexistent nonexistent.link)
+ (cd $testroot/wt && got add alpha.link epsilon.link passwd.link \
+ epsilon/beta.link nonexistent.link > /dev/null)
+
+ (cd $testroot/wt && got commit -m 'test commit_symlink' > $testroot/stdout)
+ #(cd $testroot/wt && egdb --args got commit -m 'test commit_symlink')
+
+ local head_rev=`git_show_head $testroot/repo`
+ echo "A alpha.link" > $testroot/stdout.expected
+ echo "A epsilon.link" >> $testroot/stdout.expected
+ echo "A nonexistent.link" >> $testroot/stdout.expected
+ echo "A passwd.link" >> $testroot/stdout.expected
+ echo "A epsilon/beta.link" >> $testroot/stdout.expected
+ echo "Created commit $head_rev" >> $testroot/stdout.expected
+
cmp -s $testroot/stdout.expected $testroot/stdout
ret="$?"
if [ "$ret" != "0" ]; then
return 1
fi
- readlink $testroot/wt2/nonexistent.link > $testroot/stdout
- echo "nonexistent" > $testroot/stdout.expected
- cmp -s $testroot/stdout.expected $testroot/stdout
+ # verify created in-repository tree
+ got checkout $testroot/repo $testroot/wt2 > /dev/null
ret="$?"
if [ "$ret" != "0" ]; then
- diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
fi
- test_done "$testroot" "$ret"
+ check_symlinks $testroot/wt2
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # verify post-commit work tree state matches a fresh checkout
+ check_symlinks $testroot/wt
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ test_done "$testroot" "0"
}
run_test test_commit_basic