Message ID | 87y2auw3jo.fsf@inria.fr |
---|---|
State | New |
Headers | show |
Series | [bug#49252] Channels defaulting to HEAD instead of ‘master’ | expand |
Ludovic Courtès writes: > For the record, commit cb41c15827a2e910aa56fb5d1917ba8a085c95c7 by Kyle > (Cc’d) gives the ability to use the remote HEAD by default, which is > exactly what we need here. > > Unfortunately, for the Guix repo at Savannah, I get: > > --8<---------------cut here---------------start------------->8--- > $ ./pre-inst-env guix time-machine -- describe > guix time-machine: error: Git error: reference 'refs/remotes/origin/HEAD' not found > --8<---------------cut here---------------end--------------->8--- > > … but it works for <https://github.com/guix-mirror/guix>. > > Presumably we have a server-side setup issue at Savannah? Hmm, it looks like it's there: $ git ls-remote https://git.savannah.gnu.org/git/guix.git | grep HEAD e499500730de23fd54ae63696a725d4b90a406d7 HEAD $ git clone https://git.savannah.gnu.org/git/guix.git && cd guix $ git for-each-ref refs/remotes/origin | grep HEAD e499500730de23fd54ae63696a725d4b90a406d7 commit refs/remotes/origin/HEAD $ git name-rev refs/remotes/origin/HEAD refs/remotes/origin/HEAD master This might be a long shot, but if you cloned the repo with magit-clone, it has some custom logic that deletes refs/remotes/<remote>/HEAD unless magit-clone-set-remote-head is non-nil (and it's nil by default). > Are there downsides, or cases where it might pick the wrong branch? The main downside I can think of is what I mentioned in bug#45187 (87v9d8dk0r.fsf@kyleam.com): [The remote HEAD symref] probably the best indicator of what the primary branch is. In a clone, it doesn't necessarily match HEAD on the remote, because users may change it to another branch they're interested in, but that isn't really relevant to these behind-the-scenes checkouts. So, if it's a user-facing clone, refs/remotes/origin/HEAD isn't a reliable indicator because users are free to redirect the remote HEAD symref to another branch of interest [*] or to delete it altogether. I think channel clones would fall into the "behind the scenes" category, though, right? [*] This is discussed here: https://lore.kernel.org/git/xmqqlfivwvtw.fsf@gitster.c.googlers.com/
Hi Kyle, Kyle Meyer <kyle@kyleam.com> skribis: > Ludovic Courtès writes: > >> For the record, commit cb41c15827a2e910aa56fb5d1917ba8a085c95c7 by Kyle >> (Cc’d) gives the ability to use the remote HEAD by default, which is >> exactly what we need here. >> >> Unfortunately, for the Guix repo at Savannah, I get: >> >> --8<---------------cut here---------------start------------->8--- >> $ ./pre-inst-env guix time-machine -- describe >> guix time-machine: error: Git error: reference 'refs/remotes/origin/HEAD' not found >> --8<---------------cut here---------------end--------------->8--- >> >> … but it works for <https://github.com/guix-mirror/guix>. >> >> Presumably we have a server-side setup issue at Savannah? > > Hmm, it looks like it's there: > > $ git ls-remote https://git.savannah.gnu.org/git/guix.git | grep HEAD > e499500730de23fd54ae63696a725d4b90a406d7 HEAD > > $ git clone https://git.savannah.gnu.org/git/guix.git && cd guix > $ git for-each-ref refs/remotes/origin | grep HEAD > e499500730de23fd54ae63696a725d4b90a406d7 commit refs/remotes/origin/HEAD > $ git name-rev refs/remotes/origin/HEAD > refs/remotes/origin/HEAD master OK, so something else must be at fault. Ideas? > This might be a long shot, but if you cloned the repo with magit-clone, > it has some custom logic that deletes refs/remotes/<remote>/HEAD unless > magit-clone-set-remote-head is non-nil (and it's nil by default). No, these are clones managed via (guix git) only. >> Are there downsides, or cases where it might pick the wrong branch? > > The main downside I can think of is what I mentioned in bug#45187 > (87v9d8dk0r.fsf@kyleam.com): > > [The remote HEAD symref] probably the best indicator of what the > primary branch is. In a clone, it doesn't necessarily match HEAD on > the remote, because users may change it to another branch they're > interested in, but that isn't really relevant to these > behind-the-scenes checkouts. > > So, if it's a user-facing clone, refs/remotes/origin/HEAD isn't a > reliable indicator because users are free to redirect the remote HEAD > symref to another branch of interest [*] or to delete it altogether. > > I think channel clones would fall into the "behind the scenes" category, > though, right? Right, those cached clones managed by (guix git) are behind the scenes; we can assume only (guix git) will only ever touch them. Thanks, Ludo’.
Ludovic Courtès <ludo@gnu.org> writes: > For the record, commit cb41c15827a2e910aa56fb5d1917ba8a085c95c7 > by Kyle > (Cc’d) gives the ability to use the remote HEAD by default, > which is > exactly what we need here. > > Unfortunately, for the Guix repo at Savannah, I get: > > --8<---------------cut > here---------------start------------->8--- > $ ./pre-inst-env guix time-machine -- describe > guix time-machine: error: Git error: reference > 'refs/remotes/origin/HEAD' not found > --8<---------------cut > here---------------end--------------->8--- > > … but it works for <https://github.com/guix-mirror/guix>. For what it’s worth I found the same kind of problem when importing an R package from Github: ./pre-inst-env guix import cran -a git https://github.com/ImmuneDynamics/Spectre Here’s the error backtrace: --8<---------------cut here---------------start------------->8--- Backtrace: In ice-9/boot-9.scm: 724:2 19 (call-with-prompt _ _ #<procedure default-prompt-handler (k proc)>) In ice-9/eval.scm: 619:8 18 (_ #(#(#<directory (guile-user) 7f95f8626c80>))) In guix/ui.scm: 2185:7 17 (run-guix . _) 2148:10 16 (run-guix-command _ . _) In guix/scripts/import.scm: 120:11 15 (guix-import . _) In guix/scripts/import/cran.scm: 110:25 14 (guix-import-cran . _) In guix/memoization.scm: 98:0 13 (mproc "https://github.com/ImmuneDynamics/Spectre" #:repo git) In unknown file: 12 (_ #<procedure 7f95f4ce5e60 at guix/memoization.scm:179:32 ()> #<procedure list _> (this is nothing)) In guix/import/cran.scm: 594:24 11 (_ "https://github.com/ImmuneDynamics/Spectre" #:repo _ #:version _) 279:25 10 (fetch-description _ "https://github.com/ImmuneDynamics/Spectre") In guix/memoization.scm: 98:0 9 (mproc "https://github.com/ImmuneDynamics/Spectre" #:method git) In unknown file: 8 (_ #<procedure 7f95f4ce5e00 at guix/memoization.scm:179:32 ()> #<procedure list _> (this is nothing)) In ice-9/boot-9.scm: 1752:10 7 (with-exception-handler _ _ #:unwind? _ #:unwind-for-type _) In guix/store.scm: 658:37 6 (thunk) In guix/git.scm: 481:8 5 (latest-repository-commit #<store-connection 256.99 7f95f4c18320> "https://github.com/ImmuneDynamics/Spectre" #:recursive? _ #:log-port _ #:cache-directory _ #:ref _) 247:4 4 (update-cached-checkout _ #:ref _ #:recursive? _ #:check-out? _ #:starting-commit _ #:log-port _ #:cache-directory _) 214:18 3 (resolve _) In git/reference.scm: 60:8 2 (_ _ _) In git/bindings.scm: 77:2 1 (raise-git-error _) In ice-9/boot-9.scm: 1685:16 0 (raise-exception _ #:continuable? _) ice-9/boot-9.scm:1685:16: In procedure raise-exception: Git error: reference 'refs/remotes/origin/HEAD' not found --8<---------------cut here---------------end--------------->8--- > Presumably we have a server-side setup issue at Savannah? I think it might be a repository setting rather than a server setting as other Github repositories work, yet this one repo on Github does not.
Ricardo Wurmus writes: > For what it’s worth I found the same kind of problem when > importing an R package from Github: > > ./pre-inst-env guix import cran -a git > https://github.com/ImmuneDynamics/Spectre > > Here’s the error backtrace: [...] > > ice-9/boot-9.scm:1685:16: In procedure raise-exception: > Git error: reference 'refs/remotes/origin/HEAD' not found > --8<---------------cut here---------------end--------------->8--- > >> Presumably we have a server-side setup issue at Savannah? > > I think it might be a repository setting rather than a server > setting as other Github repositories work, yet this one repo on > Github does not. Hmm, I'm not sure what's going on here, but, as with <https://git.savannah.gnu.org/git/guix.git>, if I clone the above repo via `git clone', refs/remotes/origin/HEAD is there: $ git ls-remote https://github.com/ImmuneDynamics/Spectre | grep HEAD 742ebc4bc09ce69b970eceb78291bdbf5229d20d HEAD $ git clone https://github.com/ImmuneDynamics/Spectre && cd Spectre $ git for-each-ref refs/remotes/origin | grep HEAD 742ebc4bc09ce69b970eceb78291bdbf5229d20d commit refs/remotes/origin/HEAD
Ludovic Courtès writes: > Kyle Meyer <kyle@kyleam.com> skribis: [...] >> >> Hmm, it looks like it's there: >> >> $ git ls-remote https://git.savannah.gnu.org/git/guix.git | grep HEAD >> e499500730de23fd54ae63696a725d4b90a406d7 HEAD >> >> $ git clone https://git.savannah.gnu.org/git/guix.git && cd guix >> $ git for-each-ref refs/remotes/origin | grep HEAD >> e499500730de23fd54ae63696a725d4b90a406d7 commit refs/remotes/origin/HEAD >> $ git name-rev refs/remotes/origin/HEAD >> refs/remotes/origin/HEAD master > > OK, so something else must be at fault. > > Ideas? My understanding is that the clone is happening through guile-git, which uses libgit2. In libgit2's v1.1.0 release notes, I see "[t]he refs/remotes/origin/HEAD file will be created at clone time to point to the origin's default branch". So my current guess is that this clone was made before guix's libgit2 was 1.1.0. Does that sound possible (i.e. this clone is from before 2020-12-ish)? Do you still see the "reference 'refs/remotes/origin/HEAD' not found" failure if you force a fresh clone?
Hello Kyle, Kyle Meyer <kyle@kyleam.com> skribis: > Ludovic Courtès writes: > >> Kyle Meyer <kyle@kyleam.com> skribis: > [...] >>> >>> Hmm, it looks like it's there: >>> >>> $ git ls-remote https://git.savannah.gnu.org/git/guix.git | grep HEAD >>> e499500730de23fd54ae63696a725d4b90a406d7 HEAD >>> >>> $ git clone https://git.savannah.gnu.org/git/guix.git && cd guix >>> $ git for-each-ref refs/remotes/origin | grep HEAD >>> e499500730de23fd54ae63696a725d4b90a406d7 commit refs/remotes/origin/HEAD >>> $ git name-rev refs/remotes/origin/HEAD >>> refs/remotes/origin/HEAD master >> >> OK, so something else must be at fault. >> >> Ideas? > > My understanding is that the clone is happening through guile-git, which > uses libgit2. In libgit2's v1.1.0 release notes, I see "[t]he > refs/remotes/origin/HEAD file will be created at clone time to point to > the origin's default branch". So my current guess is that this clone > was made before guix's libgit2 was 1.1.0. > > Does that sound possible (i.e. this clone is from before 2020-12-ish)? > Do you still see the "reference 'refs/remotes/origin/HEAD' not found" > failure if you force a fresh clone? Oh, this must be the reason. If I remove the cached clone: rm -rf ~/.cache/guix/checkouts/pjmkglp4t7znuugeurpurzikxq3tnlaywmisyr27shj7apsnalwq and then clone again, it works: --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> ,use(guix git) scheme@(guile-user)> (update-cached-checkout "https://git.savannah.gnu.org/git/guix.git") $2 = "/home/ludo/.cache/guix/checkouts/pjmkglp4t7znuugeurpurzikxq3tnlaywmisyr27shj7apsnalwq" $3 = "30289f4d4638452520f52c1a36240220d0d940ff" $4 = #f --8<---------------cut here---------------end--------------->8--- Likewise with the other repo Ricardo mentions: --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> (update-cached-checkout "https://github.com/ImmuneDynamics/Spectre") $5 = "/home/ludo/.cache/guix/checkouts/lpz4iyuu3pztx73ks74bfiymewyzn24rm565l6cgiutvjow5joka" $6 = "742ebc4bc09ce69b970eceb78291bdbf5229d20d" $7 = #f --8<---------------cut here---------------end--------------->8--- I suppose we need to add a call to fetch remote heads when they’re missing? It seems that calling ‘remote-fetch’ is not enough. If I explicitly remove ‘HEAD’ from the clone, then I get: --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> (define r (repository-open "/home/ludo/.cache/guix/checkouts/lpz4iyuu3pztx73ks74bfiymewyzn24rm565l6cgiutvjow5joka")) scheme@(guile-user)> (reference-name->oid r "refs/remotes/origin/HEAD") ice-9/boot-9.scm:1685:16: In procedure raise-exception: Git error: reference 'refs/remotes/origin/HEAD' not found Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. scheme@(guile-user) [1]> ,q scheme@(guile-user)> (remote-fetch (remote-lookup r "origin")) scheme@(guile-user)> (reference-name->oid r "refs/remotes/origin/HEAD") ice-9/boot-9.scm:1685:16: In procedure raise-exception: Git error: reference 'refs/remotes/origin/HEAD' not found Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. scheme@(guile-user) [1]> ,q --8<---------------cut here---------------end--------------->8--- Calling ‘remote-ls’ doesn’t populate that file either: --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> (define o (remote-lookup r "origin")) scheme@(guile-user)> (remote-connect o) scheme@(guile-user)> (remote-ls o) $13 = (#<<remote-head> local: 0 oid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> loid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> name: "HEAD" symref-target: 73408368> #<<remote-head> local: 0 oid: #<oid 00000000c9d6e28e5d48c056ee6ad36cc4ab0861> loid: #<oid 00000000c9d6e28e5d48c056ee6ad36cc4ab0861> name: "refs/heads/development" symref-target: 0> #<<remote-head> local: 0 oid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> loid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> name: "refs/heads/master" symref-target: 0> #<<remote-head> local: 0 oid: #<oid 00000000e6985cce0e75b63dab3032ae875f5592> loid: #<oid 00000000e6985cce0e75b63dab3032ae875f5592> name: "refs/heads/pre-release" symref-target: 0> […]) --8<---------------cut here---------------end--------------->8--- So I’m not sure what the best approach is. Thoughts? Ludo’.
Ludovic Courtès writes: > Oh, this must be the reason. If I remove the cached clone: > > rm -rf ~/.cache/guix/checkouts/pjmkglp4t7znuugeurpurzikxq3tnlaywmisyr27shj7apsnalwq > > and then clone again, it works: Great, glad that that mystery is solved. > I suppose we need to add a call to fetch remote heads when they’re > missing? It seems that calling ‘remote-fetch’ is not enough. Right, a fetch won't do it [1]. Users are free to delete or redirect refs/remotes/<remote>/HEAD, and a fetch won't repopulate it. On the command line, you can query the remote and recreate the symref with `remote set-head <remote> -a': $ git symbolic-ref -d refs/remotes/origin/HEAD $ git symbolic-ref refs/remotes/origin/HEAD fatal: ref refs/remotes/origin/HEAD is not a symbolic ref $ git remote set-head origin -a origin/HEAD set to master $ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master In the libgit2 world, I think the most direct path might be something along the lines of "get remote ref via git_remote_default_branch()" followed by "create refs/remotes/<remote>/HEAD via git_reference_symbolic_create()". I'm not spotting either of those functions in guile-git, though. I haven't confirmed but based on the "symref-target" in the remote-ls output you showed... > --8<---------------cut here---------------start------------->8--- > scheme@(guile-user)> (define o (remote-lookup r "origin")) > scheme@(guile-user)> (remote-connect o) > scheme@(guile-user)> (remote-ls o) > $13 = (#<<remote-head> local: 0 oid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> loid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> name: "HEAD" symref-target: 73408368> [...] ... I guess it might be possible to determine the ref target with remote-ls. In that case, it'd just be the git_reference_symbolic_create() wrapper that was missing. [1] In addition to the link I gave upthread, see <https://lore.kernel.org/git/20180601065121.GA15578@sigill.intra.peff.net/> for some discussion about whether fetch should populate it.
Kyle Meyer <kyle@kyleam.com> skribis: > Ludovic Courtès writes: > >> Oh, this must be the reason. If I remove the cached clone: >> >> rm -rf ~/.cache/guix/checkouts/pjmkglp4t7znuugeurpurzikxq3tnlaywmisyr27shj7apsnalwq >> >> and then clone again, it works: > > Great, glad that that mystery is solved. > >> I suppose we need to add a call to fetch remote heads when they’re >> missing? It seems that calling ‘remote-fetch’ is not enough. > > Right, a fetch won't do it [1]. Users are free to delete or redirect > refs/remotes/<remote>/HEAD, and a fetch won't repopulate it. > > On the command line, you can query the remote and recreate the symref > with `remote set-head <remote> -a': > > $ git symbolic-ref -d refs/remotes/origin/HEAD > $ git symbolic-ref refs/remotes/origin/HEAD > fatal: ref refs/remotes/origin/HEAD is not a symbolic ref > $ git remote set-head origin -a > origin/HEAD set to master > $ git symbolic-ref refs/remotes/origin/HEAD > refs/remotes/origin/master > > In the libgit2 world, I think the most direct path might be something > along the lines of "get remote ref via git_remote_default_branch()" > followed by "create refs/remotes/<remote>/HEAD via > git_reference_symbolic_create()". I'm not spotting either of those > functions in guile-git, though. > > I haven't confirmed but based on the "symref-target" in the remote-ls > output you showed... > >> --8<---------------cut here---------------start------------->8--- >> scheme@(guile-user)> (define o (remote-lookup r "origin")) >> scheme@(guile-user)> (remote-connect o) >> scheme@(guile-user)> (remote-ls o) >> $13 = (#<<remote-head> local: 0 oid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> loid: #<oid 00000000742ebc4bc09ce69b970eceb78291bdbf> name: "HEAD" symref-target: 73408368> [...] > > ... I guess it might be possible to determine the ref target with > remote-ls. In that case, it'd just be the > git_reference_symbolic_create() wrapper that was missing. Right. We can add the missing procedures in Guile-Git, but in the meantime, maybe we could create .git/refs/origin/HEAD “by hand”? The advantage is that we wouldn’t have to wait for the new Guile-Git release; but OTOH, we’re the ones making the Guile-Git release, too… Thanks, Ludo’.
Ludovic Courtès writes: > Right. We can add the missing procedures in Guile-Git, but in the > meantime, maybe we could create .git/refs/origin/HEAD “by hand”? Yep, for the default branch $NAME, "ref: refs/remotes/origin/$NAME\n" can be written to .git/refs/origin/HEAD. With respect to getting the default branch, I said in my last message that I thought it'd be possible to glean the target from the remote-ls output. To flesh that out a bit: --8<---------------cut here---------------start------------->8--- (use-modules (git) (system foreign)) (define origin (remote-lookup (repository-open ".") "origin")) (remote-connect origin) (define head (car (remote-ls origin))) (define target-pt (make-pointer ((record-accessor (record-type-descriptor head) 'symref-target) head))) (if (not (null-pointer? target-pt)) (display (pointer->string target-pt))) ;; => refs/heads/master --8<---------------cut here---------------end--------------->8--- And that looks like it matches what libgit2's git_remote_default_branch() does for the non-guessing case (see below), so stopping there may be sufficient for a compatibility kludge. --8<---------------cut here---------------start------------->8--- if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) goto done; if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) { error = GIT_ENOTFOUND; goto done; } if ((error = git_buf_sanitize(out)) < 0) return error; /* the first one must be HEAD so if that has the symref info, we're done */ if (heads[0]->symref_target) { error = git_buf_puts(out, heads[0]->symref_target); goto done; } /* * If there's no symref information, we have to look over them * and guess. We return the first match unless the default * branch is a candidate. Then we return the default branch. */--8<---------------cut here---------------end--------------->8---
diff --git a/guix/channels.scm b/guix/channels.scm index 476d62e1f4..a5283b4bf4 100644 --- a/guix/channels.scm +++ b/guix/channels.scm @@ -122,7 +122,7 @@ channel? (name channel-name) (url channel-url) - (branch channel-branch (default "master")) + (branch channel-branch (default #f)) (commit channel-commit (default #f)) (introduction channel-introduction (default #f)) (location channel-location @@ -179,7 +179,6 @@ to the corresponding bytevector." (define %default-guix-channel (channel (name 'guix) - (branch "master") (url %default-channel-url) (introduction %guix-channel-introduction))) @@ -225,7 +224,9 @@ introduction, add it." "Return the \"reference\" for CHANNEL, an sexp suitable for 'latest-repository-commit'." (match (channel-commit channel) - (#f `(branch . ,(channel-branch channel))) + (#f (if (channel-branch channel) + `(branch . ,(channel-branch channel)) + '())) ;remote HEAD (commit `(commit . ,(channel-commit channel))))) (define sexp->channel-introduction diff --git a/guix/inferior.scm b/guix/inferior.scm index 7c8e478f2a..3db63167fd 100644 --- a/guix/inferior.scm +++ b/guix/inferior.scm @@ -732,7 +732,10 @@ prefix, resolve it; and if 'commit' is unset, fetch CHANNEL's branch tip." (branch (channel-branch channel))) (if (and commit (= (string-length commit) 40)) commit - (let* ((ref (if commit `(commit . ,commit) `(branch . ,branch))) + (let* ((ref (cond + (commit `(commit . ,commit)) + (branch `(branch . ,branch)) + (else '()))) ;remote HEAD (cache commit relation (update-cached-checkout (channel-url channel) #:ref ref