|
|
|
/*
|
|
|
|
* rev-parse.c
|
|
|
|
*
|
|
|
|
* Copyright (C) Linus Torvalds, 2005
|
|
|
|
*/
|
|
|
|
#include "cache.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "quote.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
|
|
|
|
#define DO_REVS 1
|
|
|
|
#define DO_NOREV 2
|
|
|
|
#define DO_FLAGS 4
|
|
|
|
#define DO_NONFLAGS 8
|
|
|
|
static int filter = ~0;
|
|
|
|
|
|
|
|
static const char *def;
|
|
|
|
|
|
|
|
#define NORMAL 0
|
|
|
|
#define REVERSED 1
|
|
|
|
static int show_type = NORMAL;
|
|
|
|
static int symbolic;
|
|
|
|
static int abbrev;
|
|
|
|
static int output_sq;
|
|
|
|
|
|
|
|
static int revs_count;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some arguments are relevant "revision" arguments,
|
|
|
|
* others are about output format or other details.
|
|
|
|
* This sorts it all out.
|
|
|
|
*/
|
|
|
|
static int is_rev_argument(const char *arg)
|
|
|
|
{
|
|
|
|
static const char *rev_args[] = {
|
|
|
|
"--all",
|
|
|
|
"--bisect",
|
|
|
|
"--dense",
|
|
|
|
"--branches",
|
|
|
|
"--header",
|
|
|
|
"--max-age=",
|
|
|
|
"--max-count=",
|
|
|
|
"--min-age=",
|
|
|
|
"--no-merges",
|
|
|
|
"--objects",
|
|
|
|
"--objects-edge",
|
|
|
|
"--parents",
|
|
|
|
"--pretty",
|
|
|
|
"--remotes",
|
|
|
|
"--sparse",
|
|
|
|
"--tags",
|
|
|
|
"--topo-order",
|
|
|
|
"--date-order",
|
|
|
|
"--unpacked",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
const char **p = rev_args;
|
|
|
|
|
|
|
|
/* accept -<digit>, like traditional "head" */
|
|
|
|
if ((*arg == '-') && isdigit(arg[1]))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
const char *str = *p++;
|
|
|
|
int len;
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
len = strlen(str);
|
|
|
|
if (!strcmp(arg, str) ||
|
|
|
|
(str[len-1] == '=' && !strncmp(arg, str, len)))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output argument as a string, either SQ or normal */
|
[PATCH] Help scripts that use git-rev-parse to grok args with SP/TAB/LF
The git-rev-parse command uses LF to separate each argument it
parses, so its users at least need to set IFS to LF to be able
to handle filenames with embedded SPs and TABs. Some commands,
however, can take and do expect arguments with embedded LF,
notably, "-S" (pickaxe) of diff family, so even this workaround
does not work for them.
When --sq flag to git-rev-parse is given, instead of showing one
argument per line, it outputs arguments quoted for consumption
with "eval" by the caller, to remedy this situation.
As an example, this patch converts git-whatchanged to use this
new feature.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
20 years ago
|
|
|
static void show(const char *arg)
|
|
|
|
{
|
|
|
|
if (output_sq) {
|
|
|
|
int sq = '\'', ch;
|
|
|
|
|
|
|
|
putchar(sq);
|
|
|
|
while ((ch = *arg++)) {
|
|
|
|
if (ch == sq)
|
|
|
|
fputs("'\\'", stdout);
|
|
|
|
putchar(ch);
|
|
|
|
}
|
|
|
|
putchar(sq);
|
|
|
|
putchar(' ');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
puts(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output a revision, only if filter allows it */
|
|
|
|
static void show_rev(int type, const unsigned char *sha1, const char *name)
|
|
|
|
{
|
|
|
|
if (!(filter & DO_REVS))
|
|
|
|
return;
|
|
|
|
def = NULL;
|
|
|
|
revs_count++;
|
[PATCH] Help scripts that use git-rev-parse to grok args with SP/TAB/LF
The git-rev-parse command uses LF to separate each argument it
parses, so its users at least need to set IFS to LF to be able
to handle filenames with embedded SPs and TABs. Some commands,
however, can take and do expect arguments with embedded LF,
notably, "-S" (pickaxe) of diff family, so even this workaround
does not work for them.
When --sq flag to git-rev-parse is given, instead of showing one
argument per line, it outputs arguments quoted for consumption
with "eval" by the caller, to remedy this situation.
As an example, this patch converts git-whatchanged to use this
new feature.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
20 years ago
|
|
|
|
|
|
|
if (type != show_type)
|
|
|
|
putchar('^');
|
|
|
|
if (symbolic && name)
|
|
|
|
show(name);
|
|
|
|
else if (abbrev)
|
|
|
|
show(find_unique_abbrev(sha1, abbrev));
|
|
|
|
else
|
|
|
|
show(sha1_to_hex(sha1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output a flag, only if filter allows it. */
|
|
|
|
static int show_flag(const char *arg)
|
|
|
|
{
|
|
|
|
if (!(filter & DO_FLAGS))
|
|
|
|
return 0;
|
|
|
|
if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
|
|
|
|
show(arg);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_default(void)
|
|
|
|
{
|
|
|
|
const char *s = def;
|
|
|
|
|
|
|
|
if (s) {
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
|
|
|
def = NULL;
|
|
|
|
if (!get_sha1(s, sha1)) {
|
|
|
|
show_rev(NORMAL, sha1, s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
|
|
|
{
|
|
|
|
show_rev(NORMAL, sha1, refname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_datestring(const char *flag, const char *datestr)
|
|
|
|
{
|
|
|
|
static char buffer[100];
|
|
|
|
|
|
|
|
/* date handling requires both flags and revs */
|
|
|
|
if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
|
|
|
|
return;
|
git's rev-parse.c function show_datestring presumes gnu date
Ok. This is the insane patch to do this.
It really isn't very careful, and the reason I call it "approxidate()"
will become obvious when you look at the code. It is very liberal in what
it accepts, to the point where sometimes the results may not make a whole
lot of sense.
It accepts "last week" as a date string, by virtue of "last" parsing as
the number 1, and it totally ignoring superfluous fluff like "ago", so
"last week" ends up being exactly the same thing as "1 week ago". Fine so
far.
It has strange side effects: "last december" will actually parse as "Dec
1", which actually _does_ turn out right, because it will then notice that
it's not December yet, so it will decide that you must be talking about a
date last year. So it actually gets it right, but it's kind of for the
"wrong" reasons.
It also accepts the numbers 1..10 in string format ("one" .. "ten"), so
you can do "ten weeks ago" or "ten hours ago" and it will do the right
thing.
But it will do some really strange thigns too: the string "this will last
forever", will not recognize anyting but "last", which is recognized as
"1", which since it doesn't understand anything else it will think is the
day of the month. So if you do
gitk --since="this will last forever"
the date will actually parse as the first day of the current month.
And it will parse the string "now" as "now", but only because it doesn't
understand it at all, and it makes everything relative to "now".
Similarly, it doesn't actually parse the "ago" or "from now", so "2 weeks
ago" is exactly the same as "2 weeks from now". It's the current date
minus 14 days.
But hey, it's probably better (and certainly faster) than depending on GNU
date. So now you can portably do things like
gitk --since="two weeks and three days ago"
git log --since="July 5"
git-whatchanged --since="10 hours ago"
git log --since="last october"
and it will actually do exactly what you thought it would do (I think). It
will count 17 days backwards, and it will do so even if you don't have GNU
date installed.
(I don't do "last monday" or similar yet, but I can extend it to that too
if people want).
It was kind of fun trying to write code that uses such totally relaxed
"understanding" of dates yet tries to get it right for the trivial cases.
The result should be mixed with a few strange preprocessor tricks, and be
submitted for the IOCCC ;)
Feel free to try it out, and see how many strange dates it gets right. Or
wrong.
And if you find some interesting (and valid - not "interesting" as in
"strange", but "interesting" as in "I'd be interested in actually doing
this) thing it gets wrong - usually by not understanding it and silently
just doing some strange things - please holler.
Now, as usual this certainly hasn't been getting a lot of testing. But my
code always works, no?
Linus
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
|
|
|
|
show(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_file(const char *arg)
|
|
|
|
{
|
|
|
|
show_default();
|
|
|
|
if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
|
|
|
|
show(arg);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int try_difference(const char *arg)
|
|
|
|
{
|
|
|
|
char *dotdot;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
unsigned char end[20];
|
|
|
|
const char *next;
|
|
|
|
const char *this;
|
|
|
|
int symmetric;
|
|
|
|
|
|
|
|
if (!(dotdot = strstr(arg, "..")))
|
|
|
|
return 0;
|
|
|
|
next = dotdot + 2;
|
|
|
|
this = arg;
|
|
|
|
symmetric = (*next == '.');
|
|
|
|
|
|
|
|
*dotdot = 0;
|
|
|
|
next += symmetric;
|
|
|
|
|
|
|
|
if (!*next)
|
|
|
|
next = "HEAD";
|
|
|
|
if (dotdot == arg)
|
|
|
|
this = "HEAD";
|
|
|
|
if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
|
|
|
|
show_rev(NORMAL, end, next);
|
|
|
|
show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
|
|
|
|
if (symmetric) {
|
|
|
|
struct commit_list *exclude;
|
|
|
|
struct commit *a, *b;
|
|
|
|
a = lookup_commit_reference(sha1);
|
|
|
|
b = lookup_commit_reference(end);
|
|
|
|
exclude = get_merge_bases(a, b, 1);
|
|
|
|
while (exclude) {
|
|
|
|
struct commit_list *n = exclude->next;
|
|
|
|
show_rev(REVERSED,
|
|
|
|
exclude->item->object.sha1,NULL);
|
|
|
|
free(exclude);
|
|
|
|
exclude = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*dotdot = '.';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int i, as_is = 0, verify = 0;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
|
|
|
git_config(git_default_config);
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
const char *arg = argv[i];
|
|
|
|
|
|
|
|
if (as_is) {
|
|
|
|
if (show_file(arg) && as_is < 2)
|
|
|
|
verify_filename(prefix, arg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg,"-n")) {
|
|
|
|
if (++i >= argc)
|
|
|
|
die("-n requires an argument");
|
|
|
|
if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
|
|
|
|
show(arg);
|
|
|
|
show(argv[i]);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg,"-n",2)) {
|
|
|
|
if ((filter & DO_FLAGS) && (filter & DO_REVS))
|
|
|
|
show(arg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*arg == '-') {
|
|
|
|
if (!strcmp(arg, "--")) {
|
|
|
|
as_is = 2;
|
|
|
|
/* Pass on the "--" if we show anything but files.. */
|
|
|
|
if (filter & (DO_FLAGS | DO_REVS))
|
|
|
|
show_file(arg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--default")) {
|
|
|
|
def = argv[i+1];
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--revs-only")) {
|
|
|
|
filter &= ~DO_NOREV;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--no-revs")) {
|
|
|
|
filter &= ~DO_REVS;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--flags")) {
|
|
|
|
filter &= ~DO_NONFLAGS;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--no-flags")) {
|
|
|
|
filter &= ~DO_FLAGS;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--verify")) {
|
|
|
|
filter &= ~(DO_FLAGS|DO_NOREV);
|
|
|
|
verify = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--short") ||
|
|
|
|
!strncmp(arg, "--short=", 8)) {
|
|
|
|
filter &= ~(DO_FLAGS|DO_NOREV);
|
|
|
|
verify = 1;
|
|
|
|
abbrev = DEFAULT_ABBREV;
|
|
|
|
if (arg[7] == '=')
|
|
|
|
abbrev = strtoul(arg + 8, NULL, 10);
|
|
|
|
if (abbrev < MINIMUM_ABBREV)
|
|
|
|
abbrev = MINIMUM_ABBREV;
|
|
|
|
else if (40 <= abbrev)
|
|
|
|
abbrev = 40;
|
|
|
|
continue;
|
|
|
|
}
|
[PATCH] Help scripts that use git-rev-parse to grok args with SP/TAB/LF
The git-rev-parse command uses LF to separate each argument it
parses, so its users at least need to set IFS to LF to be able
to handle filenames with embedded SPs and TABs. Some commands,
however, can take and do expect arguments with embedded LF,
notably, "-S" (pickaxe) of diff family, so even this workaround
does not work for them.
When --sq flag to git-rev-parse is given, instead of showing one
argument per line, it outputs arguments quoted for consumption
with "eval" by the caller, to remedy this situation.
As an example, this patch converts git-whatchanged to use this
new feature.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
20 years ago
|
|
|
if (!strcmp(arg, "--sq")) {
|
|
|
|
output_sq = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--not")) {
|
|
|
|
show_type ^= REVERSED;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--symbolic")) {
|
|
|
|
symbolic = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--all")) {
|
|
|
|
for_each_ref(show_reference, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--branches")) {
|
|
|
|
for_each_branch_ref(show_reference, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--tags")) {
|
|
|
|
for_each_tag_ref(show_reference, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--remotes")) {
|
|
|
|
for_each_remote_ref(show_reference, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--show-prefix")) {
|
|
|
|
if (prefix)
|
|
|
|
puts(prefix);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--show-cdup")) {
|
|
|
|
const char *pfx = prefix;
|
|
|
|
while (pfx) {
|
|
|
|
pfx = strchr(pfx, '/');
|
|
|
|
if (pfx) {
|
|
|
|
pfx++;
|
|
|
|
printf("../");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--git-dir")) {
|
|
|
|
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
|
|
|
|
static char cwd[PATH_MAX];
|
|
|
|
if (gitdir) {
|
|
|
|
puts(gitdir);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!prefix) {
|
|
|
|
puts(".git");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!getcwd(cwd, PATH_MAX))
|
|
|
|
die("unable to get current working directory");
|
|
|
|
printf("%s/.git\n", cwd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--is-inside-git-dir")) {
|
|
|
|
printf("%s\n", is_inside_git_dir() ? "true"
|
|
|
|
: "false");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--since=", 8)) {
|
|
|
|
show_datestring("--max-age=", arg+8);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--after=", 8)) {
|
|
|
|
show_datestring("--max-age=", arg+8);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--before=", 9)) {
|
|
|
|
show_datestring("--min-age=", arg+9);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--until=", 8)) {
|
|
|
|
show_datestring("--min-age=", arg+8);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (show_flag(arg) && verify)
|
|
|
|
die("Needed a single revision");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not a flag argument */
|
|
|
|
if (try_difference(arg))
|
|
|
|
continue;
|
|
|
|
if (!get_sha1(arg, sha1)) {
|
|
|
|
show_rev(NORMAL, sha1, arg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*arg == '^' && !get_sha1(arg+1, sha1)) {
|
|
|
|
show_rev(REVERSED, sha1, arg+1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
as_is = 1;
|
|
|
|
if (!show_file(arg))
|
|
|
|
continue;
|
|
|
|
if (verify)
|
|
|
|
die("Needed a single revision");
|
|
|
|
verify_filename(prefix, arg);
|
|
|
|
}
|
|
|
|
show_default();
|
|
|
|
if (verify && revs_count != 1)
|
|
|
|
die("Needed a single revision");
|
|
|
|
return 0;
|
|
|
|
}
|