#include "cache.h"
#include "tag.h"
#include "commit.h"
+#include "pkt-line.h"
+#include "utf8.h"
int save_commit_buffer = 1;
struct sort_node
{
/*
- * the number of children of the associated commit
- * that also occur in the list being sorted.
- */
+ * the number of children of the associated commit
+ * that also occur in the list being sorted.
+ */
unsigned int indegree;
/*
- * reference to original list item that we will re-use
- * on output.
- */
+ * reference to original list item that we will re-use
+ * on output.
+ */
struct commit_list * list_item;
};
return;
graft_file = get_graft_file();
read_graft_file(graft_file);
+ /* make sure shallows are read */
+ is_repository_shallow();
commit_graft_prepared = 1;
}
return commit_graft[pos];
}
+int write_shallow_commits(int fd, int use_pack_protocol)
+{
+ int i, count = 0;
+ for (i = 0; i < commit_graft_nr; i++)
+ if (commit_graft[i]->nr_parent < 0) {
+ const char *hex =
+ sha1_to_hex(commit_graft[i]->sha1);
+ count++;
+ if (use_pack_protocol)
+ packet_write(fd, "shallow %s", hex);
+ else {
+ if (write_in_full(fd, hex, 40) != 40)
+ break;
+ if (write_in_full(fd, "\n", 1) != 1)
+ break;
+ }
+ }
+ return count;
+}
+
+int unregister_shallow(const unsigned char *sha1)
+{
+ int pos = commit_graft_pos(sha1);
+ if (pos < 0)
+ return -1;
+ if (pos + 1 < commit_graft_nr)
+ memcpy(commit_graft + pos, commit_graft + pos + 1,
+ sizeof(struct commit_graft *)
+ * (commit_graft_nr - pos - 1));
+ commit_graft_nr--;
+ return 0;
+}
+
int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
{
char *tail = buffer;
return ret;
}
+/* High bit set, or ISO-2022-INT */
+static int non_ascii(int ch)
+{
+ ch = (ch & 0xff);
+ return ((ch & 0x80) || (ch == 0x1b));
+}
+
static int is_rfc2047_special(char ch)
{
- return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_'));
+ return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
}
-static int add_rfc2047(char *buf, const char *line, int len)
+static int add_rfc2047(char *buf, const char *line, int len,
+ const char *encoding)
{
char *bp = buf;
int i, needquote;
- static const char q_utf8[] = "=?utf-8?q?";
+ char q_encoding[128];
+ const char *q_encoding_fmt = "=?%s?q?";
for (i = needquote = 0; !needquote && i < len; i++) {
- unsigned ch = line[i];
- if (ch & 0x80)
+ int ch = line[i];
+ if (non_ascii(ch))
needquote++;
if ((i + 1 < len) &&
(ch == '=' && line[i+1] == '?'))
if (!needquote)
return sprintf(buf, "%.*s", len, line);
- memcpy(bp, q_utf8, sizeof(q_utf8)-1);
- bp += sizeof(q_utf8)-1;
+ i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
+ if (sizeof(q_encoding) < i)
+ die("Insanely long encoding name %s", encoding);
+ memcpy(bp, q_encoding, i);
+ bp += i;
for (i = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF;
if (is_rfc2047_special(ch)) {
return bp - buf;
}
-static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
+static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
+ const char *line, int relative_date,
+ const char *encoding)
{
char *date;
int namelen;
filler = "";
strcpy(buf, "From: ");
ret = strlen(buf);
- ret += add_rfc2047(buf + ret, line, display_name_length);
+ ret += add_rfc2047(buf + ret, line, display_name_length,
+ encoding);
memcpy(buf + ret, name_tail, namelen - display_name_length);
ret += namelen - display_name_length;
buf[ret++] = '\n';
}
switch (fmt) {
case CMIT_FMT_MEDIUM:
- ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
+ ret += sprintf(buf + ret, "Date: %s\n",
+ show_date(time, tz, relative_date));
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
show_rfc2822_date(time, tz));
break;
case CMIT_FMT_FULLER:
- ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
+ ret += sprintf(buf + ret, "%sDate: %s\n", what,
+ show_date(time, tz, relative_date));
break;
default:
/* notin' */
while (parent) {
struct commit *p = parent->item;
- const char *hex = abbrev
- ? find_unique_abbrev(p->object.sha1, abbrev)
- : sha1_to_hex(p->object.sha1);
- const char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
+ const char *hex = NULL;
+ const char *dots;
+ if (abbrev)
+ hex = find_unique_abbrev(p->object.sha1, abbrev);
+ if (!hex)
+ hex = sha1_to_hex(p->object.sha1);
+ dots = (abbrev && strlen(hex) != 40) ? "..." : "";
parent = parent->next;
offset += sprintf(buf + offset, " %s%s", hex, dots);
return offset;
}
-unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject)
+static char *get_header(const struct commit *commit, const char *key)
+{
+ int key_len = strlen(key);
+ const char *line = commit->buffer;
+
+ for (;;) {
+ const char *eol = strchr(line, '\n'), *next;
+
+ if (line == eol)
+ return NULL;
+ if (!eol) {
+ eol = line + strlen(line);
+ next = NULL;
+ } else
+ next = eol + 1;
+ if (!strncmp(line, key, key_len) && line[key_len] == ' ') {
+ int len = eol - line - key_len;
+ char *ret = xmalloc(len);
+ memcpy(ret, line + key_len + 1, len - 1);
+ ret[len - 1] = '\0';
+ return ret;
+ }
+ line = next;
+ }
+}
+
+static char *replace_encoding_header(char *buf, char *encoding)
{
- int hdr = 1, body = 0;
+ char *encoding_header = strstr(buf, "\nencoding ");
+ char *end_of_encoding_header;
+ int encoding_header_pos;
+ int encoding_header_len;
+ int new_len;
+ int need_len;
+ int buflen = strlen(buf) + 1;
+
+ if (!encoding_header)
+ return buf; /* should not happen but be defensive */
+ encoding_header++;
+ end_of_encoding_header = strchr(encoding_header, '\n');
+ if (!end_of_encoding_header)
+ return buf; /* should not happen but be defensive */
+ end_of_encoding_header++;
+
+ encoding_header_len = end_of_encoding_header - encoding_header;
+ encoding_header_pos = encoding_header - buf;
+
+ if (is_encoding_utf8(encoding)) {
+ /* we have re-coded to UTF-8; drop the header */
+ memmove(encoding_header, end_of_encoding_header,
+ buflen - (encoding_header_pos + encoding_header_len));
+ return buf;
+ }
+ new_len = strlen(encoding);
+ need_len = new_len + strlen("encoding \n");
+ if (encoding_header_len < need_len) {
+ buf = xrealloc(buf, buflen + (need_len - encoding_header_len));
+ encoding_header = buf + encoding_header_pos;
+ end_of_encoding_header = encoding_header + encoding_header_len;
+ }
+ memmove(end_of_encoding_header + (need_len - encoding_header_len),
+ end_of_encoding_header,
+ buflen - (encoding_header_pos + encoding_header_len));
+ memcpy(encoding_header + 9, encoding, strlen(encoding));
+ encoding_header[9 + new_len] = '\n';
+ return buf;
+}
+
+static char *logmsg_reencode(const struct commit *commit,
+ char *output_encoding)
+{
+ char *encoding;
+ char *out;
+ char *utf8 = "utf-8";
+
+ if (!*output_encoding)
+ return NULL;
+ encoding = get_header(commit, "encoding");
+ if (!encoding)
+ encoding = utf8;
+ if (!strcmp(encoding, output_encoding))
+ out = strdup(commit->buffer);
+ else
+ out = reencode_string(commit->buffer,
+ output_encoding, encoding);
+ if (out)
+ out = replace_encoding_header(out, output_encoding);
+
+ if (encoding != utf8)
+ free(encoding);
+ if (!out)
+ return NULL;
+ return out;
+}
+
+unsigned long pretty_print_commit(enum cmit_fmt fmt,
+ const struct commit *commit,
+ unsigned long len,
+ char *buf, unsigned long space,
+ int abbrev, const char *subject,
+ const char *after_subject,
+ int relative_date)
+{
+ int hdr = 1, body = 0, seen_title = 0;
unsigned long offset = 0;
int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
int plain_non_ascii = 0;
+ char *reencoded;
+ char *encoding;
+
+ encoding = (git_log_output_encoding
+ ? git_log_output_encoding
+ : git_commit_encoding);
+ if (!encoding)
+ encoding = "utf-8";
+ reencoded = logmsg_reencode(commit, encoding);
+ if (reencoded)
+ msg = reencoded;
if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
indent = 0;
for (in_body = i = 0; (ch = msg[i]) && i < len; i++) {
if (!in_body) {
/* author could be non 7-bit ASCII but
- * the log may so; skip over the
+ * the log may be so; skip over the
* header part first.
*/
if (ch == '\n' &&
i + 1 < len && msg[i+1] == '\n')
in_body = 1;
}
- else if (ch & 0x80) {
+ else if (non_ascii(ch)) {
plain_non_ascii = 1;
break;
}
if (!memcmp(line, "author ", 7))
offset += add_user_info("Author", fmt,
buf + offset,
- line + 7);
+ line + 7,
+ relative_date,
+ encoding);
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
offset += add_user_info("Commit", fmt,
buf + offset,
- line + 10);
+ line + 10,
+ relative_date,
+ encoding);
continue;
}
body = 1;
if (is_empty_line(line, &linelen)) {
+ if (!seen_title)
+ continue;
if (!body)
continue;
if (subject)
break;
}
+ seen_title = 1;
if (subject) {
int slen = strlen(subject);
memcpy(buf + offset, subject, slen);
offset += slen;
- offset += add_rfc2047(buf + offset, line, linelen);
+ offset += add_rfc2047(buf + offset, line, linelen,
+ encoding);
}
else {
memset(buf + offset, ' ', indent);
if (fmt == CMIT_FMT_ONELINE)
break;
if (subject && plain_non_ascii) {
- static const char header[] =
- "Content-Type: text/plain; charset=UTF-8\n"
+ int sz;
+ char header[512];
+ const char *header_fmt =
+ "Content-Type: text/plain; charset=%s\n"
"Content-Transfer-Encoding: 8bit\n";
- memcpy(buf + offset, header, sizeof(header)-1);
- offset += sizeof(header)-1;
+ sz = snprintf(header, sizeof(header), header_fmt,
+ encoding);
+ if (sizeof(header) < sz)
+ die("Encoding name %s too long", encoding);
+ memcpy(buf + offset, header, sz);
+ offset += sz;
}
if (after_subject) {
int slen = strlen(after_subject);
if (fmt == CMIT_FMT_EMAIL && !body)
buf[offset++] = '\n';
buf[offset] = '\0';
+
+ free(reencoded);
return offset;
}
free(nodes);
}
-/* merge-rebase stuff */
+/* merge-base stuff */
+
+/* bits #0..15 in revision.h */
+#define PARENT1 (1u<<16)
+#define PARENT2 (1u<<17)
+#define STALE (1u<<18)
+#define RESULT (1u<<19)
-/* bits #0..7 in revision.h */
-#define PARENT1 (1u<< 8)
-#define PARENT2 (1u<< 9)
-#define STALE (1u<<10)
-#define RESULT (1u<<11)
+static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
static struct commit *interesting(struct commit_list *list)
{
}
/* Clean up the result to remove stale ones */
+ free_commit_list(list);
list = result; result = NULL;
while (list) {
struct commit_list *n = list->next;
struct commit *two,
int cleanup)
{
- const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
struct commit_list *list;
struct commit **rslt;
struct commit_list *result;
free(rslt);
return result;
}
+
+int in_merge_bases(struct commit *rev1, struct commit *rev2)
+{
+ struct commit_list *bases, *b;
+ int ret = 0;
+
+ bases = get_merge_bases(rev1, rev2, 1);
+ for (b = bases; b; b = b->next) {
+ if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ free_commit_list(bases);
+ return ret;
+}