This is rebased so the diff below (against the version on Junio's repo) is only approximate. Changes include test fixes for Windows port, $GIT_COMMON_DIR and $GIT_DIR/modules problems with submodules. Patch 03/34 is rewritten to touch less in refs.c to reduce conflicts. A lot of changes there are just revert. diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 2b30a92..7173b38 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -248,9 +248,7 @@ commondir:: incomplete without the repository pointed by "commondir". modules:: - Contains the git-repositories of the submodules. This - directory is ignored if $GIT_COMMON_DIR is set and - "$GIT_COMMON_DIR/modules" will be used instead. + Contains the git-repositories of the submodules. worktrees:: Contains worktree specific information of linked diff --git a/builtin/checkout.c b/builtin/checkout.c index 01d0f2f..e70e66c 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -865,7 +865,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (!new->commit) die(_("no branch specified")); - if (file_exists(path)) + if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); len = strlen(path); @@ -1207,7 +1207,7 @@ static int parse_branchname_arg(int argc, const char **argv, if (new->path && !force_detach && !*new_branch) { unsigned char sha1[20]; int flag; - char *head_ref = resolve_refdup("HEAD", sha1, 0, &flag); + char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); if (head_ref && (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) check_linked_checkouts(new); diff --git a/environment.c b/environment.c index d5b0788..8351007 100644 --- a/environment.c +++ b/environment.c @@ -101,6 +101,7 @@ const char * const local_repo_env[] = { NO_REPLACE_OBJECTS_ENVIRONMENT, GIT_PREFIX_ENVIRONMENT, GIT_SHALLOW_FILE_ENVIRONMENT, + GIT_COMMON_DIR_ENVIRONMENT, NULL }; diff --git a/path.c b/path.c index 35d498e..a5c51a3 100644 --- a/path.c +++ b/path.c @@ -92,7 +92,7 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir) } static const char *common_list[] = { - "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules", + "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn", "config", "!gc.pid", "packed-refs", "shallow", NULL diff --git a/refs.c b/refs.c index 3cefbd3..f7e48b0 100644 --- a/refs.c +++ b/refs.c @@ -1398,14 +1398,16 @@ static const char *handle_missing_loose_ref(const char *refname, } /* This function needs to return a meaningful errno on failure */ -const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag) +static const char *resolve_ref_unsafe_1(const char *refname, + unsigned char *sha1, + int reading, + int *flags, + struct strbuf *sb_path) { - struct strbuf sb_path = STRBUF_INIT; int depth = MAXDEPTH; ssize_t len; char buffer[256]; static char refname_buffer[256]; - const char *ret; if (flag) *flag = 0; @@ -1423,12 +1425,12 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea if (--depth < 0) { errno = ELOOP; - goto fail; + return NULL; } - strbuf_reset(&sb_path); - strbuf_git_path(&sb_path, "%s", refname); - path = sb_path.buf; + strbuf_reset(sb_path); + strbuf_git_path(sb_path, "%s", refname); + path = sb_path->buf; /* * We might have to loop back here to avoid a race @@ -1442,11 +1444,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea stat_ref: if (lstat(path, &st) < 0) { if (errno == ENOENT) - ret = handle_missing_loose_ref(refname, sha1, - reading, flag); + return handle_missing_loose_ref(refname, sha1, + reading, flag); else - ret = NULL; - goto done; + return NULL; } /* Follow "normalized" - ie "refs/.." symlinks by hand */ @@ -1457,7 +1458,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea /* inconsistent with lstat; retry */ goto stat_ref; else - goto fail; + return NULL; } buffer[len] = 0; if (starts_with(buffer, "refs/") && @@ -1473,7 +1474,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea /* Is it a directory? */ if (S_ISDIR(st.st_mode)) { errno = EISDIR; - goto fail; + return NULL; } /* @@ -1486,15 +1487,14 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea /* inconsistent with lstat; retry */ goto stat_ref; else - goto fail; + return NULL; } - len = read_in_full(fd, buffer, sizeof(buffer)-1); if (len < 0) { int save_errno = errno; close(fd); errno = save_errno; - goto fail; + return NULL; } close(fd); while (len && isspace(buffer[len-1])) @@ -1514,10 +1514,9 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea if (flag) *flag |= REF_ISBROKEN; errno = EINVAL; - goto fail; + return NULL; } - ret = refname; - goto done; + return refname; } if (flag) *flag |= REF_ISSYMREF; @@ -1528,13 +1527,17 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea if (flag) *flag |= REF_ISBROKEN; errno = EINVAL; - goto fail; + return NULL; } refname = strcpy(refname_buffer, buf); } -fail: - ret = NULL; -done: +} + +const char *resolve_ref_unsafe(const char *refname, int reading, + unsigned char *sha1, int *flags) +{ + struct strbuf sb_path = STRBUF_INIT; + const char *ret = resolve_ref_unsafe_1(refname, reading,, sha1, flags, &sb_path); strbuf_release(&sb_path); return ret; } @@ -2844,62 +2847,66 @@ static int copy_msg(char *buf, const char *msg) } /* This function must set a meaningful errno on failure */ -int log_ref_setup(const char *refname, struct strbuf *logfile) +int log_ref_setup(const char *refname, struct strbuf *sb_logfile) { int logfd, oflags = O_APPEND | O_WRONLY; + char *logfile; - strbuf_git_path(logfile, "logs/%s", refname); + strbuf_git_path(sb_logfile, "logs/%s", refname); + logfile = sb_logfile->buf; + /* make sure the rest of the function can't change "logfile" */ + sb_logfile = NULL; if (log_all_ref_updates && (starts_with(refname, "refs/heads/") || starts_with(refname, "refs/remotes/") || starts_with(refname, "refs/notes/") || !strcmp(refname, "HEAD"))) { - if (safe_create_leading_directories(logfile->buf) < 0) { + if (safe_create_leading_directories(logfile) < 0) { int save_errno = errno; - error("unable to create directory for %s", logfile->buf); + error("unable to create directory for %s", logfile); errno = save_errno; return -1; } oflags |= O_CREAT; } - logfd = open(logfile->buf, oflags, 0666); + logfd = open(logfile, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { - if (remove_empty_directories(logfile->buf)) { + if (remove_empty_directories(logfile)) { int save_errno = errno; error("There are still logs under '%s'", - logfile->buf); + logfile); errno = save_errno; return -1; } - logfd = open(logfile->buf, oflags, 0666); + logfd = open(logfile, oflags, 0666); } if (logfd < 0) { int save_errno = errno; - error("Unable to append to %s: %s", logfile->buf, + error("Unable to append to %s: %s", logfile, strerror(errno)); errno = save_errno; return -1; } } - adjust_shared_perm(logfile->buf); + adjust_shared_perm(logfile); close(logfd); return 0; } -static int log_ref_write(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg) +static int log_ref_write_1(const char *refname, const unsigned char *old_sha1, + const unsigned char *new_sha1, const char *msg, + struct strbuf *sb_log_file) { int logfd, result, written, oflags = O_APPEND | O_WRONLY; unsigned maxlen, len; int msglen; - struct strbuf sb_log_file = STRBUF_INIT; const char *log_file; char *logrec; const char *committer; @@ -2907,14 +2914,16 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1, if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); - result = log_ref_setup(refname, &sb_log_file); + result = log_ref_setup(refname, sb_log_file); if (result) - goto done; - log_file = sb_log_file.buf; + return result; + log_file = sb_log_file->buf; + /* make sure the rest of the function can't change "log_file" */ + sb_log_file = NULL; logfd = open(log_file, oflags); if (logfd < 0) - goto done; + return 0; msglen = msg ? strlen(msg) : 0; committer = git_committer_info(0); maxlen = strlen(committer) + msglen + 100; @@ -2932,19 +2941,24 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1, close(logfd); error("Unable to append to %s", log_file); errno = save_errno; - result = -1; - goto done; + return -1; } if (close(logfd)) { int save_errno = errno; error("Unable to append to %s", log_file); errno = save_errno; - result = -1; - goto done; + return -1; } -done: - strbuf_release(&sb_log_file); - return result; + return 0; +} + +static int log_ref_write(const char *refname, const unsigned char *old_sha1, + const unsigned char *new_sha1, const char *msg) +{ + struct strbuf sb = STRBUF_INIT; + int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb); + strbuf_release(&sb); + return ret; } int is_branch(const char *refname) diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index e6ac7a4..4df7a2f 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -366,7 +366,7 @@ test_expect_success 'GIT_DIR set (1)' ' test_expect_success 'GIT_DIR set (2)' ' echo "gitdir: repo.git/repos/foo" >gitfile && - echo "$TRASH_DIRECTORY/repo.git" >repo.git/repos/foo/commondir && + echo "$(pwd)/repo.git" >repo.git/repos/foo/commondir && ( cd work && GIT_DIR=../gitfile git rev-parse --git-common-dir >actual && diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index eddd325..915b506 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -13,10 +13,15 @@ test_expect_success 'checkout --to not updating paths' ' ' test_expect_success 'checkout --to an existing worktree' ' - mkdir existing && + mkdir -p existing/subtree && test_must_fail git checkout --detach --to existing master ' +test_expect_success 'checkout --to an existing empty worktree' ' + mkdir existing_empty && + git checkout --detach --to existing_empty master +' + test_expect_success 'checkout --to refuses to checkout locked branch' ' test_must_fail git checkout --to zere master && ! test -d zere && diff --git a/t/t2026-prune-linked-checkouts.sh b/t/t2026-prune-linked-checkouts.sh index 3622800..170aefe 100755 --- a/t/t2026-prune-linked-checkouts.sh +++ b/t/t2026-prune-linked-checkouts.sh @@ -57,7 +57,7 @@ test_expect_success 'prune directories with invalid gitdir' ' test_expect_success 'prune directories with gitdir pointing to nowhere' ' mkdir -p .git/worktrees/def/abc && : >.git/worktrees/def/def && - echo "$TRASH_DIRECTORY"/nowhere >.git/worktrees/def/gitdir && + echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir && git prune --worktrees --verbose >actual && test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual && ! test -d .git/worktrees/def && @@ -76,7 +76,7 @@ test_expect_success 'not prune recent checkouts' ' test_when_finished rm -r .git/worktrees mkdir zz && mkdir -p .git/worktrees/jlm && - echo "$TRASH_DIRECTORY"/zz >.git/worktrees/jlm/gitdir && + echo "$(pwd)"/zz >.git/worktrees/jlm/gitdir && git prune --worktrees --verbose --expire=2.days.ago && test -d .git/worktrees/jlm ' diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh new file mode 100755 index 0000000..8f30aed --- /dev/null +++ b/t/t7410-submodule-checkout-to.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='Combination of submodules and multiple workdirs' + +. ./test-lib.sh + +base_path=$(pwd -P) + +test_expect_success 'setup: make origin' \ + 'mkdir -p origin/sub && ( cd origin/sub && git init && + echo file1 >file1 && + git add file1 && + git commit -m file1 ) && + mkdir -p origin/main && ( cd origin/main && git init && + git submodule add ../sub && + git commit -m "add sub" ) && + ( cd origin/sub && + echo file1updated >file1 && + git add file1 && + git commit -m "file1 updated" ) && + ( cd origin/main/sub && git pull ) && + ( cd origin/main && + git add sub && + git commit -m "sub updated" )' + +test_expect_success 'setup: clone' \ + 'mkdir clone && ( cd clone && + git clone --recursive "$base_path/origin/main")' + +rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1") +rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1") + +test_expect_success 'checkout main' \ + 'mkdir default_checkout && + (cd clone/main && + git checkout --to "$base_path/default_checkout/main" "$rev1_hash_main")' + +test_expect_failure 'can see submodule diffs just after checkout' \ + '(cd default_checkout/main && git diff --submodule master"^!" | grep "file1 updated")' + +test_expect_success 'checkout main and initialize independed clones' \ + 'mkdir fully_cloned_submodule && + (cd clone/main && + git checkout --to "$base_path/fully_cloned_submodule/main" "$rev1_hash_main") && + (cd fully_cloned_submodule/main && git submodule update)' + +test_expect_success 'can see submodule diffs after independed cloning' \ + '(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")' + +test_done -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html