-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-name-rev tries to find nice symbolic names for commits. It does so by walking the commits from the refs. When the symbolic name is ambiguous, the following heuristic is applied: Try to avoid too many ~'s, and if two ambiguous names have the same count of ~'s, take the one whose last number is smaller. With "--tags", the names are derived only from tags. With "--stdin", the stdin is parsed, and after every sha1 for which a name could be found, the name is appended. (Try "git log | git name-rev --stdin".) Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Junio C Hamano <junkio@cox.net>
- Loading branch information
Johannes Schindelin
authored and
Junio C Hamano
committed
Oct 26, 2005
1 parent
f3123c4
commit bd321bc
Showing
3 changed files
with
313 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
git-name-rev(1) | ||
=============== | ||
|
||
NAME | ||
---- | ||
git-name-rev - Find symbolic names for given revs. | ||
|
||
|
||
SYNOPSIS | ||
-------- | ||
'git-name-rev' [--tags] ( --all | --stdin | <commitish>... ) | ||
|
||
DESCRIPTION | ||
----------- | ||
Finds symbolic names suitable for human digestion for revisions given in any | ||
format parsable by git-rev-parse. | ||
|
||
|
||
OPTIONS | ||
------- | ||
|
||
--tags:: | ||
Do not use branch names, but only tags to name the commits | ||
|
||
--all:: | ||
List all commits reachable from all refs | ||
|
||
--stdin:: | ||
Read from stdin, append "(<rev_name>)" to all sha1's of name'able | ||
commits, and pass to stdout | ||
|
||
EXAMPLE | ||
------- | ||
|
||
Given a commit, find out where it is relative to the local refs. Say somebody | ||
wrote you about that phantastic commit 33db5f4d9027a10e477ccf054b2c1ab94f74c85a. | ||
Of course, you look into the commit, but that only tells you what happened, but | ||
not the context. | ||
|
||
Enter git-name-rev: | ||
|
||
------------ | ||
% git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a | ||
------------ | ||
|
||
Now you are wiser, because you know that it happened 940 revisions before v0.99. | ||
|
||
Another nice thing you can do is: | ||
|
||
------------ | ||
% git log | git name-rev --stdin | ||
------------ | ||
|
||
|
||
Author | ||
------ | ||
Written by Johannes Schindelin <Johannes.Schindelin@gmx.de> | ||
|
||
Documentation | ||
-------------- | ||
Documentation by Johannes Schindelin. | ||
|
||
GIT | ||
--- | ||
Part of the gitlink:git[7] suite | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
#include <stdlib.h> | ||
#include "cache.h" | ||
#include "commit.h" | ||
#include "tag.h" | ||
#include "refs.h" | ||
|
||
static const char name_rev_usage[] = | ||
"git-name-rev [--tags] ( --all | --stdin | commitish [commitish...] )\n"; | ||
|
||
typedef struct rev_name { | ||
const char *tip_name; | ||
int merge_traversals; | ||
int generation; | ||
} rev_name; | ||
|
||
static long cutoff = LONG_MAX; | ||
|
||
static void name_rev(struct commit *commit, | ||
const char *tip_name, int merge_traversals, int generation, | ||
int deref) | ||
{ | ||
struct rev_name *name = (struct rev_name *)commit->object.util; | ||
struct commit_list *parents; | ||
int parent_number = 0; | ||
|
||
if (!commit->object.parsed) | ||
parse_commit(commit); | ||
|
||
if (commit->date < cutoff) | ||
return; | ||
|
||
if (deref) { | ||
char *new_name = xmalloc(strlen(tip_name)+3); | ||
strcpy(new_name, tip_name); | ||
strcat(new_name, "^0"); | ||
tip_name = new_name; | ||
|
||
if (generation) | ||
die("generation: %d, but deref?", generation); | ||
} | ||
|
||
if (name == NULL) { | ||
name = xmalloc(sizeof(rev_name)); | ||
commit->object.util = name; | ||
goto copy_data; | ||
} else if (name->merge_traversals > merge_traversals || | ||
(name->merge_traversals == merge_traversals && | ||
name->generation > generation)) { | ||
copy_data: | ||
name->tip_name = tip_name; | ||
name->merge_traversals = merge_traversals; | ||
name->generation = generation; | ||
} else | ||
return; | ||
|
||
for (parents = commit->parents; | ||
parents; | ||
parents = parents->next, parent_number++) { | ||
if (parent_number > 0) { | ||
char *new_name = xmalloc(strlen(tip_name)+8); | ||
|
||
if (generation > 0) | ||
sprintf(new_name, "%s~%d^%d", tip_name, | ||
generation, parent_number); | ||
else | ||
sprintf(new_name, "%s^%d", tip_name, parent_number); | ||
|
||
name_rev(parents->item, new_name, | ||
merge_traversals + 1 , 0, 0); | ||
} else { | ||
name_rev(parents->item, tip_name, merge_traversals, | ||
generation + 1, 0); | ||
} | ||
} | ||
} | ||
|
||
static int tags_only = 0; | ||
|
||
static int name_ref(const char *path, const unsigned char *sha1) | ||
{ | ||
struct object *o = parse_object(sha1); | ||
int deref = 0; | ||
|
||
if (tags_only && strncmp(path, "refs/tags/", 10)) | ||
return 0; | ||
|
||
while (o && o->type == tag_type) { | ||
struct tag *t = (struct tag *) o; | ||
if (!t->tagged) | ||
break; /* broken repository */ | ||
o = parse_object(t->tagged->sha1); | ||
deref = 1; | ||
} | ||
if (o && o->type == commit_type) { | ||
struct commit *commit = (struct commit *)o; | ||
const char *p; | ||
|
||
while ((p = strchr(path, '/'))) | ||
path = p+1; | ||
|
||
name_rev(commit, strdup(path), 0, 0, deref); | ||
} | ||
return 0; | ||
} | ||
|
||
/* returns a static buffer */ | ||
static const char* get_rev_name(struct object *o) | ||
{ | ||
static char buffer[1024]; | ||
struct rev_name *n = (struct rev_name *)o->util; | ||
if (!n) | ||
return "undefined"; | ||
|
||
if (!n->generation) | ||
return n->tip_name; | ||
|
||
snprintf(buffer, sizeof(buffer), "%s~%d", n->tip_name, n->generation); | ||
|
||
return buffer; | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
struct object_list *revs = NULL; | ||
struct object_list **walker = &revs; | ||
int as_is = 0, all = 0, transform_stdin = 0; | ||
|
||
setup_git_directory(); | ||
|
||
if (argc < 2) | ||
usage(name_rev_usage); | ||
|
||
for (--argc, ++argv; argc; --argc, ++argv) { | ||
unsigned char sha1[20]; | ||
struct object *o; | ||
struct commit *commit; | ||
|
||
if (!as_is && (*argv)[0] == '-') { | ||
if (!strcmp(*argv, "--")) { | ||
as_is = 1; | ||
continue; | ||
} else if (!strcmp(*argv, "--tags")) { | ||
tags_only = 1; | ||
continue; | ||
} else if (!strcmp(*argv, "--all")) { | ||
if (argc > 1) | ||
die("Specify either a list, or --all, not both!"); | ||
all = 1; | ||
cutoff = 0; | ||
continue; | ||
} else if (!strcmp(*argv, "--stdin")) { | ||
if (argc > 1) | ||
die("Specify either a list, or --stdin, not both!"); | ||
transform_stdin = 1; | ||
cutoff = 0; | ||
continue; | ||
} | ||
usage(name_rev_usage); | ||
} | ||
|
||
if (get_sha1(*argv, sha1)) { | ||
fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", | ||
*argv); | ||
continue; | ||
} | ||
|
||
o = deref_tag(parse_object(sha1)); | ||
if (!o || o->type != commit_type) { | ||
fprintf(stderr, "Could not get commit for %s. Skipping.\n", | ||
*argv); | ||
continue; | ||
} | ||
|
||
commit = (struct commit *)o; | ||
|
||
if (cutoff > commit->date) | ||
cutoff = commit->date; | ||
|
||
object_list_append((struct object *)commit, walker); | ||
(*walker)->name = *argv; | ||
walker = &((*walker)->next); | ||
} | ||
|
||
for_each_ref(name_ref); | ||
|
||
if (transform_stdin) { | ||
char buffer[2048]; | ||
char *p, *p_start; | ||
|
||
while (!feof(stdin)) { | ||
int forty = 0; | ||
p = fgets(buffer, sizeof(buffer), stdin); | ||
if (!p) | ||
break; | ||
|
||
for (p_start = p; *p; p++) { | ||
#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) | ||
if (!ishex(*p)) | ||
forty = 0; | ||
else if (++forty == 40 && | ||
!ishex(*(p+1))) { | ||
unsigned char sha1[40]; | ||
const char *name = "undefined"; | ||
char c = *(p+1); | ||
|
||
forty = 0; | ||
|
||
*(p+1) = 0; | ||
if (!get_sha1(p - 39, sha1)) { | ||
struct object *o = | ||
lookup_object(sha1); | ||
if (o) | ||
name = get_rev_name(o); | ||
} | ||
*(p+1) = c; | ||
|
||
if (!strcmp(name, "undefined")) | ||
continue; | ||
|
||
fwrite(p_start, p - p_start, 1, stdout); | ||
fputc('(', stdout); | ||
fputs(name, stdout); | ||
fputc(')', stdout); | ||
p_start = p + 1; | ||
} | ||
} | ||
|
||
/* flush */ | ||
if (p_start != p) | ||
fwrite(p_start, p - p_start, 1, stdout); | ||
} | ||
} else if (all) { | ||
extern struct object **objs; | ||
extern int nr_objs; | ||
int i; | ||
|
||
for (i = 0; i < nr_objs; i++) | ||
printf("%s %s\n", sha1_to_hex(objs[i]->sha1), | ||
get_rev_name(objs[i])); | ||
} else | ||
for ( ; revs; revs = revs->next) | ||
printf("%s %s\n", revs->name, get_rev_name(revs->item)); | ||
|
||
return 0; | ||
} | ||
|