commit 4a1ddfc2ae992bd73e34ce89b7f309df643aeba5 from: Stefan Sperling date: Sat Jan 12 21:15:58 2019 UTC fix bug where update forgot to create a parent directory commit - 46cee7a3e76f2e44d2e0c5919de76d228b9c79af commit + 4a1ddfc2ae992bd73e34ce89b7f309df643aeba5 blob - 45ace73306c72d723185ef208bb2d4b51fd09546 blob + f51b680fd50600a7dde8e98ca7bb6f718b81089c --- lib/worktree.c +++ lib/worktree.c @@ -507,6 +507,40 @@ lock_worktree(struct got_worktree *worktree, int opera } static const struct got_error * +add_dir_on_disk(struct got_worktree *worktree, const char *path) +{ + const struct got_error *err = NULL; + char *abspath; + + if (asprintf(&abspath, "%s/%s", worktree->root_path, path) == -1) + return got_error_from_errno(); + + /* XXX queue work rather than editing disk directly? */ + if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1) { + struct stat sb; + + if (errno != EEXIST) { + err = got_error_from_errno(); + goto done; + } + + if (lstat(abspath, &sb) == -1) { + err = got_error_from_errno(); + goto done; + } + + if (!S_ISDIR(sb.st_mode)) { + /* TODO directory is obstructed; do something */ + return got_error(GOT_ERR_FILE_OBSTRUCTED); + } + } + +done: + free(abspath); + return err; +} + +static const struct got_error * install_blob(struct got_worktree *worktree, struct got_fileindex *fileindex, struct got_fileindex_entry *entry, const char *path, struct got_blob_object *blob, @@ -526,7 +560,19 @@ install_blob(struct got_worktree *worktree, struct got fd = open(ondisk_path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE); if (fd == -1) { - if (errno == EEXIST) { + if (errno == ENOENT) { + char *parent = dirname(path); + if (parent == NULL) + return got_error_from_errno(); + err = add_dir_on_disk(worktree, parent); + if (err) + return err; + fd = open(ondisk_path, + O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, + GOT_DEFAULT_FILE_MODE); + if (fd == -1) + return got_error_from_errno(); + } else if (errno == EEXIST) { struct stat sb; if (lstat(ondisk_path, &sb) == -1) { err = got_error_from_errno(); @@ -595,40 +641,6 @@ done: close(fd); free(ondisk_path); free(tmppath); - return err; -} - -static const struct got_error * -add_dir_on_disk(struct got_worktree *worktree, const char *path) -{ - const struct got_error *err = NULL; - char *abspath; - - if (asprintf(&abspath, "%s/%s", worktree->root_path, path) == -1) - return got_error_from_errno(); - - /* XXX queue work rather than editing disk directly? */ - if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1) { - struct stat sb; - - if (errno != EEXIST) { - err = got_error_from_errno(); - goto done; - } - - if (lstat(abspath, &sb) == -1) { - err = got_error_from_errno(); - goto done; - } - - if (!S_ISDIR(sb.st_mode)) { - /* TODO directory is obstructed; do something */ - return got_error(GOT_ERR_FILE_OBSTRUCTED); - } - } - -done: - free(abspath); return err; } blob - a8a3ea579510a1fb52d1335755b07681391a398d blob + a7e8448f37599141ef907df5d2159eaac83edf3e --- regress/cmdline/common.sh +++ regress/cmdline/common.sh @@ -55,6 +55,7 @@ function make_test_tree function test_init { local testname="$1" + local no_tree="$2" if [ -z "$testname" ]; then echo "No test name provided" >&2 return 1 @@ -62,8 +63,10 @@ function test_init local testroot=`mktemp -p /tmp -d got-test-$testname-XXXXXXXX` mkdir $testroot/repo git_init $testroot/repo - make_test_tree $testroot/repo - git_commit $testroot/repo -m "adding the test tree" + if [ -z "$no_tree" ]; then + make_test_tree $testroot/repo + git_commit $testroot/repo -m "adding the test tree" + fi echo "$testroot" } blob - 29675054ce70a9414adddb3bf89fd6f602397973 blob + b7d9d15172b1bc6145e019ed80dba37c3dea057e --- regress/cmdline/update.sh +++ regress/cmdline/update.sh @@ -458,6 +458,59 @@ function test_update_moves_files_to_new_dir { if [ -e $testroot/wt/epsilon/psi/mu ]; then echo "removed file epsilon/psi/mu still exists on disk" >&2 test_done "$testroot" "1" + return 1 + fi + + test_done "$testroot" "0" +} + +function test_update_creates_missing_parent { + local testroot=`test_init update_creates_missing_parent no_tree` + + touch $testroot/repo/Makefile + touch $testroot/repo/snake.6 + touch $testroot/repo/snake.c + (cd $testroot/repo && git add .) + git_commit $testroot/repo -m "adding initial snake tree" + + got checkout $testroot/repo $testroot/wt > /dev/null + if [ "$?" != "0" ]; then + test_done "$testroot" "$?" + return 1 + fi + + mkdir -p $testroot/repo/snake + (cd $testroot/repo && git mv Makefile snake.6 snake.c snake) + touch $testroot/repo/snake/move.c + touch $testroot/repo/snake/pathnames.h + touch $testroot/repo/snake/snake.h + mkdir -p $testroot/repo/snscore + touch $testroot/repo/snscore/Makefile + touch $testroot/repo/snscore/snscore.c + (cd $testroot/repo && git add .) + git_commit $testroot/repo -m "restructuring snake tree" + + echo "D Makefile" > $testroot/stdout.expected + echo "A snake/Makefile" >> $testroot/stdout.expected + echo "A snake/move.c" >> $testroot/stdout.expected + echo "A snake/pathnames.h" >> $testroot/stdout.expected + echo "A snake/snake.6" >> $testroot/stdout.expected + echo "A snake/snake.c" >> $testroot/stdout.expected + echo "A snake/snake.h" >> $testroot/stdout.expected + echo "D snake.6" >> $testroot/stdout.expected + echo "D snake.c" >> $testroot/stdout.expected + echo "A snscore/Makefile" >> $testroot/stdout.expected + echo "A snscore/snscore.c" >> $testroot/stdout.expected + echo -n "Updated to commit " >> $testroot/stdout.expected + git_show_head $testroot/repo >> $testroot/stdout.expected + echo >> $testroot/stdout.expected + + (cd $testroot/wt && got update > $testroot/stdout) + + cmp $testroot/stdout.expected $testroot/stdout + if [ "$?" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$?" return 1 fi @@ -474,3 +527,4 @@ run_test test_update_sibling_dirs_with_common_prefix run_test test_update_dir_with_dot_sibling run_test test_update_moves_files_upwards #run_test test_update_moves_files_to_new_dir # test is failing +run_test test_update_creates_missing_parent