* Detached checkout will clobber branch head when using symlink HEAD @ 2008-10-15 18:24 Matt Draisey 2008-10-16 19:17 ` Jeff King 0 siblings, 1 reply; 15+ messages in thread From: Matt Draisey @ 2008-10-15 18:24 UTC (permalink / raw) To: git The faulty code appears to be this git 1.6.0.2.526.g5c283 builtin-checkout.c > 481 static void update_refs_for_switch(struct checkout_opts *opts, > 500 if (new->path) { > > 511 } else if (strcmp(new->name, "HEAD")) { > 512 update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, > 513 REF_NODEREF, DIE_ON_ERR); > 514 if (!opts->quiet) { > 515 if (old->path) > 516 fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name); > 517 describe_detached_head("HEAD is now at", new->commit); > 518 } > 519 } If HEAD is a symlink rather than a "ref:" style link this is really bad. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-15 18:24 Detached checkout will clobber branch head when using symlink HEAD Matt Draisey @ 2008-10-16 19:17 ` Jeff King 2008-10-16 20:11 ` Matt Draisey 0 siblings, 1 reply; 15+ messages in thread From: Jeff King @ 2008-10-16 19:17 UTC (permalink / raw) To: Matt Draisey; +Cc: Junio C Hamano, git On Wed, Oct 15, 2008 at 02:24:47PM -0400, Matt Draisey wrote: > If HEAD is a symlink rather than a "ref:" style link this is really > bad. Hmm. I don't think we have shipped with a symlink HEAD for quite a long time. Using one obviously doesn't work with detached HEAD, but also would fail with packed refs. I don't know if we ever deprecated it. Still, this is a pretty nasty effect, in that it kills whatever was in the existing ref. There is a fix below which refuses to lock any ref if it is a symlink and we have requested not to dereference (actually, it is overly cautious -- the symlink flag is triggered when we see a symlink anywhere in the chain, though we need only prevent the situation when the _first_ symref is a symlink. I don't know if it is worth differentiating, since they are both "should never happen anymore" situations, I think). --- diff --git a/refs.c b/refs.c index b680750..43568e7 100644 --- a/refs.c +++ b/refs.c @@ -447,7 +447,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * strcpy(ref_buffer, buffer); ref = ref_buffer; if (flag) - *flag |= REF_ISSYMREF; + *flag |= REF_ISSYMREF | REF_ISSYMLINK; continue; } } @@ -817,6 +817,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char } ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type); } + if (type & REF_ISSYMLINK && flags & REF_NODEREF) { + error("unable to directly lock symbolic link '%s'", orig_ref); + goto error_return; + } if (type_p) *type_p = type; if (!ref) { diff --git a/refs.h b/refs.h index 06ad260..6356a6a 100644 --- a/refs.h +++ b/refs.h @@ -12,6 +12,7 @@ struct ref_lock { #define REF_ISSYMREF 01 #define REF_ISPACKED 02 +#define REF_ISSYMLINK 04 /* * Calls the specified function for each ref file until it returns nonzero, ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 19:17 ` Jeff King @ 2008-10-16 20:11 ` Matt Draisey 2008-10-16 20:20 ` Nicolas Pitre 2008-10-16 20:39 ` Jeff King 0 siblings, 2 replies; 15+ messages in thread From: Matt Draisey @ 2008-10-16 20:11 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, git On Thu, 2008-10-16 at 15:17 -0400, Jeff King wrote: > Hmm. I don't think we have shipped with a symlink HEAD for quite a long > time. Using one obviously doesn't work with detached HEAD, but also > would fail with packed refs. I don't know if we ever deprecated it. I am using the following system defaults: core.prefersymlinkrefs=true gc.packrefs=false so almost all my git repositories are still using a symlink HEAD. I have some old scripts That I use occasionally and still depend on it. Using detached checkout is the only problem I've had. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:11 ` Matt Draisey @ 2008-10-16 20:20 ` Nicolas Pitre 2008-10-16 20:22 ` Jeff King 2008-10-16 20:28 ` Matt Draisey 2008-10-16 20:39 ` Jeff King 1 sibling, 2 replies; 15+ messages in thread From: Nicolas Pitre @ 2008-10-16 20:20 UTC (permalink / raw) To: Matt Draisey; +Cc: Jeff King, Junio C Hamano, git On Thu, 16 Oct 2008, Matt Draisey wrote: > On Thu, 2008-10-16 at 15:17 -0400, Jeff King wrote: > > > Hmm. I don't think we have shipped with a symlink HEAD for quite a long > > time. Using one obviously doesn't work with detached HEAD, but also > > would fail with packed refs. I don't know if we ever deprecated it. > > I am using the following system defaults: > > core.prefersymlinkrefs=true > gc.packrefs=false > > so almost all my git repositories are still using a symlink HEAD. > I have some old scripts That I use occasionally and still depend on it. > Using detached checkout is the only problem I've had. A symlink HEAD and detached checkouts are simply incompatible. Nicolas ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:20 ` Nicolas Pitre @ 2008-10-16 20:22 ` Jeff King 2008-10-16 20:30 ` Nicolas Pitre 2008-10-16 20:28 ` Matt Draisey 1 sibling, 1 reply; 15+ messages in thread From: Jeff King @ 2008-10-16 20:22 UTC (permalink / raw) To: Nicolas Pitre; +Cc: Matt Draisey, Junio C Hamano, git On Thu, Oct 16, 2008 at 04:20:32PM -0400, Nicolas Pitre wrote: > > so almost all my git repositories are still using a symlink HEAD. > > I have some old scripts That I use occasionally and still depend on it. > > Using detached checkout is the only problem I've had. > > A symlink HEAD and detached checkouts are simply incompatible. Agreed, but I think the complaint is not that it doesn't work, but that it silently clobbers the current branch when you try it. -Peff ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:22 ` Jeff King @ 2008-10-16 20:30 ` Nicolas Pitre 0 siblings, 0 replies; 15+ messages in thread From: Nicolas Pitre @ 2008-10-16 20:30 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, Junio C Hamano, git On Thu, 16 Oct 2008, Jeff King wrote: > On Thu, Oct 16, 2008 at 04:20:32PM -0400, Nicolas Pitre wrote: > > > > so almost all my git repositories are still using a symlink HEAD. > > > I have some old scripts That I use occasionally and still depend on it. > > > Using detached checkout is the only problem I've had. > > > > A symlink HEAD and detached checkouts are simply incompatible. > > Agreed, but I think the complaint is not that it doesn't work, but that > it silently clobbers the current branch when you try it. That is unacceptable indeed. Nicolas ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:20 ` Nicolas Pitre 2008-10-16 20:22 ` Jeff King @ 2008-10-16 20:28 ` Matt Draisey 2008-10-16 20:47 ` Nicolas Pitre 1 sibling, 1 reply; 15+ messages in thread From: Matt Draisey @ 2008-10-16 20:28 UTC (permalink / raw) To: Nicolas Pitre; +Cc: Jeff King, Junio C Hamano, git On Thu, 2008-10-16 at 16:20 -0400, Nicolas Pitre wrote: > A symlink HEAD and detached checkouts are simply incompatible. Not necessarily. The symlinking code will unlink the original inode each time it creates a new symlink anyway. It is simply a matter of creating a new file in its place. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:28 ` Matt Draisey @ 2008-10-16 20:47 ` Nicolas Pitre 0 siblings, 0 replies; 15+ messages in thread From: Nicolas Pitre @ 2008-10-16 20:47 UTC (permalink / raw) To: Matt Draisey; +Cc: Jeff King, Junio C Hamano, git On Thu, 16 Oct 2008, Matt Draisey wrote: > On Thu, 2008-10-16 at 16:20 -0400, Nicolas Pitre wrote: > > A symlink HEAD and detached checkouts are simply incompatible. > > Not necessarily. The symlinking code will unlink the original inode > each time it creates a new symlink anyway. It is simply a matter of > creating a new file in its place. True. I didn't think it all through initially. Nicolas ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:11 ` Matt Draisey 2008-10-16 20:20 ` Nicolas Pitre @ 2008-10-16 20:39 ` Jeff King 2008-10-17 23:08 ` Junio C Hamano 1 sibling, 1 reply; 15+ messages in thread From: Jeff King @ 2008-10-16 20:39 UTC (permalink / raw) To: Matt Draisey; +Cc: Junio C Hamano, git On Thu, Oct 16, 2008 at 04:11:03PM -0400, Matt Draisey wrote: > I am using the following system defaults: > > core.prefersymlinkrefs=true > gc.packrefs=false > > so almost all my git repositories are still using a symlink HEAD. > I have some old scripts That I use occasionally and still depend on it. > Using detached checkout is the only problem I've had. In your position I would consider updating my scripts. But I guess you could also try to work up a patch that makes detached HEAD work (replacing the symlink with a file, then bringing back the symlink when you're on a branch). In the meantime, here is a cleaned-up version of my patch, with a proper commit message and a test. -- >8 -- do not clobber symlinked ref with detached HEAD The default configuration for git uses a symref for HEAD. When we detach the HEAD, we can simply write the detached commit sha1 into the HEAD file. It is still possible to use symlinks for HEAD (either by setting it up manually, or by using core.prefersymlinkrefs). In that case, moving to a detached HEAD is impossible, since we have nowhere to store the sha1. However, the current code doesn't realize this and actually writes into the HEAD file anyway, meaning that it overwrites the value of the currently checked out branch. Instead, let's detect in the locking mechanism that we have a symlink but the caller requested that we lock the original ref name instead of its linked destination. This has two advantages: - we don't have to add an extra stat call, since we discover the symlink during normal ref resolution - any code to update a ref should lock it, meaning that we should catch any other similar instances Signed-off-by: Jeff King <peff@peff.net> --- refs.c | 9 ++++++++- refs.h | 1 + t/t7202-checkout-symlink.sh | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletions(-) create mode 100755 t/t7202-checkout-symlink.sh diff --git a/refs.c b/refs.c index b680750..b4b3865 100644 --- a/refs.c +++ b/refs.c @@ -446,8 +446,11 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int * buffer[len] = 0; strcpy(ref_buffer, buffer); ref = ref_buffer; - if (flag) + if (flag) { + if (!(*flag & REF_ISSYMREF)) + *flag |= REF_OUTER_IS_SYMLINK; *flag |= REF_ISSYMREF; + } continue; } } @@ -817,6 +820,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char } ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type); } + if (type & REF_OUTER_IS_SYMLINK && flags & REF_NODEREF) { + error("unable to directly lock symbolic link '%s'", orig_ref); + goto error_return; + } if (type_p) *type_p = type; if (!ref) { diff --git a/refs.h b/refs.h index 06ad260..9b0dcd9 100644 --- a/refs.h +++ b/refs.h @@ -12,6 +12,7 @@ struct ref_lock { #define REF_ISSYMREF 01 #define REF_ISPACKED 02 +#define REF_OUTER_IS_SYMLINK 04 /* * Calls the specified function for each ref file until it returns nonzero, diff --git a/t/t7202-checkout-symlink.sh b/t/t7202-checkout-symlink.sh new file mode 100755 index 0000000..cf09f5f --- /dev/null +++ b/t/t7202-checkout-symlink.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +test_description='checkout with symlinked HEAD' +. ./test-lib.sh + +test_expect_success 'setup' ' + echo one > file && git add file && git commit -m one && + echo two > file && git add file && git commit -m two && + ln -sf refs/heads/master .git/HEAD +' + +test_expect_success 'checkout detached HEAD does not clobber ref' ' + test_must_fail git checkout HEAD^ && + echo two >expect && + git log -1 --pretty=tformat:%s >actual + test_cmp actual expect +' + +test_done -- 1.6.0.2.711.gf1ba4.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-16 20:39 ` Jeff King @ 2008-10-17 23:08 ` Junio C Hamano 2008-10-17 23:10 ` [PATCH 1/3] demonstrate breakage of detached checkout with symbolic link HEAD Junio C Hamano ` (3 more replies) 0 siblings, 4 replies; 15+ messages in thread From: Junio C Hamano @ 2008-10-17 23:08 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, git I do not think that is a correct approach. The offending callchain is: update_ref(..., "HEAD", REF_NODEREF, ...); -> lock_any_ref_for_update("HEAD", ..., REF_NODEREF); -> lock_ref_sha1_basic("HEAD", ..., REF_NODEREF, ...); . calls resolve_ref() to read HEAD to arrive at refs/heads/master . however, it notices REF_NODEREF and adjusts the ref to be updated back to "HEAD"; -> hold_lock_file_for_update(..., "HEAD", 1); -> lock_file(..., "HEAD"); . resolves symlink "HEAD" to "refs/heads/master", and locks it! This creates "refs/heads/master.lock", that is then renamed to "refs/heads/master" when unlocked. In other words, the breakage is in lock_file() and not in resolve_ref(). The latter gives the same output to the caller whether the HEAD is symbolic link or textual symref -- at least it should. The behaviour of lock_file() to resolve symlink at this point in the code comes from d58e8d3 (When locking in a symlinked repository, try to lock the original, 2007-07-25), and as explained in the log message of that commit, we cannot unconditionally remove it. Three patches to fix this issue, that come on top of the fix to t7201 (--track from detached HEAD should fail) I sent out last night, will follow. ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/3] demonstrate breakage of detached checkout with symbolic link HEAD 2008-10-17 23:08 ` Junio C Hamano @ 2008-10-17 23:10 ` Junio C Hamano 2008-10-17 23:10 ` [PATCH 2/3] Enhance hold_lock_file_for_{update,append}() API Junio C Hamano ` (2 subsequent siblings) 3 siblings, 0 replies; 15+ messages in thread From: Junio C Hamano @ 2008-10-17 23:10 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, git When core.prefersymlinkrefs is in use, detaching the HEAD by checkout incorrectly clobbers the tip of the current branch. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- t/t7201-co.sh | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-) diff --git a/t/t7201-co.sh b/t/t7201-co.sh index ee2cab6..01304d7 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -339,6 +339,18 @@ test_expect_success 'checkout w/--track from non-branch HEAD fails' ' test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)" ' +test_expect_failure 'detch 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 an unmerged path should fail' ' rm -f .git/index && O=$(echo original | git hash-object -w --stdin) && -- 1.6.0.2.734.gae0be ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/3] Enhance hold_lock_file_for_{update,append}() API 2008-10-17 23:08 ` Junio C Hamano 2008-10-17 23:10 ` [PATCH 1/3] demonstrate breakage of detached checkout with symbolic link HEAD Junio C Hamano @ 2008-10-17 23:10 ` Junio C Hamano 2008-10-17 23:11 ` [PATCH 3/3] Fix checkout not to clobber the branch when using symlinked HEAD upon detaching Junio C Hamano 2008-10-19 13:00 ` Detached checkout will clobber branch head when using symlink HEAD Jeff King 3 siblings, 0 replies; 15+ messages in thread From: Junio C Hamano @ 2008-10-17 23:10 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, git This changes the "die_on_error" boolean parameter to a mere "flags", and changes the existing callers of hold_lock_file_for_update/append() functions to pass LOCK_DIE_ON_ERROR. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-commit.c | 3 ++- builtin-fetch-pack.c | 3 ++- builtin-revert.c | 3 ++- bundle.c | 3 ++- cache.h | 1 + lockfile.c | 26 +++++++++++++++----------- pack-refs.c | 3 ++- refs.c | 3 ++- rerere.c | 3 ++- sha1_file.c | 2 +- 10 files changed, 31 insertions(+), 19 deletions(-) diff --git a/builtin-commit.c b/builtin-commit.c index e2a7e48..b563a0d 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -320,7 +320,8 @@ 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-%d", getpid()), 1); + git_path("next-index-%d", 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 85509f5..21ce3e0 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 27881e9..e839387 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -338,7 +338,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 00b2aab..7d17a1f 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 884fae8..941a9dc 100644 --- a/cache.h +++ b/cache.h @@ -411,6 +411,7 @@ struct lock_file { char on_list; char filename[PATH_MAX]; }; +#define LOCK_DIE_ON_ERROR 1 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 4023797..bc1b585 100644 --- a/lockfile.c +++ b/lockfile.c @@ -121,9 +121,10 @@ 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 @@ -155,21 +156,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 +178,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 +216,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 848d311..2c76fb1 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 39a3b23..5467e98 100644 --- a/refs.c +++ b/refs.c @@ -845,7 +845,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, + LOCK_DIE_ON_ERROR); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; diff --git a/rerere.c b/rerere.c index 323e493..2b7a99d 100644 --- a/rerere.c +++ b/rerere.c @@ -346,7 +346,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 e2cb342..5cfae5d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -385,7 +385,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)) -- 1.6.0.2.734.gae0be ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 3/3] Fix checkout not to clobber the branch when using symlinked HEAD upon detaching 2008-10-17 23:08 ` Junio C Hamano 2008-10-17 23:10 ` [PATCH 1/3] demonstrate breakage of detached checkout with symbolic link HEAD Junio C Hamano 2008-10-17 23:10 ` [PATCH 2/3] Enhance hold_lock_file_for_{update,append}() API Junio C Hamano @ 2008-10-17 23:11 ` Junio C Hamano 2008-10-19 13:00 ` Detached checkout will clobber branch head when using symlink HEAD Jeff King 3 siblings, 0 replies; 15+ messages in thread From: Junio C Hamano @ 2008-10-17 23:11 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, git When you are using core.prefersymlinkrefs (i.e. your ".git/HEAD" is a symlink to "refs/heads/$current_branch"), attempt to detach HEAD resulted in clobbering the tip of the current branch. The offending callchain is: update_ref(..., "HEAD", REF_NODEREF, ...); -> lock_any_ref_for_update("HEAD", ..., REF_NODEREF); -> lock_ref_sha1_basic("HEAD", ..., REF_NODEREF, ...); . calls resolve_ref() to read HEAD to arrive at refs/heads/master . however, it notices REF_NODEREF and adjusts the ref to be updated back to "HEAD"; -> hold_lock_file_for_update(..., "HEAD", 1); -> lock_file(..., "HEAD"); . resolves symlink "HEAD" to "refs/heads/master", and locks it! This creates "refs/heads/master.lock", that is then renamed to "refs/heads/master" when unlocked. The behaviour of lock_file() to resolve symlink at this point in the code comes from d58e8d3 (When locking in a symlinked repository, try to lock the original, 2007-07-25), and as explained in the log message of that commit, we cannot unconditionally remove it. This patch fixes this. It teaches lock_file() not to dereference the symbolic link when LOCK_NODEREF is given, and uses this new flag in lock_ref_sha1_basic() when it is operating directly on HEAD (iow when REF_NODEREF was given to it). Signed-off-by: Junio C Hamano <gitster@pobox.com> --- I haven't tested it beyond running the full testsuite, which it does pass, but I can't give any more guarantee than that. Testing is for wimps ;-) cache.h | 1 + lockfile.c | 3 ++- refs.c | 10 ++++++---- t/t7201-co.sh | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cache.h b/cache.h index 941a9dc..8ab2fd8 100644 --- a/cache.h +++ b/cache.h @@ -412,6 +412,7 @@ struct lock_file { 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 bc1b585..6d75608 100644 --- a/lockfile.c +++ b/lockfile.c @@ -130,7 +130,8 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) * 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) { diff --git a/refs.c b/refs.c index 5467e98..9e422dc 100644 --- a/refs.c +++ b/refs.c @@ -790,7 +790,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)); @@ -830,8 +830,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); @@ -845,9 +848,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, - LOCK_DIE_ON_ERROR); + 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/t/t7201-co.sh b/t/t7201-co.sh index 01304d7..d9a80aa 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -339,7 +339,7 @@ test_expect_success 'checkout w/--track from non-branch HEAD fails' ' test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)" ' -test_expect_failure 'detch a symbolic link HEAD' ' +test_expect_success 'detch a symbolic link HEAD' ' git checkout master && git config --bool core.prefersymlinkrefs yes && git checkout side && -- 1.6.0.2.734.gae0be ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-17 23:08 ` Junio C Hamano ` (2 preceding siblings ...) 2008-10-17 23:11 ` [PATCH 3/3] Fix checkout not to clobber the branch when using symlinked HEAD upon detaching Junio C Hamano @ 2008-10-19 13:00 ` Jeff King 2008-10-19 19:36 ` Junio C Hamano 3 siblings, 1 reply; 15+ messages in thread From: Jeff King @ 2008-10-19 13:00 UTC (permalink / raw) To: Junio C Hamano; +Cc: Matt Draisey, git On Fri, Oct 17, 2008 at 04:08:42PM -0700, Junio C Hamano wrote: > I do not think that is a correct approach. > > The offending callchain is: > > update_ref(..., "HEAD", REF_NODEREF, ...); > -> lock_any_ref_for_update("HEAD", ..., REF_NODEREF); > -> lock_ref_sha1_basic("HEAD", ..., REF_NODEREF, ...); > . calls resolve_ref() to read HEAD to arrive at > refs/heads/master > . however, it notices REF_NODEREF and adjusts the ref to be updated > back to "HEAD"; > -> hold_lock_file_for_update(..., "HEAD", 1); > -> lock_file(..., "HEAD"); > . resolves symlink "HEAD" to "refs/heads/master", and > locks it! This creates "refs/heads/master.lock", that is > then renamed to "refs/heads/master" when unlocked. > > In other words, the breakage is in lock_file() and not in resolve_ref(). > The latter gives the same output to the caller whether the HEAD is > symbolic link or textual symref -- at least it should. To be fair, my patch fixed the problem in lock_ref_sha1_basic, not resolve_ref. It merely asked resolve_ref to annotate the resolution to show whether a symlink was found, since it already had that information. But if I am reading your patch right, you are actually enabling detached HEAD in this instance, which is much better. Unfortunately, I had quite a few conflicts in applying your patches on top of master (with or without the patch from "checkout --track -b broken"), and when I thought I had fixed up the result, the test actually still failed. So I will take your word that it actually works, and that I screwed up resolving the conflicts. -Peff PS If you are rebasing or resolving anyway, as I suspect you will have to, there is a typo in the test: s/detch/detach/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Detached checkout will clobber branch head when using symlink HEAD 2008-10-19 13:00 ` Detached checkout will clobber branch head when using symlink HEAD Jeff King @ 2008-10-19 19:36 ` Junio C Hamano 0 siblings, 0 replies; 15+ messages in thread From: Junio C Hamano @ 2008-10-19 19:36 UTC (permalink / raw) To: Jeff King; +Cc: Matt Draisey, git Jeff King <peff@peff.net> writes: > But if I am reading your patch right, you are actually enabling detached > HEAD in this instance, which is much better. Unfortunately, I had quite > a few conflicts in applying your patches on top of master (with or > without the patch from "checkout --track -b broken"), and when I thought > I had fixed up the result, the test actually still failed. > > So I will take your word that it actually works, and that I screwed up > resolving the conflicts. It's the three-patch series that leads to 684968c (Fix checkout not to clobber the branch when using symlinked HEAD upon detaching, 2008-10-17). > PS If you are rebasing or resolving anyway, as I suspect you will have > to, there is a typo in the test: s/detch/detach/ Thanks; the series was only in 'pu', so I will. ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2008-10-19 19:37 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-10-15 18:24 Detached checkout will clobber branch head when using symlink HEAD Matt Draisey 2008-10-16 19:17 ` Jeff King 2008-10-16 20:11 ` Matt Draisey 2008-10-16 20:20 ` Nicolas Pitre 2008-10-16 20:22 ` Jeff King 2008-10-16 20:30 ` Nicolas Pitre 2008-10-16 20:28 ` Matt Draisey 2008-10-16 20:47 ` Nicolas Pitre 2008-10-16 20:39 ` Jeff King 2008-10-17 23:08 ` Junio C Hamano 2008-10-17 23:10 ` [PATCH 1/3] demonstrate breakage of detached checkout with symbolic link HEAD Junio C Hamano 2008-10-17 23:10 ` [PATCH 2/3] Enhance hold_lock_file_for_{update,append}() API Junio C Hamano 2008-10-17 23:11 ` [PATCH 3/3] Fix checkout not to clobber the branch when using symlinked HEAD upon detaching Junio C Hamano 2008-10-19 13:00 ` Detached checkout will clobber branch head when using symlink HEAD Jeff King 2008-10-19 19:36 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).