2022-11-19 13:07:38 +00:00
|
|
|
#define USE_THE_INDEX_VARIABLE
|
2023-05-16 06:33:57 +00:00
|
|
|
#include "builtin.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2023-03-21 06:26:03 +00:00
|
|
|
#include "environment.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2014-10-01 10:28:42 +00:00
|
|
|
#include "lockfile.h"
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
#include "commit.h"
|
2005-12-27 22:36:49 +00:00
|
|
|
#include "tag.h"
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
#include "blob.h"
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
#include "refs.h"
|
2018-04-10 21:26:18 +00:00
|
|
|
#include "exec-cmd.h"
|
2023-04-11 07:41:49 +00:00
|
|
|
#include "object-name.h"
|
2007-10-07 18:54:08 +00:00
|
|
|
#include "parse-options.h"
|
2023-05-16 06:33:56 +00:00
|
|
|
#include "read-cache-ll.h"
|
describe: do not use cmd_*() as a subroutine
The cmd_foo() function is a moral equivalent of 'main' for a Git
subcommand 'git foo', and as such, it is allowed to do many things
that make it unsuitable to be called as a subroutine, including
- call exit(3) to terminate the process;
- allocate resource held and used throughout its lifetime, without
releasing it upon return/exit;
- rely on global variables being initialized at program startup,
and update them as needed, making another clean invocation of the
function impossible.
The call to cmd_diff_index() "git describe" makes has been working
by accident that the function did not call exit(3); it sets a bad
precedent for people to cut and paste.
We could invoke it via the run_command() interface, but the diff
family of commands have helper functions in diff-lib.c that are
meant to be usable as subroutines, and using the latter does not
make the resulting code all that longer. Use it.
Note that there is also an invocation of cmd_name_rev() at the end;
"git describe --contains" massages its command line arguments to be
suitable for "git name-rev" invocation and jumps to it, never to
regain control. This call is left as-is as an exception to the
rule. When we start to allow calling name-rev repeatedly as a
helper function, we would be able to remove this call as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-10 03:42:58 +00:00
|
|
|
#include "revision.h"
|
2009-10-21 13:35:22 +00:00
|
|
|
#include "diff.h"
|
2013-11-14 19:18:35 +00:00
|
|
|
#include "hashmap.h"
|
2023-03-21 06:26:05 +00:00
|
|
|
#include "setup.h"
|
2020-07-28 20:23:39 +00:00
|
|
|
#include "strvec.h"
|
2017-03-21 22:57:18 +00:00
|
|
|
#include "run-command.h"
|
2023-05-16 06:34:06 +00:00
|
|
|
#include "object-store-ll.h"
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
#include "list-objects.h"
|
2018-05-19 05:28:20 +00:00
|
|
|
#include "commit-slab.h"
|
2023-05-16 06:34:03 +00:00
|
|
|
#include "wildmatch.h"
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
|
2007-01-13 22:30:53 +00:00
|
|
|
#define MAX_TAGS (FLAG_BITS - 1)
|
2023-07-21 13:41:33 +00:00
|
|
|
#define DEFAULT_CANDIDATES 10
|
2007-01-13 22:30:53 +00:00
|
|
|
|
2018-05-19 05:28:20 +00:00
|
|
|
define_commit_slab(commit_names, struct commit_name *);
|
|
|
|
|
2007-10-07 18:54:08 +00:00
|
|
|
static const char * const describe_usage[] = {
|
2022-10-13 15:39:13 +00:00
|
|
|
N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]"),
|
|
|
|
N_("git describe [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]"),
|
|
|
|
N_("git describe <blob>"),
|
2007-10-07 18:54:08 +00:00
|
|
|
NULL
|
|
|
|
};
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
|
2007-01-13 22:30:53 +00:00
|
|
|
static int debug; /* Display lots of verbose info */
|
2008-10-13 14:39:46 +00:00
|
|
|
static int all; /* Any valid ref can be used */
|
|
|
|
static int tags; /* Allow lightweight tags */
|
2008-02-25 09:43:33 +00:00
|
|
|
static int longformat;
|
2013-05-17 20:56:18 +00:00
|
|
|
static int first_parent;
|
2010-10-28 18:28:04 +00:00
|
|
|
static int abbrev = -1; /* unspecified */
|
2023-07-21 13:41:33 +00:00
|
|
|
static int max_candidates = DEFAULT_CANDIDATES;
|
2013-11-14 19:18:35 +00:00
|
|
|
static struct hashmap names;
|
2010-12-09 06:47:29 +00:00
|
|
|
static int have_util;
|
2017-01-18 23:06:07 +00:00
|
|
|
static struct string_list patterns = STRING_LIST_INIT_NODUP;
|
2017-01-18 23:06:08 +00:00
|
|
|
static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
|
2008-03-02 16:51:57 +00:00
|
|
|
static int always;
|
2017-03-21 22:57:18 +00:00
|
|
|
static const char *suffix, *dirty, *broken;
|
2018-05-19 05:28:20 +00:00
|
|
|
static struct commit_names commit_names;
|
2009-10-21 13:35:22 +00:00
|
|
|
|
|
|
|
/* diff-index command arguments to check if working tree is dirty. */
|
|
|
|
static const char *diff_index_args[] = {
|
|
|
|
"diff-index", "--quiet", "HEAD", "--", NULL
|
|
|
|
};
|
|
|
|
|
2007-01-15 03:16:55 +00:00
|
|
|
struct commit_name {
|
2013-11-14 19:18:35 +00:00
|
|
|
struct hashmap_entry entry;
|
2017-02-21 23:47:22 +00:00
|
|
|
struct object_id peeled;
|
2008-02-28 06:22:36 +00:00
|
|
|
struct tag *tag;
|
2010-04-12 23:25:29 +00:00
|
|
|
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
|
|
|
|
unsigned name_checked:1;
|
describe: force long format for a name based on a mislocated tag
An annotated tag has two names: where it sits in the refs/tags
hierarchy and the tagname recorded in the "tag" field in the object
itself. They usually should match.
Since 212945d4 ("Teach git-describe to verify annotated tag names
before output", 2008-02-28), a commit described using an annotated
tag bases its name on the tagname from the object. While this was a
deliberate design decision to make it easier to converse about tags
with others, even if the tags happen to be fetched to a different
name than it was given by its creator, it had one downside.
The output from "git describe", at least in the modern Git, should
be usable as an object name to name the exact commit given to the
"git describe" command. Using the tagname, when two names differ,
breaks this property, when describing a commit that is directly
pointed at by such a tag. An annotated tag Bob made as "v1.0" may
sit at "refs/tags/v1.0-bob" in the ref hierarchy, and output from
"git describe v1.0-bob^0" would say "v1.0", but there may not be
any tag at "refs/tags/v1.0" locally or there may be another tag that
points at a different object.
Note that this won't be a problem if a commit being described is not
directly pointed at by such a mislocated tag. In the example in the
previous paragraph, describing a commit whose parent is v1.0-bob
would result in "v1.0" (i.e. the tagname taken from the tag object)
followed by "-1-gXXXXX" where XXXXX is the abbreviated object name,
and a string that ends with "-g" followed by a hexadecimal string is
an object name for the object whose name begins with hexadecimal
string (as long as it is unique), so it does not matter if the
leading part is "v1.0" or "v1.0-bob".
Show the name in the long format, i.e. with "-0-gXXXXX" suffix, when
the name we give is based on a mislocated annotated tag to ensure
that the output can be used as the object name for the object
originally given to the command to fix the issue.
While at it, remove an overly cautious dead code to protect against
an annotated tag object without the tagname. Such a tag is filtered
out much earlier in the codeflow, and will not reach this part of
the code.
Helped-by: Matheus Tavares <matheus.bernardino@usp.br>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 17:34:36 +00:00
|
|
|
unsigned misnamed:1;
|
2017-02-21 23:47:22 +00:00
|
|
|
struct object_id oid;
|
2013-05-25 09:08:00 +00:00
|
|
|
char *path;
|
2007-01-15 03:16:55 +00:00
|
|
|
};
|
2013-10-31 09:25:41 +00:00
|
|
|
|
2007-01-14 09:37:44 +00:00
|
|
|
static const char *prio_names[] = {
|
2017-03-27 16:50:05 +00:00
|
|
|
N_("head"), N_("lightweight"), N_("annotated"),
|
2007-01-14 09:37:44 +00:00
|
|
|
};
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
|
2022-08-25 17:09:48 +00:00
|
|
|
static int commit_name_neq(const void *cmp_data UNUSED,
|
2019-10-06 23:30:37 +00:00
|
|
|
const struct hashmap_entry *eptr,
|
|
|
|
const struct hashmap_entry *entry_or_key,
|
2017-06-30 19:14:05 +00:00
|
|
|
const void *peeled)
|
2013-11-14 19:18:35 +00:00
|
|
|
{
|
2019-10-06 23:30:37 +00:00
|
|
|
const struct commit_name *cn1, *cn2;
|
|
|
|
|
|
|
|
cn1 = container_of(eptr, const struct commit_name, entry);
|
|
|
|
cn2 = container_of(entry_or_key, const struct commit_name, entry);
|
2017-07-01 00:28:31 +00:00
|
|
|
|
2018-08-28 21:22:55 +00:00
|
|
|
return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);
|
2013-11-14 19:18:35 +00:00
|
|
|
}
|
|
|
|
|
2017-02-21 23:47:22 +00:00
|
|
|
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
|
2010-12-09 06:46:08 +00:00
|
|
|
{
|
2019-10-06 23:30:36 +00:00
|
|
|
return hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled,
|
|
|
|
struct commit_name, entry);
|
2010-12-09 06:47:29 +00:00
|
|
|
}
|
|
|
|
|
2010-04-12 23:25:29 +00:00
|
|
|
static int replace_name(struct commit_name *e,
|
|
|
|
int prio,
|
2017-02-21 23:47:22 +00:00
|
|
|
const struct object_id *oid,
|
2010-04-12 23:25:29 +00:00
|
|
|
struct tag **tag)
|
|
|
|
{
|
|
|
|
if (!e || e->prio < prio)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (e->prio == 2 && prio == 2) {
|
|
|
|
/* Multiple annotated tags point to the same commit.
|
|
|
|
* Select one to keep based upon their tagger date.
|
|
|
|
*/
|
|
|
|
struct tag *t;
|
|
|
|
|
|
|
|
if (!e->tag) {
|
2018-06-29 01:22:03 +00:00
|
|
|
t = lookup_tag(the_repository, &e->oid);
|
2010-04-12 23:25:29 +00:00
|
|
|
if (!t || parse_tag(t))
|
|
|
|
return 1;
|
|
|
|
e->tag = t;
|
|
|
|
}
|
|
|
|
|
2018-06-29 01:22:03 +00:00
|
|
|
t = lookup_tag(the_repository, oid);
|
2010-04-12 23:25:29 +00:00
|
|
|
if (!t || parse_tag(t))
|
|
|
|
return 0;
|
|
|
|
*tag = t;
|
|
|
|
|
|
|
|
if (e->tag->date < t->date)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-12-28 00:09:37 +00:00
|
|
|
static void add_to_known_names(const char *path,
|
2017-02-21 23:47:22 +00:00
|
|
|
const struct object_id *peeled,
|
2008-02-28 06:22:36 +00:00
|
|
|
int prio,
|
2017-02-21 23:47:22 +00:00
|
|
|
const struct object_id *oid)
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
{
|
2010-12-09 06:46:08 +00:00
|
|
|
struct commit_name *e = find_commit_name(peeled);
|
2010-04-12 23:25:29 +00:00
|
|
|
struct tag *tag = NULL;
|
2017-02-21 23:47:22 +00:00
|
|
|
if (replace_name(e, prio, oid, &tag)) {
|
2010-12-09 06:43:32 +00:00
|
|
|
if (!e) {
|
|
|
|
e = xmalloc(sizeof(struct commit_name));
|
2017-02-21 23:47:22 +00:00
|
|
|
oidcpy(&e->peeled, peeled);
|
2019-10-06 23:30:27 +00:00
|
|
|
hashmap_entry_init(&e->entry, oidhash(peeled));
|
2019-10-06 23:30:29 +00:00
|
|
|
hashmap_add(&names, &e->entry);
|
2013-05-25 09:08:00 +00:00
|
|
|
e->path = NULL;
|
2010-12-09 06:43:32 +00:00
|
|
|
}
|
2010-04-12 23:25:29 +00:00
|
|
|
e->tag = tag;
|
2007-01-15 03:16:55 +00:00
|
|
|
e->prio = prio;
|
2010-04-12 23:25:29 +00:00
|
|
|
e->name_checked = 0;
|
describe: force long format for a name based on a mislocated tag
An annotated tag has two names: where it sits in the refs/tags
hierarchy and the tagname recorded in the "tag" field in the object
itself. They usually should match.
Since 212945d4 ("Teach git-describe to verify annotated tag names
before output", 2008-02-28), a commit described using an annotated
tag bases its name on the tagname from the object. While this was a
deliberate design decision to make it easier to converse about tags
with others, even if the tags happen to be fetched to a different
name than it was given by its creator, it had one downside.
The output from "git describe", at least in the modern Git, should
be usable as an object name to name the exact commit given to the
"git describe" command. Using the tagname, when two names differ,
breaks this property, when describing a commit that is directly
pointed at by such a tag. An annotated tag Bob made as "v1.0" may
sit at "refs/tags/v1.0-bob" in the ref hierarchy, and output from
"git describe v1.0-bob^0" would say "v1.0", but there may not be
any tag at "refs/tags/v1.0" locally or there may be another tag that
points at a different object.
Note that this won't be a problem if a commit being described is not
directly pointed at by such a mislocated tag. In the example in the
previous paragraph, describing a commit whose parent is v1.0-bob
would result in "v1.0" (i.e. the tagname taken from the tag object)
followed by "-1-gXXXXX" where XXXXX is the abbreviated object name,
and a string that ends with "-g" followed by a hexadecimal string is
an object name for the object whose name begins with hexadecimal
string (as long as it is unique), so it does not matter if the
leading part is "v1.0" or "v1.0-bob".
Show the name in the long format, i.e. with "-0-gXXXXX" suffix, when
the name we give is based on a mislocated annotated tag to ensure
that the output can be used as the object name for the object
originally given to the command to fix the issue.
While at it, remove an overly cautious dead code to protect against
an annotated tag object without the tagname. Such a tag is filtered
out much earlier in the codeflow, and will not reach this part of
the code.
Helped-by: Matheus Tavares <matheus.bernardino@usp.br>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 17:34:36 +00:00
|
|
|
e->misnamed = 0;
|
2017-02-21 23:47:22 +00:00
|
|
|
oidcpy(&e->oid, oid);
|
2013-05-25 09:08:00 +00:00
|
|
|
free(e->path);
|
|
|
|
e->path = xstrdup(path);
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-19 10:08:32 +00:00
|
|
|
static int get_name(const char *path, const struct object_id *oid,
|
2022-08-25 17:09:48 +00:00
|
|
|
int flag UNUSED, void *cb_data UNUSED)
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
{
|
2017-09-20 01:10:10 +00:00
|
|
|
int is_tag = 0;
|
2015-05-25 18:38:34 +00:00
|
|
|
struct object_id peeled;
|
2013-02-28 21:53:00 +00:00
|
|
|
int is_annotated, prio;
|
2017-09-20 01:10:10 +00:00
|
|
|
const char *path_to_match = NULL;
|
|
|
|
|
|
|
|
if (skip_prefix(path, "refs/tags/", &path_to_match)) {
|
|
|
|
is_tag = 1;
|
|
|
|
} else if (all) {
|
|
|
|
if ((exclude_patterns.nr || patterns.nr) &&
|
|
|
|
!skip_prefix(path, "refs/heads/", &path_to_match) &&
|
|
|
|
!skip_prefix(path, "refs/remotes/", &path_to_match)) {
|
|
|
|
/* Only accept reference of known type if there are match/exclude patterns */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Reject anything outside refs/tags/ unless --all */
|
2008-02-24 08:07:28 +00:00
|
|
|
return 0;
|
2017-09-20 01:10:10 +00:00
|
|
|
}
|
2008-02-24 08:07:28 +00:00
|
|
|
|
2017-01-18 23:06:08 +00:00
|
|
|
/*
|
|
|
|
* If we're given exclude patterns, first exclude any tag which match
|
|
|
|
* any of the exclude pattern.
|
|
|
|
*/
|
|
|
|
if (exclude_patterns.nr) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
for_each_string_list_item(item, &exclude_patterns) {
|
2017-09-20 01:10:10 +00:00
|
|
|
if (!wildmatch(item->string, path_to_match, 0))
|
2017-01-18 23:06:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 23:06:07 +00:00
|
|
|
/*
|
|
|
|
* If we're given patterns, accept only tags which match at least one
|
|
|
|
* pattern.
|
|
|
|
*/
|
|
|
|
if (patterns.nr) {
|
2017-09-16 05:53:44 +00:00
|
|
|
int found = 0;
|
2017-01-18 23:06:07 +00:00
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
for_each_string_list_item(item, &patterns) {
|
2017-09-20 01:10:10 +00:00
|
|
|
if (!wildmatch(item->string, path_to_match, 0)) {
|
2017-09-16 05:53:44 +00:00
|
|
|
found = 1;
|
2017-01-18 23:06:07 +00:00
|
|
|
break;
|
2017-09-16 05:53:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-18 23:06:07 +00:00
|
|
|
|
2017-09-16 05:53:44 +00:00
|
|
|
if (!found)
|
2017-01-18 23:06:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-02-28 21:53:00 +00:00
|
|
|
|
|
|
|
/* Is it annotated? */
|
refs: switch peel_ref() to peel_iterated_oid()
The peel_ref() interface is confusing and error-prone:
- it's typically used by ref iteration callbacks that have both a
refname and oid. But since they pass only the refname, we may load
the ref value from the filesystem again. This is inefficient, but
also means we are open to a race if somebody simultaneously updates
the ref. E.g., this:
int some_ref_cb(const char *refname, const struct object_id *oid, ...)
{
if (!peel_ref(refname, &peeled))
printf("%s peels to %s",
oid_to_hex(oid), oid_to_hex(&peeled);
}
could print nonsense. It is correct to say "refname peels to..."
(you may see the "before" value or the "after" value, either of
which is consistent), but mentioning both oids may be mixing
before/after values.
Worse, whether this is possible depends on whether the optimization
to read from the current iterator value kicks in. So it is actually
not possible with:
for_each_ref(some_ref_cb);
but it _is_ possible with:
head_ref(some_ref_cb);
which does not use the iterator mechanism (though in practice, HEAD
should never peel to anything, so this may not be triggerable).
- it must take a fully-qualified refname for the read_ref_full() code
path to work. Yet we routinely pass it partial refnames from
callbacks to for_each_tag_ref(), etc. This happens to work when
iterating because there we do not call read_ref_full() at all, and
only use the passed refname to check if it is the same as the
iterator. But the requirements for the function parameters are quite
unclear.
Instead of taking a refname, let's instead take an oid. That fixes both
problems. It's a little funny for a "ref" function not to involve refs
at all. The key thing is that it's optimizing under the hood based on
having access to the ref iterator. So let's change the name to make it
clear why you'd want this function versus just peel_object().
There are two other directions I considered but rejected:
- we could pass the peel information into the each_ref_fn callback.
However, we don't know if the caller actually wants it or not. For
packed-refs, providing it is essentially free. But for loose refs,
we actually have to peel the object, which would be wasteful in most
cases. We could likewise pass in a flag to the callback indicating
whether the peeled information is known, but that complicates those
callbacks, as they then have to decide whether to manually peel
themselves. Plus it requires changing the interface of every
callback, whether they care about peeling or not, and there are many
of them.
- we could make a function to return the peeled value of the current
iterated ref (computing it if necessary), and BUG() otherwise. I.e.:
int peel_current_iterated_ref(struct object_id *out);
Each of the current callers is an each_ref_fn callback, so they'd
mostly be happy. But:
- we use those callbacks with functions like head_ref(), which do
not use the iteration code. So we'd need to handle the fallback
case there, anyway.
- it's possible that a caller would want to call into generic code
that sometimes is used during iteration and sometimes not. This
encapsulates the logic to do the fast thing when possible, and
fallback when necessary.
The implementation is mostly obvious, but I want to call out a few
things in the patch:
- the test-tool coverage for peel_ref() is now meaningless, as it all
collapses to a single peel_object() call (arguably they were pretty
uninteresting before; the tricky part of that function is the
fast-path we see during iteration, but these calls didn't trigger
that). I've just dropped it entirely, though note that some other
tests relied on the tags we created; I've moved that creation to the
tests where it matters.
- we no longer need to take a ref_store parameter, since we'd never
look up a ref now. We do still rely on a global "current iterator"
variable which _could_ be kept per-ref-store. But in practice this
is only useful if there are multiple recursive iterations, at which
point the more appropriate solution is probably a stack of
iterators. No caller used the actual ref-store parameter anyway
(they all call the wrapper that passes the_repository).
- the original only kicked in the optimization when the "refname"
pointer matched (i.e., not string comparison). We do likewise with
the "oid" parameter here, but fall back to doing an actual oideq()
call. This in theory lets us kick in the optimization more often,
though in practice no current caller cares. It should never be
wrong, though (peeling is a property of an object, so two refs
pointing to the same object would peel identically).
- the original took care not to touch the peeled out-parameter unless
we found something to put in it. But no caller cares about this, and
anyway, it is enforced by peel_object() itself (and even in the
optimized iterator case, that's where we eventually end up). We can
shorten the code and avoid an extra copy by just passing the
out-parameter through the stack.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-20 19:44:43 +00:00
|
|
|
if (!peel_iterated_oid(oid, &peeled)) {
|
convert "oidcmp() == 0" to oideq()
Using the more restrictive oideq() should, in the long run,
give the compiler more opportunities to optimize these
callsites. For now, this conversion should be a complete
noop with respect to the generated code.
The result is also perhaps a little more readable, as it
avoids the "zero is equal" idiom. Since it's so prevalent in
C, I think seasoned programmers tend not to even notice it
anymore, but it can sometimes make for awkward double
negations (e.g., we can drop a few !!oidcmp() instances
here).
This patch was generated almost entirely by the included
coccinelle patch. This mechanical conversion should be
completely safe, because we check explicitly for cases where
oidcmp() is compared to 0, which is what oideq() is doing
under the hood. Note that we don't have to catch "!oidcmp()"
separately; coccinelle's standard isomorphisms make sure the
two are treated equivalently.
I say "almost" because I did hand-edit the coccinelle output
to fix up a few style violations (it mostly keeps the
original formatting, but sometimes unwraps long lines).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 21:22:40 +00:00
|
|
|
is_annotated = !oideq(oid, &peeled);
|
2008-02-24 08:07:25 +00:00
|
|
|
} else {
|
2015-05-25 18:38:34 +00:00
|
|
|
oidcpy(&peeled, oid);
|
2013-02-28 21:53:00 +00:00
|
|
|
is_annotated = 0;
|
2008-02-24 08:07:25 +00:00
|
|
|
}
|
2005-12-28 00:09:37 +00:00
|
|
|
|
2013-02-28 21:53:00 +00:00
|
|
|
/*
|
|
|
|
* By default, we only use annotated tags, but with --tags
|
|
|
|
* we fall back to lightweight ones (even without --tags,
|
|
|
|
* we still remember lightweight ones, only to give hints
|
|
|
|
* in an error message). --all allows any refs to be used.
|
2005-12-27 22:40:17 +00:00
|
|
|
*/
|
2013-02-28 21:53:00 +00:00
|
|
|
if (is_annotated)
|
|
|
|
prio = 2;
|
|
|
|
else if (is_tag)
|
|
|
|
prio = 1;
|
2005-12-28 00:09:37 +00:00
|
|
|
else
|
|
|
|
prio = 0;
|
|
|
|
|
2017-02-21 23:47:22 +00:00
|
|
|
add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-10 11:39:47 +00:00
|
|
|
struct possible_tag {
|
|
|
|
struct commit_name *name;
|
2007-01-14 09:37:44 +00:00
|
|
|
int depth;
|
|
|
|
int found_order;
|
2007-01-13 22:30:53 +00:00
|
|
|
unsigned flag_within;
|
2007-01-10 11:39:47 +00:00
|
|
|
};
|
|
|
|
|
2007-01-14 09:37:44 +00:00
|
|
|
static int compare_pt(const void *a_, const void *b_)
|
|
|
|
{
|
|
|
|
struct possible_tag *a = (struct possible_tag *)a_;
|
|
|
|
struct possible_tag *b = (struct possible_tag *)b_;
|
|
|
|
if (a->depth != b->depth)
|
|
|
|
return a->depth - b->depth;
|
|
|
|
if (a->found_order != b->found_order)
|
|
|
|
return a->found_order - b->found_order;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-27 06:54:21 +00:00
|
|
|
static unsigned long finish_depth_computation(
|
|
|
|
struct commit_list **list,
|
|
|
|
struct possible_tag *best)
|
|
|
|
{
|
|
|
|
unsigned long seen_commits = 0;
|
|
|
|
while (*list) {
|
|
|
|
struct commit *c = pop_commit(list);
|
|
|
|
struct commit_list *parents = c->parents;
|
|
|
|
seen_commits++;
|
|
|
|
if (c->object.flags & best->flag_within) {
|
|
|
|
struct commit_list *a = *list;
|
|
|
|
while (a) {
|
|
|
|
struct commit *i = a->item;
|
|
|
|
if (!(i->object.flags & best->flag_within))
|
|
|
|
break;
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
if (!a)
|
|
|
|
break;
|
|
|
|
} else
|
|
|
|
best->depth++;
|
|
|
|
while (parents) {
|
|
|
|
struct commit *p = parents->item;
|
2023-03-28 13:58:48 +00:00
|
|
|
repo_parse_commit(the_repository, p);
|
2007-01-27 06:54:21 +00:00
|
|
|
if (!(p->object.flags & SEEN))
|
2010-11-27 01:58:14 +00:00
|
|
|
commit_list_insert_by_date(p, list);
|
2007-01-27 06:54:21 +00:00
|
|
|
p->object.flags |= c->object.flags;
|
|
|
|
parents = parents->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return seen_commits;
|
|
|
|
}
|
|
|
|
|
2017-11-16 02:00:38 +00:00
|
|
|
static void append_name(struct commit_name *n, struct strbuf *dst)
|
2008-02-28 06:22:36 +00:00
|
|
|
{
|
|
|
|
if (n->prio == 2 && !n->tag) {
|
2018-06-29 01:22:03 +00:00
|
|
|
n->tag = lookup_tag(the_repository, &n->oid);
|
2010-04-12 23:25:29 +00:00
|
|
|
if (!n->tag || parse_tag(n->tag))
|
2011-02-22 23:42:23 +00:00
|
|
|
die(_("annotated tag %s not available"), n->path);
|
2010-04-12 23:25:29 +00:00
|
|
|
}
|
|
|
|
if (n->tag && !n->name_checked) {
|
describe: force long format for a name based on a mislocated tag
An annotated tag has two names: where it sits in the refs/tags
hierarchy and the tagname recorded in the "tag" field in the object
itself. They usually should match.
Since 212945d4 ("Teach git-describe to verify annotated tag names
before output", 2008-02-28), a commit described using an annotated
tag bases its name on the tagname from the object. While this was a
deliberate design decision to make it easier to converse about tags
with others, even if the tags happen to be fetched to a different
name than it was given by its creator, it had one downside.
The output from "git describe", at least in the modern Git, should
be usable as an object name to name the exact commit given to the
"git describe" command. Using the tagname, when two names differ,
breaks this property, when describing a commit that is directly
pointed at by such a tag. An annotated tag Bob made as "v1.0" may
sit at "refs/tags/v1.0-bob" in the ref hierarchy, and output from
"git describe v1.0-bob^0" would say "v1.0", but there may not be
any tag at "refs/tags/v1.0" locally or there may be another tag that
points at a different object.
Note that this won't be a problem if a commit being described is not
directly pointed at by such a mislocated tag. In the example in the
previous paragraph, describing a commit whose parent is v1.0-bob
would result in "v1.0" (i.e. the tagname taken from the tag object)
followed by "-1-gXXXXX" where XXXXX is the abbreviated object name,
and a string that ends with "-g" followed by a hexadecimal string is
an object name for the object whose name begins with hexadecimal
string (as long as it is unique), so it does not matter if the
leading part is "v1.0" or "v1.0-bob".
Show the name in the long format, i.e. with "-0-gXXXXX" suffix, when
the name we give is based on a mislocated annotated tag to ensure
that the output can be used as the object name for the object
originally given to the command to fix the issue.
While at it, remove an overly cautious dead code to protect against
an annotated tag object without the tagname. Such a tag is filtered
out much earlier in the codeflow, and will not reach this part of
the code.
Helped-by: Matheus Tavares <matheus.bernardino@usp.br>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 17:34:36 +00:00
|
|
|
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) {
|
|
|
|
warning(_("tag '%s' is externally known as '%s'"),
|
|
|
|
n->path, n->tag->tag);
|
|
|
|
n->misnamed = 1;
|
|
|
|
}
|
2010-04-12 23:25:29 +00:00
|
|
|
n->name_checked = 1;
|
2008-02-28 06:22:36 +00:00
|
|
|
}
|
|
|
|
|
2017-12-11 17:24:54 +00:00
|
|
|
if (n->tag) {
|
|
|
|
if (all)
|
2018-01-23 21:16:28 +00:00
|
|
|
strbuf_addstr(dst, "tags/");
|
2017-11-16 02:00:38 +00:00
|
|
|
strbuf_addstr(dst, n->tag->tag);
|
2017-12-11 17:24:54 +00:00
|
|
|
} else {
|
2017-11-16 02:00:38 +00:00
|
|
|
strbuf_addstr(dst, n->path);
|
2017-12-11 17:24:54 +00:00
|
|
|
}
|
2008-03-03 21:08:26 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 02:00:38 +00:00
|
|
|
static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
|
2008-03-03 21:08:26 +00:00
|
|
|
{
|
2023-03-28 13:58:46 +00:00
|
|
|
strbuf_addf(dst, "-%d-g%s", depth,
|
|
|
|
repo_find_unique_abbrev(the_repository, oid, abbrev));
|
2008-02-28 06:22:36 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 02:00:38 +00:00
|
|
|
static void describe_commit(struct object_id *oid, struct strbuf *dst)
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
{
|
2007-01-13 22:30:53 +00:00
|
|
|
struct commit *cmit, *gave_up_on = NULL;
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
struct commit_list *list;
|
|
|
|
struct commit_name *n;
|
2007-01-14 09:37:44 +00:00
|
|
|
struct possible_tag all_matches[MAX_TAGS];
|
2007-01-13 22:30:53 +00:00
|
|
|
unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
|
|
|
|
unsigned long seen_commits = 0;
|
2009-10-28 22:10:06 +00:00
|
|
|
unsigned int unannotated_cnt = 0;
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
|
2018-06-29 01:21:58 +00:00
|
|
|
cmit = lookup_commit_reference(the_repository, oid);
|
2006-01-11 21:57:42 +00:00
|
|
|
|
2017-02-21 23:47:22 +00:00
|
|
|
n = find_commit_name(&cmit->object.oid);
|
2009-11-18 13:32:26 +00:00
|
|
|
if (n && (tags || all || n->prio == 2)) {
|
2008-03-03 21:08:26 +00:00
|
|
|
/*
|
|
|
|
* Exact match to an existing ref.
|
|
|
|
*/
|
2017-11-16 02:00:38 +00:00
|
|
|
append_name(n, dst);
|
describe: force long format for a name based on a mislocated tag
An annotated tag has two names: where it sits in the refs/tags
hierarchy and the tagname recorded in the "tag" field in the object
itself. They usually should match.
Since 212945d4 ("Teach git-describe to verify annotated tag names
before output", 2008-02-28), a commit described using an annotated
tag bases its name on the tagname from the object. While this was a
deliberate design decision to make it easier to converse about tags
with others, even if the tags happen to be fetched to a different
name than it was given by its creator, it had one downside.
The output from "git describe", at least in the modern Git, should
be usable as an object name to name the exact commit given to the
"git describe" command. Using the tagname, when two names differ,
breaks this property, when describing a commit that is directly
pointed at by such a tag. An annotated tag Bob made as "v1.0" may
sit at "refs/tags/v1.0-bob" in the ref hierarchy, and output from
"git describe v1.0-bob^0" would say "v1.0", but there may not be
any tag at "refs/tags/v1.0" locally or there may be another tag that
points at a different object.
Note that this won't be a problem if a commit being described is not
directly pointed at by such a mislocated tag. In the example in the
previous paragraph, describing a commit whose parent is v1.0-bob
would result in "v1.0" (i.e. the tagname taken from the tag object)
followed by "-1-gXXXXX" where XXXXX is the abbreviated object name,
and a string that ends with "-g" followed by a hexadecimal string is
an object name for the object whose name begins with hexadecimal
string (as long as it is unique), so it does not matter if the
leading part is "v1.0" or "v1.0-bob".
Show the name in the long format, i.e. with "-0-gXXXXX" suffix, when
the name we give is based on a mislocated annotated tag to ensure
that the output can be used as the object name for the object
originally given to the command to fix the issue.
While at it, remove an overly cautious dead code to protect against
an annotated tag object without the tagname. Such a tag is filtered
out much earlier in the codeflow, and will not reach this part of
the code.
Helped-by: Matheus Tavares <matheus.bernardino@usp.br>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 17:34:36 +00:00
|
|
|
if (n->misnamed || longformat)
|
2019-09-05 19:59:42 +00:00
|
|
|
append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst);
|
2017-03-21 22:57:18 +00:00
|
|
|
if (suffix)
|
2017-11-16 02:00:38 +00:00
|
|
|
strbuf_addstr(dst, suffix);
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-24 08:07:31 +00:00
|
|
|
if (!max_candidates)
|
2015-11-10 02:22:28 +00:00
|
|
|
die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
|
2007-01-13 22:30:53 +00:00
|
|
|
if (debug)
|
2017-11-16 02:00:37 +00:00
|
|
|
fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
|
2007-01-13 22:30:53 +00:00
|
|
|
|
2010-12-09 06:47:29 +00:00
|
|
|
if (!have_util) {
|
2013-11-14 19:18:35 +00:00
|
|
|
struct hashmap_iter iter;
|
|
|
|
struct commit *c;
|
2018-05-19 05:28:20 +00:00
|
|
|
struct commit_name *n;
|
|
|
|
|
|
|
|
init_commit_names(&commit_names);
|
2019-10-06 23:30:41 +00:00
|
|
|
hashmap_for_each_entry(&names, &iter, n,
|
2019-10-06 23:30:38 +00:00
|
|
|
entry /* member name */) {
|
2018-06-29 01:21:57 +00:00
|
|
|
c = lookup_commit_reference_gently(the_repository,
|
|
|
|
&n->peeled, 1);
|
2013-11-14 19:18:35 +00:00
|
|
|
if (c)
|
2018-05-19 05:28:20 +00:00
|
|
|
*commit_names_at(&commit_names, c) = n;
|
2013-11-14 19:18:35 +00:00
|
|
|
}
|
2010-12-09 06:47:29 +00:00
|
|
|
have_util = 1;
|
|
|
|
}
|
|
|
|
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
list = NULL;
|
2007-01-13 22:30:53 +00:00
|
|
|
cmit->object.flags = SEEN;
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
commit_list_insert(cmit, &list);
|
|
|
|
while (list) {
|
2007-01-10 11:39:47 +00:00
|
|
|
struct commit *c = pop_commit(&list);
|
2007-01-13 22:27:52 +00:00
|
|
|
struct commit_list *parents = c->parents;
|
2018-05-19 05:28:20 +00:00
|
|
|
struct commit_name **slot;
|
|
|
|
|
2007-01-13 22:30:53 +00:00
|
|
|
seen_commits++;
|
2018-05-19 05:28:20 +00:00
|
|
|
slot = commit_names_peek(&commit_names, c);
|
|
|
|
n = slot ? *slot : NULL;
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
if (n) {
|
2009-10-28 22:10:06 +00:00
|
|
|
if (!tags && !all && n->prio < 2) {
|
|
|
|
unannotated_cnt++;
|
|
|
|
} else if (match_cnt < max_candidates) {
|
2007-01-13 22:30:53 +00:00
|
|
|
struct possible_tag *t = &all_matches[match_cnt++];
|
|
|
|
t->name = n;
|
|
|
|
t->depth = seen_commits - 1;
|
|
|
|
t->flag_within = 1u << match_cnt;
|
2007-01-25 17:40:03 +00:00
|
|
|
t->found_order = match_cnt;
|
2007-01-13 22:30:53 +00:00
|
|
|
c->object.flags |= t->flag_within;
|
|
|
|
if (n->prio == 2)
|
|
|
|
annotated_cnt++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gave_up_on = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
|
|
|
|
struct possible_tag *t = &all_matches[cur_match];
|
|
|
|
if (!(c->object.flags & t->flag_within))
|
|
|
|
t->depth++;
|
|
|
|
}
|
2020-02-26 17:48:53 +00:00
|
|
|
/* Stop if last remaining path already covered by best candidate(s) */
|
2007-01-13 22:30:53 +00:00
|
|
|
if (annotated_cnt && !list) {
|
2020-02-26 17:48:53 +00:00
|
|
|
int best_depth = INT_MAX;
|
|
|
|
unsigned best_within = 0;
|
|
|
|
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
|
|
|
|
struct possible_tag *t = &all_matches[cur_match];
|
|
|
|
if (t->depth < best_depth) {
|
|
|
|
best_depth = t->depth;
|
|
|
|
best_within = t->flag_within;
|
|
|
|
} else if (t->depth == best_depth) {
|
|
|
|
best_within |= t->flag_within;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((c->object.flags & best_within) == best_within) {
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, _("finished search at %s\n"),
|
|
|
|
oid_to_hex(&c->object.oid));
|
|
|
|
break;
|
|
|
|
}
|
2007-01-13 22:27:52 +00:00
|
|
|
}
|
|
|
|
while (parents) {
|
|
|
|
struct commit *p = parents->item;
|
2023-03-28 13:58:48 +00:00
|
|
|
repo_parse_commit(the_repository, p);
|
2007-01-13 22:30:53 +00:00
|
|
|
if (!(p->object.flags & SEEN))
|
2010-11-27 01:58:14 +00:00
|
|
|
commit_list_insert_by_date(p, &list);
|
2007-01-13 22:30:53 +00:00
|
|
|
p->object.flags |= c->object.flags;
|
2007-01-13 22:27:52 +00:00
|
|
|
parents = parents->next;
|
2013-05-17 20:56:18 +00:00
|
|
|
|
|
|
|
if (first_parent)
|
|
|
|
break;
|
2007-01-10 11:39:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-02 16:51:57 +00:00
|
|
|
if (!match_cnt) {
|
2017-11-16 02:00:36 +00:00
|
|
|
struct object_id *cmit_oid = &cmit->object.oid;
|
2008-03-02 16:51:57 +00:00
|
|
|
if (always) {
|
2018-03-12 02:27:30 +00:00
|
|
|
strbuf_add_unique_abbrev(dst, cmit_oid, abbrev);
|
2017-03-21 22:57:18 +00:00
|
|
|
if (suffix)
|
2017-11-16 02:00:38 +00:00
|
|
|
strbuf_addstr(dst, suffix);
|
2008-03-02 16:51:57 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-10-28 22:10:06 +00:00
|
|
|
if (unannotated_cnt)
|
2011-02-22 23:42:23 +00:00
|
|
|
die(_("No annotated tags can describe '%s'.\n"
|
|
|
|
"However, there were unannotated tags: try --tags."),
|
2017-11-16 02:00:36 +00:00
|
|
|
oid_to_hex(cmit_oid));
|
2009-10-28 22:10:06 +00:00
|
|
|
else
|
2011-02-22 23:42:23 +00:00
|
|
|
die(_("No tags can describe '%s'.\n"
|
|
|
|
"Try --always, or create some tags."),
|
2017-11-16 02:00:36 +00:00
|
|
|
oid_to_hex(cmit_oid));
|
2008-03-02 16:51:57 +00:00
|
|
|
}
|
2007-01-10 11:39:47 +00:00
|
|
|
|
2016-09-29 15:27:31 +00:00
|
|
|
QSORT(all_matches, match_cnt, compare_pt);
|
2007-01-27 06:54:21 +00:00
|
|
|
|
|
|
|
if (gave_up_on) {
|
2010-11-27 01:58:14 +00:00
|
|
|
commit_list_insert_by_date(gave_up_on, &list);
|
2007-01-27 06:54:21 +00:00
|
|
|
seen_commits--;
|
|
|
|
}
|
|
|
|
seen_commits += finish_depth_computation(&list, &all_matches[0]);
|
|
|
|
free_commit_list(list);
|
|
|
|
|
2007-01-13 22:30:53 +00:00
|
|
|
if (debug) {
|
2017-03-27 16:50:05 +00:00
|
|
|
static int label_width = -1;
|
|
|
|
if (label_width < 0) {
|
|
|
|
int i, w;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(prio_names); i++) {
|
|
|
|
w = strlen(_(prio_names[i]));
|
|
|
|
if (label_width < w)
|
|
|
|
label_width = w;
|
|
|
|
}
|
|
|
|
}
|
2007-01-13 22:30:53 +00:00
|
|
|
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
|
|
|
|
struct possible_tag *t = &all_matches[cur_match];
|
2017-03-27 16:50:05 +00:00
|
|
|
fprintf(stderr, " %-*s %8d %s\n",
|
|
|
|
label_width, _(prio_names[t->name->prio]),
|
2007-01-13 22:30:53 +00:00
|
|
|
t->depth, t->name->path);
|
|
|
|
}
|
2011-02-22 23:42:23 +00:00
|
|
|
fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
|
2007-01-13 22:30:53 +00:00
|
|
|
if (gave_up_on) {
|
|
|
|
fprintf(stderr,
|
2011-02-22 23:42:23 +00:00
|
|
|
_("more than %i tags found; listed %i most recent\n"
|
|
|
|
"gave up search at %s\n"),
|
2007-01-13 22:30:53 +00:00
|
|
|
max_candidates, max_candidates,
|
2015-11-10 02:22:28 +00:00
|
|
|
oid_to_hex(&gave_up_on->object.oid));
|
2007-01-13 22:30:53 +00:00
|
|
|
}
|
2007-01-10 11:39:47 +00:00
|
|
|
}
|
2008-02-28 06:22:36 +00:00
|
|
|
|
2017-11-16 02:00:38 +00:00
|
|
|
append_name(all_matches[0].name, dst);
|
describe: force long format for a name based on a mislocated tag
An annotated tag has two names: where it sits in the refs/tags
hierarchy and the tagname recorded in the "tag" field in the object
itself. They usually should match.
Since 212945d4 ("Teach git-describe to verify annotated tag names
before output", 2008-02-28), a commit described using an annotated
tag bases its name on the tagname from the object. While this was a
deliberate design decision to make it easier to converse about tags
with others, even if the tags happen to be fetched to a different
name than it was given by its creator, it had one downside.
The output from "git describe", at least in the modern Git, should
be usable as an object name to name the exact commit given to the
"git describe" command. Using the tagname, when two names differ,
breaks this property, when describing a commit that is directly
pointed at by such a tag. An annotated tag Bob made as "v1.0" may
sit at "refs/tags/v1.0-bob" in the ref hierarchy, and output from
"git describe v1.0-bob^0" would say "v1.0", but there may not be
any tag at "refs/tags/v1.0" locally or there may be another tag that
points at a different object.
Note that this won't be a problem if a commit being described is not
directly pointed at by such a mislocated tag. In the example in the
previous paragraph, describing a commit whose parent is v1.0-bob
would result in "v1.0" (i.e. the tagname taken from the tag object)
followed by "-1-gXXXXX" where XXXXX is the abbreviated object name,
and a string that ends with "-g" followed by a hexadecimal string is
an object name for the object whose name begins with hexadecimal
string (as long as it is unique), so it does not matter if the
leading part is "v1.0" or "v1.0-bob".
Show the name in the long format, i.e. with "-0-gXXXXX" suffix, when
the name we give is based on a mislocated annotated tag to ensure
that the output can be used as the object name for the object
originally given to the command to fix the issue.
While at it, remove an overly cautious dead code to protect against
an annotated tag object without the tagname. Such a tag is filtered
out much earlier in the codeflow, and will not reach this part of
the code.
Helped-by: Matheus Tavares <matheus.bernardino@usp.br>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 17:34:36 +00:00
|
|
|
if (all_matches[0].name->misnamed || abbrev)
|
2017-11-16 02:00:38 +00:00
|
|
|
append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
|
2017-03-21 22:57:18 +00:00
|
|
|
if (suffix)
|
2017-11-16 02:00:38 +00:00
|
|
|
strbuf_addstr(dst, suffix);
|
|
|
|
}
|
|
|
|
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
struct process_commit_data {
|
|
|
|
struct object_id current_commit;
|
|
|
|
struct object_id looking_for;
|
|
|
|
struct strbuf *dst;
|
|
|
|
struct rev_info *revs;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void process_commit(struct commit *commit, void *data)
|
|
|
|
{
|
|
|
|
struct process_commit_data *pcd = data;
|
|
|
|
pcd->current_commit = commit->object.oid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void process_object(struct object *obj, const char *path, void *data)
|
|
|
|
{
|
|
|
|
struct process_commit_data *pcd = data;
|
|
|
|
|
convert "oidcmp() == 0" to oideq()
Using the more restrictive oideq() should, in the long run,
give the compiler more opportunities to optimize these
callsites. For now, this conversion should be a complete
noop with respect to the generated code.
The result is also perhaps a little more readable, as it
avoids the "zero is equal" idiom. Since it's so prevalent in
C, I think seasoned programmers tend not to even notice it
anymore, but it can sometimes make for awkward double
negations (e.g., we can drop a few !!oidcmp() instances
here).
This patch was generated almost entirely by the included
coccinelle patch. This mechanical conversion should be
completely safe, because we check explicitly for cases where
oidcmp() is compared to 0, which is what oideq() is doing
under the hood. Note that we don't have to catch "!oidcmp()"
separately; coccinelle's standard isomorphisms make sure the
two are treated equivalently.
I say "almost" because I did hand-edit the coccinelle output
to fix up a few style violations (it mostly keeps the
original formatting, but sometimes unwraps long lines).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 21:22:40 +00:00
|
|
|
if (oideq(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
reset_revision_walk();
|
|
|
|
describe_commit(&pcd->current_commit, pcd->dst);
|
|
|
|
strbuf_addf(pcd->dst, ":%s", path);
|
|
|
|
free_commit_list(pcd->revs->commits);
|
|
|
|
pcd->revs->commits = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void describe_blob(struct object_id oid, struct strbuf *dst)
|
|
|
|
{
|
|
|
|
struct rev_info revs;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec args = STRVEC_INIT;
|
2021-04-26 01:02:56 +00:00
|
|
|
struct process_commit_data pcd = { *null_oid(), oid, dst, &revs};
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushl(&args, "internal: The first arg is not parsed",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
"--objects", "--in-commit-order", "--reverse", "HEAD",
|
|
|
|
NULL);
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
|
2018-09-21 15:57:38 +00:00
|
|
|
repo_init_revisions(the_repository, &revs, NULL);
|
2020-07-29 00:37:20 +00:00
|
|
|
if (setup_revisions(args.nr, args.v, &revs, NULL) > 1)
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
BUG("setup_revisions could not handle all args?");
|
|
|
|
|
|
|
|
if (prepare_revision_walk(&revs))
|
|
|
|
die("revision walk setup failed");
|
|
|
|
|
|
|
|
traverse_commit_list(&revs, process_commit, process_object, &pcd);
|
|
|
|
reset_revision_walk();
|
2022-04-13 20:01:36 +00:00
|
|
|
release_revisions(&revs);
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 02:00:38 +00:00
|
|
|
static void describe(const char *arg, int last_one)
|
|
|
|
{
|
|
|
|
struct object_id oid;
|
|
|
|
struct commit *cmit;
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
fprintf(stderr, _("describe %s\n"), arg);
|
|
|
|
|
2023-03-28 13:58:46 +00:00
|
|
|
if (repo_get_oid(the_repository, arg, &oid))
|
2017-11-16 02:00:38 +00:00
|
|
|
die(_("Not a valid object name %s"), arg);
|
2018-06-29 01:21:57 +00:00
|
|
|
cmit = lookup_commit_reference_gently(the_repository, &oid, 1);
|
2017-11-16 02:00:38 +00:00
|
|
|
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
if (cmit)
|
|
|
|
describe_commit(&oid, &sb);
|
2018-04-25 18:20:59 +00:00
|
|
|
else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB)
|
builtin/describe.c: describe a blob
Sometimes users are given a hash of an object and they want to
identify it further (ex.: Use verify-pack to find the largest blobs,
but what are these? or [1])
When describing commits, we try to anchor them to tags or refs, as these
are conceptually on a higher level than the commit. And if there is no ref
or tag that matches exactly, we're out of luck. So we employ a heuristic
to make up a name for the commit. These names are ambiguous, there might
be different tags or refs to anchor to, and there might be different
path in the DAG to travel to arrive at the commit precisely.
When describing a blob, we want to describe the blob from a higher layer
as well, which is a tuple of (commit, deep/path) as the tree objects
involved are rather uninteresting. The same blob can be referenced by
multiple commits, so how we decide which commit to use? This patch
implements a rather naive approach on this: As there are no back pointers
from blobs to commits in which the blob occurs, we'll start walking from
any tips available, listing the blobs in-order of the commit and once we
found the blob, we'll take the first commit that listed the blob. For
example
git describe --tags v0.99:Makefile
conversion-901-g7672db20c2:Makefile
tells us the Makefile as it was in v0.99 was introduced in commit 7672db20.
The walking is performed in reverse order to show the introduction of a
blob rather than its last occurrence.
[1] https://stackoverflow.com/questions/223678/which-commit-has-this-blob
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-16 02:00:39 +00:00
|
|
|
describe_blob(oid, &sb);
|
|
|
|
else
|
|
|
|
die(_("%s is neither a commit nor blob"), arg);
|
2017-11-16 02:00:38 +00:00
|
|
|
|
|
|
|
puts(sb.buf);
|
2007-01-10 11:39:47 +00:00
|
|
|
|
2007-01-13 22:30:53 +00:00
|
|
|
if (!last_one)
|
|
|
|
clear_commit_marks(cmit, -1);
|
2017-11-16 02:00:38 +00:00
|
|
|
|
|
|
|
strbuf_release(&sb);
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
}
|
|
|
|
|
2023-07-21 13:41:33 +00:00
|
|
|
static int option_parse_exact_match(const struct option *opt, const char *arg,
|
|
|
|
int unset)
|
|
|
|
{
|
|
|
|
BUG_ON_OPT_ARG(arg);
|
|
|
|
|
|
|
|
max_candidates = unset ? DEFAULT_CANDIDATES : 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-10 11:36:36 +00:00
|
|
|
int cmd_describe(int argc, const char **argv, const char *prefix)
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
{
|
2007-05-21 07:20:25 +00:00
|
|
|
int contains = 0;
|
2007-10-07 18:54:08 +00:00
|
|
|
struct option options[] = {
|
2013-08-03 11:51:19 +00:00
|
|
|
OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")),
|
|
|
|
OPT_BOOL(0, "debug", &debug, N_("debug search strategy on stderr")),
|
|
|
|
OPT_BOOL(0, "all", &all, N_("use any ref")),
|
|
|
|
OPT_BOOL(0, "tags", &tags, N_("use any tag, even unannotated")),
|
|
|
|
OPT_BOOL(0, "long", &longformat, N_("always use long format")),
|
|
|
|
OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")),
|
2007-10-07 18:54:08 +00:00
|
|
|
OPT__ABBREV(&abbrev),
|
2023-07-21 13:41:33 +00:00
|
|
|
OPT_CALLBACK_F(0, "exact-match", NULL, NULL,
|
|
|
|
N_("only output exact matches"),
|
|
|
|
PARSE_OPT_NOARG, option_parse_exact_match),
|
2007-10-07 18:54:08 +00:00
|
|
|
OPT_INTEGER(0, "candidates", &max_candidates,
|
2012-08-20 12:32:07 +00:00
|
|
|
N_("consider <n> most recent tags (default: 10)")),
|
2017-01-18 23:06:07 +00:00
|
|
|
OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
|
2012-08-20 12:32:07 +00:00
|
|
|
N_("only consider tags matching <pattern>")),
|
2017-01-18 23:06:08 +00:00
|
|
|
OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"),
|
|
|
|
N_("do not consider tags matching <pattern>")),
|
2013-08-03 11:51:19 +00:00
|
|
|
OPT_BOOL(0, "always", &always,
|
|
|
|
N_("show abbreviated commit object as fallback")),
|
2012-08-20 12:32:07 +00:00
|
|
|
{OPTION_STRING, 0, "dirty", &dirty, N_("mark"),
|
2013-08-03 11:51:19 +00:00
|
|
|
N_("append <mark> on dirty working tree (default: \"-dirty\")"),
|
|
|
|
PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
|
2017-03-21 22:57:18 +00:00
|
|
|
{OPTION_STRING, 0, "broken", &broken, N_("mark"),
|
|
|
|
N_("append <mark> on broken working tree (default: \"-broken\")"),
|
|
|
|
PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
|
2007-10-07 18:54:08 +00:00
|
|
|
OPT_END(),
|
|
|
|
};
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
|
2010-10-28 18:28:04 +00:00
|
|
|
git_config(git_default_config, NULL);
|
2009-05-23 18:53:12 +00:00
|
|
|
argc = parse_options(argc, argv, prefix, options, describe_usage, 0);
|
2010-10-28 18:28:04 +00:00
|
|
|
if (abbrev < 0)
|
|
|
|
abbrev = DEFAULT_ABBREV;
|
|
|
|
|
2008-02-24 08:07:31 +00:00
|
|
|
if (max_candidates < 0)
|
|
|
|
max_candidates = 0;
|
2007-10-07 18:54:08 +00:00
|
|
|
else if (max_candidates > MAX_TAGS)
|
|
|
|
max_candidates = MAX_TAGS;
|
2006-01-11 21:57:42 +00:00
|
|
|
|
2007-01-10 11:36:29 +00:00
|
|
|
save_commit_buffer = 0;
|
2006-09-14 01:03:59 +00:00
|
|
|
|
2008-02-25 09:43:33 +00:00
|
|
|
if (longformat && abbrev == 0)
|
2022-01-05 20:02:16 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--long", "--abbrev=0");
|
2008-02-25 09:43:33 +00:00
|
|
|
|
2007-05-21 07:20:25 +00:00
|
|
|
if (contains) {
|
2017-01-18 23:06:07 +00:00
|
|
|
struct string_list_item *item;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec args;
|
2013-07-07 21:42:23 +00:00
|
|
|
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_init(&args);
|
|
|
|
strvec_pushl(&args, "name-rev",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
"--peel-tag", "--name-only", "--no-undefined",
|
|
|
|
NULL);
|
2008-03-02 16:51:57 +00:00
|
|
|
if (always)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&args, "--always");
|
2007-12-21 21:49:54 +00:00
|
|
|
if (!all) {
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&args, "--tags");
|
2017-01-18 23:06:07 +00:00
|
|
|
for_each_string_list_item(item, &patterns)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushf(&args, "--refs=refs/tags/%s", item->string);
|
2017-01-18 23:06:08 +00:00
|
|
|
for_each_string_list_item(item, &exclude_patterns)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushf(&args, "--exclude=refs/tags/%s", item->string);
|
2013-07-07 21:42:23 +00:00
|
|
|
}
|
describe --contains: default to HEAD when no commit-ish is given
'git describe --contains' doesn't default to HEAD when no commit is
given, and it doesn't produce any output, not even an error:
~/src/git ((v2.5.0))$ ./git describe --contains
~/src/git ((v2.5.0))$ ./git describe --contains HEAD
v2.5.0^0
Unlike other 'git describe' options, the '--contains' code path is
implemented by calling 'name-rev' with a bunch of options plus all the
commit-ishes that were passed to 'git describe'. If no commit-ish was
present, then 'name-rev' got invoked with none, which then leads to the
behavior illustrated above.
Porcelain commands usually default to HEAD when no commit-ish is given,
and 'git describe' already does so in all other cases, so it should do
so with '--contains' as well.
Pass HEAD to 'name-rev' when no commit-ish is given on the command line
to make '--contains' behave consistently with other 'git describe'
options. While at it, use argv_array_pushv() instead of the loop to
pass commit-ishes to 'git name-rev'.
'git describe's short help already indicates that the commit-ish is
optional, but the synopsis in the man page doesn't, so update it
accordingly as well.
Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-24 16:15:18 +00:00
|
|
|
if (argc)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushv(&args, argv);
|
describe --contains: default to HEAD when no commit-ish is given
'git describe --contains' doesn't default to HEAD when no commit is
given, and it doesn't produce any output, not even an error:
~/src/git ((v2.5.0))$ ./git describe --contains
~/src/git ((v2.5.0))$ ./git describe --contains HEAD
v2.5.0^0
Unlike other 'git describe' options, the '--contains' code path is
implemented by calling 'name-rev' with a bunch of options plus all the
commit-ishes that were passed to 'git describe'. If no commit-ish was
present, then 'name-rev' got invoked with none, which then leads to the
behavior illustrated above.
Porcelain commands usually default to HEAD when no commit-ish is given,
and 'git describe' already does so in all other cases, so it should do
so with '--contains' as well.
Pass HEAD to 'name-rev' when no commit-ish is given on the command line
to make '--contains' behave consistently with other 'git describe'
options. While at it, use argv_array_pushv() instead of the loop to
pass commit-ishes to 'git name-rev'.
'git describe's short help already indicates that the commit-ish is
optional, but the synopsis in the man page doesn't, so update it
accordingly as well.
Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-24 16:15:18 +00:00
|
|
|
else
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&args, "HEAD");
|
2020-07-29 00:37:20 +00:00
|
|
|
return cmd_name_rev(args.nr, args.v, prefix);
|
2007-05-21 07:20:25 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 21:22:55 +00:00
|
|
|
hashmap_init(&names, commit_name_neq, NULL, 0);
|
2015-05-25 18:38:34 +00:00
|
|
|
for_each_rawref(get_name, NULL);
|
2017-09-06 15:43:48 +00:00
|
|
|
if (!hashmap_get_size(&names) && !always)
|
2011-02-22 23:42:23 +00:00
|
|
|
die(_("No names found, cannot describe anything."));
|
2009-10-17 16:30:48 +00:00
|
|
|
|
2007-10-07 18:54:08 +00:00
|
|
|
if (argc == 0) {
|
2017-03-21 22:57:18 +00:00
|
|
|
if (broken) {
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushv(&cp.args, diff_index_args);
|
2017-03-21 22:57:18 +00:00
|
|
|
cp.git_cmd = 1;
|
|
|
|
cp.no_stdin = 1;
|
|
|
|
cp.no_stdout = 1;
|
|
|
|
|
|
|
|
if (!dirty)
|
|
|
|
dirty = "-dirty";
|
|
|
|
|
|
|
|
switch (run_command(&cp)) {
|
|
|
|
case 0:
|
|
|
|
suffix = NULL;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
suffix = dirty;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* diff-index aborted abnormally */
|
|
|
|
suffix = broken;
|
|
|
|
}
|
|
|
|
} else if (dirty) {
|
2018-05-09 20:55:38 +00:00
|
|
|
struct lock_file index_lock = LOCK_INIT;
|
describe: do not use cmd_*() as a subroutine
The cmd_foo() function is a moral equivalent of 'main' for a Git
subcommand 'git foo', and as such, it is allowed to do many things
that make it unsuitable to be called as a subroutine, including
- call exit(3) to terminate the process;
- allocate resource held and used throughout its lifetime, without
releasing it upon return/exit;
- rely on global variables being initialized at program startup,
and update them as needed, making another clean invocation of the
function impossible.
The call to cmd_diff_index() "git describe" makes has been working
by accident that the function did not call exit(3); it sets a bad
precedent for people to cut and paste.
We could invoke it via the run_command() interface, but the diff
family of commands have helper functions in diff-lib.c that are
meant to be usable as subroutines, and using the latter does not
make the resulting code all that longer. Use it.
Note that there is also an invocation of cmd_name_rev() at the end;
"git describe --contains" massages its command line arguments to be
suitable for "git name-rev" invocation and jumps to it, never to
regain control. This call is left as-is as an exception to the
rule. When we start to allow calling name-rev repeatedly as a
helper function, we would be able to remove this call as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-10 03:42:58 +00:00
|
|
|
struct rev_info revs;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec args = STRVEC_INIT;
|
diff: drop useless return from run_diff_{files,index} functions
Neither of these functions ever returns a value other than zero.
Instead, they expect unrecoverable errors to exit immediately, and
things like "--exit-code" are stored inside the diff_options struct to
be handled later via diff_result_code().
Some callers do check the return values, but many don't bother. Let's
drop the useless return values, which are misleading callers about how
the functions work. This could be seen as a step in the wrong direction,
as we might want to eventually "lib-ify" these to more cleanly return
errors up the stack, in which case we'd have to add the return values
back in. But there are some benefits to doing this now:
1. In the current code, somebody could accidentally add a "return -1"
to one of the functions, which would be erroneously ignored by many
callers. By removing the return code, the compiler can notice the
mismatch and force the developer to decide what to do.
Obviously the other option here is that we could start consistently
checking the error code in every caller. But it would be dead code,
and we wouldn't get any compile-time help in catching new cases.
2. It communicates the situation to callers, who may want to choose a
different function. These functions are really thin wrappers for
doing git-diff-files and git-diff-index within the process. But
callers who care about recovering from an error here are probably
better off using the underlying library functions, many of
which do return errors.
If somebody eventually wants to teach these functions to propagate
errors, they'll have to switch back to returning a value, effectively
reverting this patch. But at least then they will be starting with a
level playing field: they know that they will need to inspect each
caller to see how it should handle the error.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-21 20:18:55 +00:00
|
|
|
int fd;
|
2011-08-01 01:52:41 +00:00
|
|
|
|
2019-02-03 06:00:24 +00:00
|
|
|
setup_work_tree();
|
2023-04-03 16:47:49 +00:00
|
|
|
prepare_repo_settings(the_repository);
|
|
|
|
the_repository->settings.command_requires_full_index = 0;
|
2022-11-19 13:07:38 +00:00
|
|
|
repo_read_index(the_repository);
|
2011-08-01 01:52:41 +00:00
|
|
|
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
|
|
|
|
NULL, NULL, NULL);
|
2022-11-19 13:07:38 +00:00
|
|
|
fd = repo_hold_locked_index(the_repository,
|
|
|
|
&index_lock, 0);
|
2011-08-01 01:52:41 +00:00
|
|
|
if (0 <= fd)
|
2019-01-12 02:13:27 +00:00
|
|
|
repo_update_index_if_able(the_repository, &index_lock);
|
2011-08-01 01:52:41 +00:00
|
|
|
|
2018-09-21 15:57:38 +00:00
|
|
|
repo_init_revisions(the_repository, &revs, prefix);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushv(&args, diff_index_args);
|
2020-07-29 00:37:20 +00:00
|
|
|
if (setup_revisions(args.nr, args.v, &revs, NULL) != 1)
|
describe: do not use cmd_*() as a subroutine
The cmd_foo() function is a moral equivalent of 'main' for a Git
subcommand 'git foo', and as such, it is allowed to do many things
that make it unsuitable to be called as a subroutine, including
- call exit(3) to terminate the process;
- allocate resource held and used throughout its lifetime, without
releasing it upon return/exit;
- rely on global variables being initialized at program startup,
and update them as needed, making another clean invocation of the
function impossible.
The call to cmd_diff_index() "git describe" makes has been working
by accident that the function did not call exit(3); it sets a bad
precedent for people to cut and paste.
We could invoke it via the run_command() interface, but the diff
family of commands have helper functions in diff-lib.c that are
meant to be usable as subroutines, and using the latter does not
make the resulting code all that longer. Use it.
Note that there is also an invocation of cmd_name_rev() at the end;
"git describe --contains" massages its command line arguments to be
suitable for "git name-rev" invocation and jumps to it, never to
regain control. This call is left as-is as an exception to the
rule. When we start to allow calling name-rev repeatedly as a
helper function, we would be able to remove this call as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-10 03:42:58 +00:00
|
|
|
BUG("malformed internal diff-index command line");
|
diff: drop useless return from run_diff_{files,index} functions
Neither of these functions ever returns a value other than zero.
Instead, they expect unrecoverable errors to exit immediately, and
things like "--exit-code" are stored inside the diff_options struct to
be handled later via diff_result_code().
Some callers do check the return values, but many don't bother. Let's
drop the useless return values, which are misleading callers about how
the functions work. This could be seen as a step in the wrong direction,
as we might want to eventually "lib-ify" these to more cleanly return
errors up the stack, in which case we'd have to add the return values
back in. But there are some benefits to doing this now:
1. In the current code, somebody could accidentally add a "return -1"
to one of the functions, which would be erroneously ignored by many
callers. By removing the return code, the compiler can notice the
mismatch and force the developer to decide what to do.
Obviously the other option here is that we could start consistently
checking the error code in every caller. But it would be dead code,
and we wouldn't get any compile-time help in catching new cases.
2. It communicates the situation to callers, who may want to choose a
different function. These functions are really thin wrappers for
doing git-diff-files and git-diff-index within the process. But
callers who care about recovering from an error here are probably
better off using the underlying library functions, many of
which do return errors.
If somebody eventually wants to teach these functions to propagate
errors, they'll have to switch back to returning a value, effectively
reverting this patch. But at least then they will be starting with a
level playing field: they know that they will need to inspect each
caller to see how it should handle the error.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-21 20:18:55 +00:00
|
|
|
run_diff_index(&revs, 0);
|
describe: do not use cmd_*() as a subroutine
The cmd_foo() function is a moral equivalent of 'main' for a Git
subcommand 'git foo', and as such, it is allowed to do many things
that make it unsuitable to be called as a subroutine, including
- call exit(3) to terminate the process;
- allocate resource held and used throughout its lifetime, without
releasing it upon return/exit;
- rely on global variables being initialized at program startup,
and update them as needed, making another clean invocation of the
function impossible.
The call to cmd_diff_index() "git describe" makes has been working
by accident that the function did not call exit(3); it sets a bad
precedent for people to cut and paste.
We could invoke it via the run_command() interface, but the diff
family of commands have helper functions in diff-lib.c that are
meant to be usable as subroutines, and using the latter does not
make the resulting code all that longer. Use it.
Note that there is also an invocation of cmd_name_rev() at the end;
"git describe --contains" massages its command line arguments to be
suitable for "git name-rev" invocation and jumps to it, never to
regain control. This call is left as-is as an exception to the
rule. When we start to allow calling name-rev repeatedly as a
helper function, we would be able to remove this call as well.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-10 03:42:58 +00:00
|
|
|
|
diff: drop useless return from run_diff_{files,index} functions
Neither of these functions ever returns a value other than zero.
Instead, they expect unrecoverable errors to exit immediately, and
things like "--exit-code" are stored inside the diff_options struct to
be handled later via diff_result_code().
Some callers do check the return values, but many don't bother. Let's
drop the useless return values, which are misleading callers about how
the functions work. This could be seen as a step in the wrong direction,
as we might want to eventually "lib-ify" these to more cleanly return
errors up the stack, in which case we'd have to add the return values
back in. But there are some benefits to doing this now:
1. In the current code, somebody could accidentally add a "return -1"
to one of the functions, which would be erroneously ignored by many
callers. By removing the return code, the compiler can notice the
mismatch and force the developer to decide what to do.
Obviously the other option here is that we could start consistently
checking the error code in every caller. But it would be dead code,
and we wouldn't get any compile-time help in catching new cases.
2. It communicates the situation to callers, who may want to choose a
different function. These functions are really thin wrappers for
doing git-diff-files and git-diff-index within the process. But
callers who care about recovering from an error here are probably
better off using the underlying library functions, many of
which do return errors.
If somebody eventually wants to teach these functions to propagate
errors, they'll have to switch back to returning a value, effectively
reverting this patch. But at least then they will be starting with a
level playing field: they know that they will need to inspect each
caller to see how it should handle the error.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-21 20:18:55 +00:00
|
|
|
if (!diff_result_code(&revs.diffopt, 0))
|
2017-03-21 22:57:18 +00:00
|
|
|
suffix = NULL;
|
|
|
|
else
|
|
|
|
suffix = dirty;
|
2022-04-13 20:01:36 +00:00
|
|
|
release_revisions(&revs);
|
2011-08-01 01:52:41 +00:00
|
|
|
}
|
2006-01-16 06:25:35 +00:00
|
|
|
describe("HEAD", 1);
|
2009-10-21 13:35:22 +00:00
|
|
|
} else if (dirty) {
|
2022-01-05 20:02:24 +00:00
|
|
|
die(_("option '%s' and commit-ishes cannot be used together"), "--dirty");
|
2017-03-21 22:57:18 +00:00
|
|
|
} else if (broken) {
|
2022-01-05 20:02:24 +00:00
|
|
|
die(_("option '%s' and commit-ishes cannot be used together"), "--broken");
|
2007-10-07 18:54:08 +00:00
|
|
|
} else {
|
2013-10-31 09:25:41 +00:00
|
|
|
while (argc-- > 0)
|
2007-10-07 18:54:08 +00:00
|
|
|
describe(*argv++, argc == 0);
|
|
|
|
}
|
Add a "git-describe" command
It shows you the most recent tag that is reachable from a particular
commit is.
Maybe this is something that "git-name-rev" should be taught to do,
instead of having a separate command for it. Regardless, I find it useful.
What it does is to take any random commit, and "name" it by looking up the
most recent commit that is tagged and reachable from that commit. If the
match is exact, it will just print out that ref-name directly. Otherwise
it will print out the ref-name, followed by the 8-character "short SHA".
IOW, with something like Junios current tree, I get:
[torvalds@g5 git]$ git-describe parent
refs/tags/v1.0.4-g2414721b
ie the current head of my "parent" branch (ie Junio) is based on v1.0.4,
but since it has a few commits on top of that, it has added the git hash
of the thing to the end: "-g" + 8-char shorthand for the commit
2414721b194453f058079d897d13c4e377f92dc6.
Doing a "git-describe" on a tag-name will just show the full tag path:
[torvalds@g5 git]$ git-describe v1.0.4
refs/tags/v1.0.4
unless there are _other_ tags pointing to that commit, in which case it
will just choose one at random.
This is useful for two things:
- automatic version naming in Makefiles, for example. We could use it in
git itself: when doing "git --version", we could use this to give a
much more useful description of exactly what version was installed.
- for any random commit (say, you use "gitk <pathname>" or
"git-whatchanged" to look at what has changed in some file), you can
figure out what the last version of the repo was. Ie, say I find a bug
in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
refs/tags/v2.6.14-rc4-g39ca371c
and I now know that it was _not_ in v2.6.14-rc4, but was presumably in
v2.6.14-rc5.
The latter is useful when you want to see what "version timeframe" a
commit happened in.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-12-24 21:50:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|