diff --git a/branch.c b/branch.c index 205b89dc697eb4475355ab5b2a6528e404901c0c..b1ac837f3d30c826ddbe29492b906bf2d0de0a1a 100644 --- a/branch.c +++ b/branch.c @@ -129,7 +129,9 @@ void create_branch(const char *head, die("Cannot setup tracking information; starting point is not a branch."); break; case 1: - /* Unique completion -- good */ + /* Unique completion -- good, only if it is a real ref */ + if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD")) + die("Cannot setup tracking information; starting point is not a branch."); break; default: die("Ambiguous object name: '%s'.", start_name); diff --git a/builtin-commit.c b/builtin-commit.c index 33b659edce478fb0d3d4bb723e4f54c13db0efb1..93ca49635731942c72cb52b27b1a6ba53becd1ca 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -320,7 +320,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, - git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1); + git_path("next-index-%"PRIuMAX, + (uintmax_t) getpid()), + LOCK_DIE_ON_ERROR); create_base_index(); add_remove_files(&partial); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index fa3c936493cc0b139edf3e4e8154569f453afc02..372bfa20a2eac978f7511f5d8d9296be789b527f 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -813,7 +813,8 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, ) die("shallow file was changed during fetch"); - fd = hold_lock_file_for_update(&lock, shallow, 1); + fd = hold_lock_file_for_update(&lock, shallow, + LOCK_DIE_ON_ERROR); if (!write_shallow_commits(fd, 0)) { unlink(shallow); rollback_lock_file(&lock); diff --git a/builtin-revert.c b/builtin-revert.c index 84865397405d96aa4fdbfca801e8ecd47e3dea36..472554019afdb5e8e21f2d362f62f40eb569d8aa 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -329,7 +329,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) * reverse of it if we are revert. */ - msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1); + msg_fd = hold_lock_file_for_update(&msg_file, defmsg, + LOCK_DIE_ON_ERROR); encoding = get_encoding(message); if (!encoding) diff --git a/bundle.c b/bundle.c index 00b2aabefca49b634f49143523ee31556baa7777..7d17a1fde16204859849aaf28945739aaa685f91 100644 --- a/bundle.c +++ b/bundle.c @@ -186,7 +186,8 @@ int create_bundle(struct bundle_header *header, const char *path, if (bundle_to_stdout) bundle_fd = 1; else - bundle_fd = hold_lock_file_for_update(&lock, path, 1); + bundle_fd = hold_lock_file_for_update(&lock, path, + LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); diff --git a/cache.h b/cache.h index ae6647e22c0b6811c5f932b06622a75bd97b0a22..cdbeb48c65f45b4b37ceb767a72866e5a7475cc3 100644 --- a/cache.h +++ b/cache.h @@ -423,6 +423,8 @@ struct lock_file { char on_list; char filename[PATH_MAX]; }; +#define LOCK_DIE_ON_ERROR 1 +#define LOCK_NODEREF 2 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); diff --git a/lockfile.c b/lockfile.c index 4023797b00fe21ecbabe3407ef8a12fca0690607..6d756086939b631ab13bf4fcdb8deed2787eed6b 100644 --- a/lockfile.c +++ b/lockfile.c @@ -121,15 +121,17 @@ static char *resolve_symlink(char *p, size_t s) } -static int lock_file(struct lock_file *lk, const char *path) +static int lock_file(struct lock_file *lk, const char *path, int flags) { - if (strlen(path) >= sizeof(lk->filename)) return -1; + if (strlen(path) >= sizeof(lk->filename)) + return -1; strcpy(lk->filename, path); /* * subtract 5 from size to make sure there's room for adding * ".lock" for the lock file name */ - resolve_symlink(lk->filename, sizeof(lk->filename)-5); + if (!(flags & LOCK_NODEREF)) + resolve_symlink(lk->filename, sizeof(lk->filename)-5); strcat(lk->filename, ".lock"); lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { @@ -155,21 +157,21 @@ static int lock_file(struct lock_file *lk, const char *path) return lk->fd; } -int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error) +int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags) { - int fd = lock_file(lk, path); - if (fd < 0 && die_on_error) + int fd = lock_file(lk, path, flags); + if (fd < 0 && (flags & LOCK_DIE_ON_ERROR)) die("unable to create '%s.lock': %s", path, strerror(errno)); return fd; } -int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error) +int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags) { int fd, orig_fd; - fd = lock_file(lk, path); + fd = lock_file(lk, path, flags); if (fd < 0) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) die("unable to create '%s.lock': %s", path, strerror(errno)); return fd; } @@ -177,13 +179,13 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on orig_fd = open(path, O_RDONLY); if (orig_fd < 0) { if (errno != ENOENT) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) die("cannot open '%s' for copying", path); close(fd); return error("cannot open '%s' for copying", path); } } else if (copy_fd(orig_fd, fd)) { - if (die_on_error) + if (flags & LOCK_DIE_ON_ERROR) exit(128); close(fd); return -1; @@ -215,7 +217,10 @@ int commit_lock_file(struct lock_file *lk) int hold_locked_index(struct lock_file *lk, int die_on_error) { - return hold_lock_file_for_update(lk, get_index_file(), die_on_error); + return hold_lock_file_for_update(lk, get_index_file(), + die_on_error + ? LOCK_DIE_ON_ERROR + : 0); } void set_alternate_index_output(const char *name) diff --git a/pack-refs.c b/pack-refs.c index 848d311c2b2c651dbb14893c260584f00c639357..2c76fb181f64e10c65517224b0c09cb648db81ac 100644 --- a/pack-refs.c +++ b/pack-refs.c @@ -89,7 +89,8 @@ int pack_refs(unsigned int flags) memset(&cbdata, 0, sizeof(cbdata)); cbdata.flags = flags; - fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); + fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), + LOCK_DIE_ON_ERROR); cbdata.refs_file = fdopen(fd, "w"); if (!cbdata.refs_file) die("unable to create ref-pack file structure (%s)", diff --git a/refs.c b/refs.c index b6807505e243fd26cae50460bc003254406ae7e5..0a126fa371ae24f5cb251ff52cf7d38322d78ba7 100644 --- a/refs.c +++ b/refs.c @@ -796,7 +796,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char struct ref_lock *lock; struct stat st; int last_errno = 0; - int type; + int type, lflags; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -836,8 +836,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lk = xcalloc(1, sizeof(struct lock_file)); - if (flags & REF_NODEREF) + lflags = LOCK_DIE_ON_ERROR; + if (flags & REF_NODEREF) { ref = orig_ref; + lflags |= LOCK_NODEREF; + } lock->ref_name = xstrdup(ref); lock->orig_ref_name = xstrdup(orig_ref); ref_file = git_path("%s", ref); @@ -851,8 +854,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char error("unable to create directory for %s", ref_file); goto error_return; } - lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); + lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; error_return: diff --git a/rerere.c b/rerere.c index 5bb5316cdae16154fd4aaffe996710c0bcfc0b94..8e5532b1ff96a88133d837b9a29fa78117c36abd 100644 --- a/rerere.c +++ b/rerere.c @@ -352,7 +352,8 @@ int setup_rerere(struct string_list *merge_rr) return -1; merge_rr_path = xstrdup(git_path("MERGE_RR")); - fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1); + fd = hold_lock_file_for_update(&write_lock, merge_rr_path, + LOCK_DIE_ON_ERROR); read_rr(merge_rr); return fd; } diff --git a/sha1_file.c b/sha1_file.c index 70bb453be25d4e5ba05ee5f2565630a8994842f2..ab2b520f033c75e5e8d3434376a203add59559a4 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -388,7 +388,7 @@ static void read_info_alternates(const char * relative_base, int depth) void add_to_alternates_file(const char *reference) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1); + int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR); char *alt = mkpath("%s/objects\n", reference); write_or_die(fd, alt, strlen(alt)); if (commit_lock_file(lock)) diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 82769b89fc79d91982420ddfc83ce6c3dd672095..0e21632f19e75e1e7bbca1ceb2b9402a4d291584 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -330,12 +330,26 @@ test_expect_success \ test "$(git config branch.track2.merge)" git config branch.autosetupmerge false' -test_expect_success \ - 'checkout w/--track from non-branch HEAD fails' ' - git checkout -b delete-me master && - rm .git/refs/heads/delete-me && - test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && - test_must_fail git checkout --track -b track' +test_expect_success 'checkout w/--track from non-branch HEAD fails' ' + git checkout master^0 && + test_must_fail git symbolic-ref HEAD && + test_must_fail git checkout --track -b track && + test_must_fail git rev-parse --verify track && + test_must_fail git symbolic-ref HEAD && + test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)" +' + +test_expect_success 'detach a symbolic link HEAD' ' + git checkout master && + git config --bool core.prefersymlinkrefs yes && + git checkout side && + git checkout master && + it=$(git symbolic-ref HEAD) && + test "z$it" = zrefs/heads/master && + here=$(git rev-parse --verify refs/heads/master) && + git checkout side^ && + test "z$(git rev-parse --verify refs/heads/master)" = "z$here" +' test_expect_success \ 'checkout with --track fakes a sensible -b ' '