mirror of
https://github.com/git/git
synced 2024-08-24 18:26:02 +00:00
![Junio C Hamano](/assets/img/avatar_default.png)
The synopsys text and the usage string of subcommands that read list of things from the standard input are often shown like this: git gostak [--distim] < <list-of-doshes> This is problematic in a number of ways: * The way to use these commands is more often to feed them the output from another command, not feed them from a file. * Manual pages outside Git, commands that operate on the data read from the standard input, e.g "sort", "grep", "sed", etc., are not described with such a "< redirection-from-file" in their synopsys text. Our doing so introduces inconsistency. * We do not insist on where the output should go, by saying git gostak [--distim] < <list-of-doshes> > <output> * As it is our convention to enclose placeholders inside <braket>, the redirection operator followed by a placeholder filename becomes very hard to read, both in the documentation and in the help text. Let's clean them all up, after making sure that the documentation clearly describes the modes that take information from the standard input and what kind of things are expected on the input. [jc: stole example for fmt-merge-msg from Jonathan] Helped-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
202 lines
4 KiB
C
202 lines
4 KiB
C
#include "builtin.h"
|
|
|
|
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
|
|
{
|
|
char name[50];
|
|
|
|
if (!patchlen)
|
|
return;
|
|
|
|
memcpy(name, oid_to_hex(id), GIT_SHA1_HEXSZ + 1);
|
|
printf("%s %s\n", oid_to_hex(result), name);
|
|
}
|
|
|
|
static int remove_space(char *line)
|
|
{
|
|
char *src = line;
|
|
char *dst = line;
|
|
unsigned char c;
|
|
|
|
while ((c = *src++) != '\0') {
|
|
if (!isspace(c))
|
|
*dst++ = c;
|
|
}
|
|
return dst - line;
|
|
}
|
|
|
|
static int scan_hunk_header(const char *p, int *p_before, int *p_after)
|
|
{
|
|
static const char digits[] = "0123456789";
|
|
const char *q, *r;
|
|
int n;
|
|
|
|
q = p + 4;
|
|
n = strspn(q, digits);
|
|
if (q[n] == ',') {
|
|
q += n + 1;
|
|
n = strspn(q, digits);
|
|
}
|
|
if (n == 0 || q[n] != ' ' || q[n+1] != '+')
|
|
return 0;
|
|
|
|
r = q + n + 2;
|
|
n = strspn(r, digits);
|
|
if (r[n] == ',') {
|
|
r += n + 1;
|
|
n = strspn(r, digits);
|
|
}
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
*p_before = atoi(q);
|
|
*p_after = atoi(r);
|
|
return 1;
|
|
}
|
|
|
|
static void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
|
|
{
|
|
unsigned char hash[GIT_SHA1_RAWSZ];
|
|
unsigned short carry = 0;
|
|
int i;
|
|
|
|
git_SHA1_Final(hash, ctx);
|
|
git_SHA1_Init(ctx);
|
|
/* 20-byte sum, with carry */
|
|
for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
|
|
carry += result->hash[i] + hash[i];
|
|
result->hash[i] = carry;
|
|
carry >>= 8;
|
|
}
|
|
}
|
|
|
|
static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
|
|
struct strbuf *line_buf, int stable)
|
|
{
|
|
int patchlen = 0, found_next = 0;
|
|
int before = -1, after = -1;
|
|
git_SHA_CTX ctx;
|
|
|
|
git_SHA1_Init(&ctx);
|
|
oidclr(result);
|
|
|
|
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
|
|
char *line = line_buf->buf;
|
|
char *p = line;
|
|
int len;
|
|
|
|
if (!memcmp(line, "diff-tree ", 10))
|
|
p += 10;
|
|
else if (!memcmp(line, "commit ", 7))
|
|
p += 7;
|
|
else if (!memcmp(line, "From ", 5))
|
|
p += 5;
|
|
else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
|
|
continue;
|
|
|
|
if (!get_oid_hex(p, next_oid)) {
|
|
found_next = 1;
|
|
break;
|
|
}
|
|
|
|
/* Ignore commit comments */
|
|
if (!patchlen && memcmp(line, "diff ", 5))
|
|
continue;
|
|
|
|
/* Parsing diff header? */
|
|
if (before == -1) {
|
|
if (!memcmp(line, "index ", 6))
|
|
continue;
|
|
else if (!memcmp(line, "--- ", 4))
|
|
before = after = 1;
|
|
else if (!isalpha(line[0]))
|
|
break;
|
|
}
|
|
|
|
/* Looking for a valid hunk header? */
|
|
if (before == 0 && after == 0) {
|
|
if (!memcmp(line, "@@ -", 4)) {
|
|
/* Parse next hunk, but ignore line numbers. */
|
|
scan_hunk_header(line, &before, &after);
|
|
continue;
|
|
}
|
|
|
|
/* Split at the end of the patch. */
|
|
if (memcmp(line, "diff ", 5))
|
|
break;
|
|
|
|
/* Else we're parsing another header. */
|
|
if (stable)
|
|
flush_one_hunk(result, &ctx);
|
|
before = after = -1;
|
|
}
|
|
|
|
/* If we get here, we're inside a hunk. */
|
|
if (line[0] == '-' || line[0] == ' ')
|
|
before--;
|
|
if (line[0] == '+' || line[0] == ' ')
|
|
after--;
|
|
|
|
/* Compute the sha without whitespace */
|
|
len = remove_space(line);
|
|
patchlen += len;
|
|
git_SHA1_Update(&ctx, line, len);
|
|
}
|
|
|
|
if (!found_next)
|
|
oidclr(next_oid);
|
|
|
|
flush_one_hunk(result, &ctx);
|
|
|
|
return patchlen;
|
|
}
|
|
|
|
static void generate_id_list(int stable)
|
|
{
|
|
struct object_id oid, n, result;
|
|
int patchlen;
|
|
struct strbuf line_buf = STRBUF_INIT;
|
|
|
|
oidclr(&oid);
|
|
while (!feof(stdin)) {
|
|
patchlen = get_one_patchid(&n, &result, &line_buf, stable);
|
|
flush_current_id(patchlen, &oid, &result);
|
|
oidcpy(&oid, &n);
|
|
}
|
|
strbuf_release(&line_buf);
|
|
}
|
|
|
|
static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
|
|
|
|
static int git_patch_id_config(const char *var, const char *value, void *cb)
|
|
{
|
|
int *stable = cb;
|
|
|
|
if (!strcmp(var, "patchid.stable")) {
|
|
*stable = git_config_bool(var, value);
|
|
return 0;
|
|
}
|
|
|
|
return git_default_config(var, value, cb);
|
|
}
|
|
|
|
int cmd_patch_id(int argc, const char **argv, const char *prefix)
|
|
{
|
|
int stable = -1;
|
|
|
|
git_config(git_patch_id_config, &stable);
|
|
|
|
/* If nothing is set, default to unstable. */
|
|
if (stable < 0)
|
|
stable = 0;
|
|
|
|
if (argc == 2 && !strcmp(argv[1], "--stable"))
|
|
stable = 1;
|
|
else if (argc == 2 && !strcmp(argv[1], "--unstable"))
|
|
stable = 0;
|
|
else if (argc != 1)
|
|
usage(patch_id_usage);
|
|
|
|
generate_id_list(stable);
|
|
return 0;
|
|
}
|