Message ID | xmqqsfjzaq78.fsf@gitster.g (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v2] symbolic-ref: teach "--[no-]recurse" option | expand |
On Sat, Oct 8, 2022 at 12:34 AM Junio C Hamano <gitster@pobox.com> wrote: > [...] > One thing that is slightly irritating, however, is that I do not > think there is a good way (other than "cat .git/HEAD") to learn that > you checked out 'maint' to get into that state. Just like the output > of "git branch --show-current" shows above, "git symbolic-ref HEAD" > would report 'refs/heads/maint-2.38', bypassing the intermediate > symbolic ref at 'refs/heads/maint' that is pointed at by HEAD. > [...] > Signed-off-by: Junio C Hamano <gitster@pobox.com> > --- > diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt > @@ -46,6 +46,15 @@ OPTIONS > +--recurse:: > +--no-recurse:: > + When showing the value of <name> as a symbolic ref, if > + <name> refers to another symbolic ref, follow such a chain > + of symbolic refs until the result no longer points at a > + symbolic ref (`--recurse`, which is the default). > + `--no-recurse` stops after dereferencing only a single level > + of symbolic ref. With his recent documentation-normalization work, I suspect Ævar would appreciate an update to the synopsis, as well: SYNOPSIS -------- [verse] 'git symbolic-ref' [-m <reason>] <name> <ref> -'git symbolic-ref' [-q] [--short] <name> +'git symbolic-ref' [-q] [--short] [--no-recurse] <name> 'git symbolic-ref' --delete [-q] <name> or something similar (i.e. plain [--recurse] or composite [--[no-]-recurse] or [--recurse | --no-recurse]).
On 10/8/22 11:34, Junio C Hamano wrote: > Suppose you are managing many maintenance tracks in your project, > and some of the more recent ones are maint-2.36 and maint-2.37. > Further imagine that your project recently tagged the official 2.38 > release, which means you would need to start maint-2.38 track soon, > by doing: > > $ git checkout -b maint-2.38 v2.38.0^0 > $ git branch --list 'maint-2.3[6-9]' > * maint-2.38 > maint-2.36 > maint-2.37 > > So far, so good. But it also is reasonable to want not to have to > worry about which maintenance track is the latest, by pointing a > more generic-sounding 'maint' branch at it, by doing: > > $ git symbolic-ref refs/heads/maint refs/heads/maint-2.38 > > which would allow you to say "whichever it is, check out the latest > maintenance track", by doing: > > $ git checkout maint > $ git branch --show-current > maint-2.38 > > It is arguably better to say that we are on 'maint-2.38' rather than > on 'maint', and "git merge/pull" would record "into maint-2.38" and > not "into maint", so I think what we have is a good behaviour. > > One thing that is slightly irritating, however, is that I do not > think there is a good way (other than "cat .git/HEAD") to learn that > you checked out 'maint' to get into that state. Just like the output > of "git branch --show-current" shows above, "git symbolic-ref HEAD" > would report 'refs/heads/maint-2.38', bypassing the intermediate > symbolic ref at 'refs/heads/maint' that is pointed at by HEAD. > > The internal resolve_ref() API already has the necessary support for > stopping after resolving a single level of a symbolic-ref, and we > can expose it by adding a "--[no-]recurse" option to the command. > The example case above is from recent Git releases, right? I think the wording should instead use generalized version numbers. For example the still maintained release tracks are X.Y-1 and X.Y, but now X+1 have been tagged, which creates X+1.Y track. Thanks.
Hi Junio On 08/10/2022 05:34, Junio C Hamano wrote: > Suppose you are managing many maintenance tracks in your project, > and some of the more recent ones are maint-2.36 and maint-2.37. > Further imagine that your project recently tagged the official 2.38 > release, which means you would need to start maint-2.38 track soon, > by doing: > > $ git checkout -b maint-2.38 v2.38.0^0 > $ git branch --list 'maint-2.3[6-9]' > * maint-2.38 > maint-2.36 > maint-2.37 > > So far, so good. But it also is reasonable to want not to have to > worry about which maintenance track is the latest, by pointing a > more generic-sounding 'maint' branch at it, by doing: > > $ git symbolic-ref refs/heads/maint refs/heads/maint-2.38 > > which would allow you to say "whichever it is, check out the latest > maintenance track", by doing: > > $ git checkout maint > $ git branch --show-current > maint-2.38 > > It is arguably better to say that we are on 'maint-2.38' rather than > on 'maint', and "git merge/pull" would record "into maint-2.38" and > not "into maint", so I think what we have is a good behaviour. > > One thing that is slightly irritating, however, is that I do not > think there is a good way (other than "cat .git/HEAD") to learn that > you checked out 'maint' to get into that state. Just like the output > of "git branch --show-current" shows above, "git symbolic-ref HEAD" > would report 'refs/heads/maint-2.38', bypassing the intermediate > symbolic ref at 'refs/heads/maint' that is pointed at by HEAD. > > The internal resolve_ref() API already has the necessary support for > stopping after resolving a single level of a symbolic-ref, and we > can expose it by adding a "--[no-]recurse" option to the command. > > Signed-off-by: Junio C Hamano <gitster@pobox.com> > --- > > * Updates documentation, which was entirely lacking in v1, with no > other changes. > > Documentation/git-symbolic-ref.txt | 9 +++++++++ Is there also a need to update SubmittingPatches to clarify the distinctions between the generic name 'maint', the specific `maint-xx` branches, and the symbolic ref linkages from `maint`. The existence of the `maint-xx` branches, and where they came from, was something that eluded me for a long time. The descriptions give the impression there is just a single main branch, with a singular name, and that was the end of it. It was only much later when some security patches were pushed down to numerous older `maint-xx` branches. The use of the singular `maint` is also used in examples a number of places further cementing the singular maintenance view. A few words clarifying the `maint` vs `maint-x.yy` branches could help users past the confusion. -- Philip > builtin/symbolic-ref.c | 16 ++++++++++------ > t/t1401-symbolic-ref.sh | 14 ++++++++++++++ > 3 files changed, 33 insertions(+), 6 deletions(-) > > diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt > index ef68ad2b71..3b5a97ea2b 100644 > --- a/Documentation/git-symbolic-ref.txt > +++ b/Documentation/git-symbolic-ref.txt > @@ -46,6 +46,15 @@ OPTIONS > When showing the value of <name> as a symbolic ref, try to shorten the > value, e.g. from `refs/heads/master` to `master`. > > +--recurse:: > +--no-recurse:: > + When showing the value of <name> as a symbolic ref, if > + <name> refers to another symbolic ref, follow such a chain > + of symbolic refs until the result no longer points at a > + symbolic ref (`--recurse`, which is the default). > + `--no-recurse` stops after dereferencing only a single level > + of symbolic ref. > + > -m:: > Update the reflog for <name> with <reason>. This is valid only > when creating or updating a symbolic ref. > diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c > index e547a08d6c..1af27b3a68 100644 > --- a/builtin/symbolic-ref.c > +++ b/builtin/symbolic-ref.c > @@ -10,10 +10,13 @@ static const char * const git_symbolic_ref_usage[] = { > NULL > }; > > -static int check_symref(const char *HEAD, int quiet, int shorten, int print) > +static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print) > { > - int flag; > - const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag); > + int resolve_flags, flag; > + const char *refname; > + > + resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE); > + refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag); > > if (!refname) > die("No such ref: %s", HEAD); > @@ -35,13 +38,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print) > > int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) > { > - int quiet = 0, delete = 0, shorten = 0, ret = 0; > + int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0; > const char *msg = NULL; > struct option options[] = { > OPT__QUIET(&quiet, > N_("suppress error message for non-symbolic (detached) refs")), > OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")), > OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), > + OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")), > OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), > OPT_END(), > }; > @@ -55,7 +59,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) > if (delete) { > if (argc != 1) > usage_with_options(git_symbolic_ref_usage, options); > - ret = check_symref(argv[0], 1, 0, 0); > + ret = check_symref(argv[0], 1, 0, 0, 0); > if (ret) > die("Cannot delete %s, not a symbolic ref", argv[0]); > if (!strcmp(argv[0], "HEAD")) > @@ -65,7 +69,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) > > switch (argc) { > case 1: > - ret = check_symref(argv[0], quiet, shorten, 1); > + ret = check_symref(argv[0], quiet, shorten, recurse, 1); > break; > case 2: > if (!strcmp(argv[0], "HEAD") && > diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh > index 132a1b885a..773a6e8e01 100755 > --- a/t/t1401-symbolic-ref.sh > +++ b/t/t1401-symbolic-ref.sh > @@ -163,4 +163,18 @@ test_expect_success 'symbolic-ref can resolve d/f name (ENOTDIR)' ' > test_cmp expect actual > ' > > +test_expect_success 'symbolic-ref pointing at another' ' > + git update-ref refs/heads/maint-2.37 HEAD && > + git symbolic-ref refs/heads/maint refs/heads/maint-2.37 && > + git checkout maint && > + > + git symbolic-ref HEAD >actual && > + echo refs/heads/maint-2.37 >expect && > + test_cmp expect actual && > + > + git symbolic-ref --no-recurse HEAD >actual && > + echo refs/heads/maint >expect && > + test_cmp expect actual > +' > + > test_done
spelling correction On 08/10/2022 13:28, Philip Oakley wrote: > The existence of the `maint-xx` branches, and where they came from, was > something that eluded me for a long time. > > The descriptions give the impression there is just a single main branch, s/main/`maint`/ > with a singular name, and that was the end of it. It was only much later > when some security patches were pushed down to numerous older `maint-xx` > branches. The use of the singular `maint` is also used in examples a > number of places further cementing the singular maintenance view. > > A few words clarifying the `maint` vs `maint-x.yy` branches could help > users past the confusion.
Eric Sunshine <sunshine@sunshineco.com> writes: > With his recent documentation-normalization work, I suspect Ævar would > appreciate an update to the synopsis, as well: > > SYNOPSIS > -------- > [verse] > 'git symbolic-ref' [-m <reason>] <name> <ref> > -'git symbolic-ref' [-q] [--short] <name> > +'git symbolic-ref' [-q] [--short] [--no-recurse] <name> > 'git symbolic-ref' --delete [-q] <name> > > or something similar (i.e. plain [--recurse] or composite > [--[no-]-recurse] or [--recurse | --no-recurse]). Yeah, that looks good. I checked the in-code usage that used [<options>] (hence I didn't have to do anything new), but apparently forgot about the doc. As there is (and there will ever be) no configuration knob for this plumbing command to make the non-recursive behaviour the default, I think listing [--no-recurse] alone, not [--[no-]recurse], would be the right thing to do in the documentation. Thanks.
Philip Oakley <philipoakley@iee.email> writes: > Is there also a need to update SubmittingPatches to clarify the > distinctions between the generic name 'maint', the specific `maint-xx` > branches, and the symbolic ref linkages from `maint`. Not at all. Perhaps you are thinking about MaintNotes, but even there, general public do not have to be concerned about maint-xx most of the time. I myself would not be keeping branches for ancient maintenance tracks anywhere. As the older maintenance tracks, other than the most recent ones, do not get topics graduated to 'master' merged down by me (even though topic branches that fix notable bugs may fork from an older version to allow motivated third party maintainers to merge them down to older maintenance releases), there is not really a reason to keep branches like maint-2.15 for daily operation. Only when we need security updates that go back to older maintenance tracks, I may do $ git checkout -b maint-2.30 v2.30.5 $ git merge cve-something-fix ... edit release notes etc. ... $ git commit -m 'Git 2.30.6' $ git tag -s -m 'Git 2.30.6' v2.30.6 but after that is done, there is no reason to keep an old branch like maint-2.30 lying around, other than to make it easier to prepare for v2.30.7 (as maint-2.30 will remember what tag was the latest on that particular maintenance track). > The descriptions give the impression there is just a single main branch, > with a singular name, and that was the end of it. And that is the correct world view for most of the general public. https://github.com/git/git/ shows 'maint', 'master' ('main'), 'next', and 'seen' from the project code, plus 'todo' which holds an unrelated history that keeps track of maintenance tools and misc stuff. By the way, I do not personally have 'main' in my working repository. The only trick I have to help folks who expect to see 'main' instead of 'master' is the push refspec to distribute the integration results to various repositories. My 'master' is pushed to both 'master' and 'main' in destination repositories. The mirror/backup repository of my private working repository does have tentive branches, like the broken-out topic branches that are still active, which are pruned when they no longer are needed. Some maint-xx branches are also there but they are there not because they are necessary but because I just haven't bothered to clean them up ;-)
Bagas Sanjaya <bagasdotme@gmail.com> writes: > On 10/8/22 11:34, Junio C Hamano wrote: >> Suppose you are managing many maintenance tracks in your project, >> and some of the more recent ones are maint-2.36 and maint-2.37. > > The example case above is from recent Git releases, right? It is left to reader's imagination. Concrete numbers are easier to understand, as it would be clear that 2.36 comes before 2.37 which comes before 2.38.
Hi Junio, On 09/10/2022 13:07, Junio C Hamano wrote: > Philip Oakley <philipoakley@iee.email> writes: > >> Is there also a need to update SubmittingPatches to clarify the >> distinctions between the generic name 'maint', the specific `maint-xx` >> branches, and the symbolic ref linkages from `maint`. > Not at all. Perhaps you are thinking about MaintNotes, but even > there, general public do not have to be concerned about maint-xx > most of the time. I myself would not be keeping branches for > ancient maintenance tracks anywhere. As the older maintenance > tracks, other than the most recent ones, do not get topics graduated > to 'master' merged down by me (even though topic branches that fix > notable bugs may fork from an older version to allow motivated third > party maintainers to merge them down to older maintenance releases), > there is not really a reason to keep branches like maint-2.15 for > daily operation. Only when we need security updates that go back to > older maintenance tracks, I may do > > $ git checkout -b maint-2.30 v2.30.5 > $ git merge cve-something-fix > ... edit release notes etc. ... > $ git commit -m 'Git 2.30.6' > $ git tag -s -m 'Git 2.30.6' v2.30.6 > > but after that is done, there is no reason to keep an old branch > like maint-2.30 lying around, other than to make it easier to > prepare for v2.30.7 (as maint-2.30 will remember what tag was the > latest on that particular maintenance track). > >> The descriptions give the impression there is just a single main branch, >> with a singular name, and that was the end of it. > And that is the correct world view for most of the general public. > > https://github.com/git/git/ > > shows 'maint', 'master' ('main'), 'next', and 'seen' from the > project code, plus 'todo' which holds an unrelated history that > keeps track of maintenance tools and misc stuff. > > By the way, I do not personally have 'main' in my working > repository. The only trick I have to help folks who expect to see > 'main' instead of 'master' is the push refspec to distribute the > integration results to various repositories. My 'master' is pushed > to both 'master' and 'main' in destination repositories. > > The mirror/backup repository of my private working repository does > have tentive branches, like the broken-out topic branches that are > still active, which are pruned when they no longer are needed. > Some maint-xx branches are also there but they are there not because > they are necessary but because I just haven't bothered to clean them > up ;-) > Thanks for the clarification. My confusion about `maint` started quite a few years back and may be a mixture of a number of different issues, or simply my misunderstanding. I'm guessing here, but it may have been, that back then, that the maint branch was a link to the relevant maint-xx, rather than a 'symref' and that at that time the Git-for-Windows didn't really handle them, so `maint` may not have shown for me (similar to how RelNotes is configured in the Git repo). It may also have been early confusion about remotes and their branches, and the impression that I needed (or should expect) a local branch of the same name and this wasn't happening (obvious in retrospect), so I never found any `maint` in my repo. The whole 'remote tracking branches' stuff took a long time to understand and was non-obvious from my perspective. I suspect many newbies have similar issues (e.g. the current thread https://lore.kernel.org/git/DU2P194MB15841850A17436C7AE34C149E3239@DU2P194MB1584.EURP194.PROD.OUTLOOK.COM/T/#t) -- Philip
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index ef68ad2b71..3b5a97ea2b 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -46,6 +46,15 @@ OPTIONS When showing the value of <name> as a symbolic ref, try to shorten the value, e.g. from `refs/heads/master` to `master`. +--recurse:: +--no-recurse:: + When showing the value of <name> as a symbolic ref, if + <name> refers to another symbolic ref, follow such a chain + of symbolic refs until the result no longer points at a + symbolic ref (`--recurse`, which is the default). + `--no-recurse` stops after dereferencing only a single level + of symbolic ref. + -m:: Update the reflog for <name> with <reason>. This is valid only when creating or updating a symbolic ref. diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index e547a08d6c..1af27b3a68 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -10,10 +10,13 @@ static const char * const git_symbolic_ref_usage[] = { NULL }; -static int check_symref(const char *HEAD, int quiet, int shorten, int print) +static int check_symref(const char *HEAD, int quiet, int shorten, int recurse, int print) { - int flag; - const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag); + int resolve_flags, flag; + const char *refname; + + resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE); + refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag); if (!refname) die("No such ref: %s", HEAD); @@ -35,13 +38,14 @@ static int check_symref(const char *HEAD, int quiet, int shorten, int print) int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { - int quiet = 0, delete = 0, shorten = 0, ret = 0; + int quiet = 0, delete = 0, shorten = 0, recurse = 1, ret = 0; const char *msg = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("suppress error message for non-symbolic (detached) refs")), OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")), OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), + OPT_BOOL(0, "recurse", &recurse, N_("recursively dereference (default)")), OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_END(), }; @@ -55,7 +59,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) if (delete) { if (argc != 1) usage_with_options(git_symbolic_ref_usage, options); - ret = check_symref(argv[0], 1, 0, 0); + ret = check_symref(argv[0], 1, 0, 0, 0); if (ret) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) @@ -65,7 +69,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) switch (argc) { case 1: - ret = check_symref(argv[0], quiet, shorten, 1); + ret = check_symref(argv[0], quiet, shorten, recurse, 1); break; case 2: if (!strcmp(argv[0], "HEAD") && diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh index 132a1b885a..773a6e8e01 100755 --- a/t/t1401-symbolic-ref.sh +++ b/t/t1401-symbolic-ref.sh @@ -163,4 +163,18 @@ test_expect_success 'symbolic-ref can resolve d/f name (ENOTDIR)' ' test_cmp expect actual ' +test_expect_success 'symbolic-ref pointing at another' ' + git update-ref refs/heads/maint-2.37 HEAD && + git symbolic-ref refs/heads/maint refs/heads/maint-2.37 && + git checkout maint && + + git symbolic-ref HEAD >actual && + echo refs/heads/maint-2.37 >expect && + test_cmp expect actual && + + git symbolic-ref --no-recurse HEAD >actual && + echo refs/heads/maint >expect && + test_cmp expect actual +' + test_done
Suppose you are managing many maintenance tracks in your project, and some of the more recent ones are maint-2.36 and maint-2.37. Further imagine that your project recently tagged the official 2.38 release, which means you would need to start maint-2.38 track soon, by doing: $ git checkout -b maint-2.38 v2.38.0^0 $ git branch --list 'maint-2.3[6-9]' * maint-2.38 maint-2.36 maint-2.37 So far, so good. But it also is reasonable to want not to have to worry about which maintenance track is the latest, by pointing a more generic-sounding 'maint' branch at it, by doing: $ git symbolic-ref refs/heads/maint refs/heads/maint-2.38 which would allow you to say "whichever it is, check out the latest maintenance track", by doing: $ git checkout maint $ git branch --show-current maint-2.38 It is arguably better to say that we are on 'maint-2.38' rather than on 'maint', and "git merge/pull" would record "into maint-2.38" and not "into maint", so I think what we have is a good behaviour. One thing that is slightly irritating, however, is that I do not think there is a good way (other than "cat .git/HEAD") to learn that you checked out 'maint' to get into that state. Just like the output of "git branch --show-current" shows above, "git symbolic-ref HEAD" would report 'refs/heads/maint-2.38', bypassing the intermediate symbolic ref at 'refs/heads/maint' that is pointed at by HEAD. The internal resolve_ref() API already has the necessary support for stopping after resolving a single level of a symbolic-ref, and we can expose it by adding a "--[no-]recurse" option to the command. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- * Updates documentation, which was entirely lacking in v1, with no other changes. Documentation/git-symbolic-ref.txt | 9 +++++++++ builtin/symbolic-ref.c | 16 ++++++++++------ t/t1401-symbolic-ref.sh | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-)