int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data) { const char *logfile; FILE *logfp; struct strbuf sb = STRBUF_INIT; int ret = 0; logfile = git_path("logs/%s", ref); logfp = fopen(logfile, "r"); if (!logfp) return -1; if (ofs) { struct stat statbuf; if (fstat(fileno(logfp), &statbuf) || statbuf.st_size < ofs || fseek(logfp, -ofs, SEEK_END) || strbuf_getwholeline(&sb, logfp, '\n')) { fclose(logfp); strbuf_release(&sb); return -1; } } while (!strbuf_getwholeline(&sb, logfp, '\n')) { unsigned char osha1[20], nsha1[20]; char *email_end, *message; unsigned long timestamp; int tz; /* old SP new SP name <email> SP time TAB msg LF */ if (sb.len < 83 || sb.buf[sb.len - 1] != '\n' || get_sha1_hex(sb.buf, osha1) || sb.buf[40] != ' ' || get_sha1_hex(sb.buf + 41, nsha1) || sb.buf[81] != ' ' || !(email_end = strchr(sb.buf + 82, '>')) || email_end[1] != ' ' || !(timestamp = strtoul(email_end + 2, &message, 10)) || !message || message[0] != ' ' || (message[1] != '+' && message[1] != '-') || !isdigit(message[2]) || !isdigit(message[3]) || !isdigit(message[4]) || !isdigit(message[5])) continue; /* corrupt? */ email_end[1] = '\0'; tz = strtol(message + 1, NULL, 10); if (message[6] != '\t') message += 6; else message += 7; ret = fn(osha1, nsha1, sb.buf + 82, timestamp, tz, message, cb_data); if (ret) break; } fclose(logfp); strbuf_release(&sb); return ret; }
/* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * work on (i.e. what is left by the previous invocation of "git * rerere" during the current conflict resolution session). */ static void read_rr(struct string_list *rr) { struct strbuf buf = STRBUF_INIT; FILE *in = fopen(git_path_merge_rr(), "r"); if (!in) return; while (!strbuf_getwholeline(&buf, in, '\0')) { char *path; unsigned char sha1[20]; struct rerere_id *id; /* There has to be the hash, tab, path and then NUL */ if (buf.len < 42 || get_sha1_hex(buf.buf, sha1)) die("corrupt MERGE_RR"); if (buf.buf[40] != '\t') die("corrupt MERGE_RR"); buf.buf[40] = '\0'; path = buf.buf + 41; id = new_rerere_id_hex(buf.buf); string_list_insert(rr, path)->util = id; } strbuf_release(&buf); fclose(in); }
int strbuf_getline(struct strbuf *sb, FILE *fp, int term) { if (strbuf_getwholeline(sb, fp, term)) return EOF; if (sb->buf[sb->len-1] == term) strbuf_setlen(sb, sb->len-1); return 0; }
int strbuf_getline(struct strbuf *sb, FILE *fp) { if (strbuf_getwholeline(sb, fp, '\n')) return EOF; if (sb->buf[sb->len - 1] == '\n') { strbuf_setlen(sb, sb->len - 1); if (sb->len && sb->buf[sb->len - 1] == '\r') strbuf_setlen(sb, sb->len - 1); } return 0; }
/* Called with the first line (potentially partial) * already in buf[] -- normally that should begin with * the Unix "From " line. Write it into the specified * file. */ static int split_one(FILE *mbox, const char *name, int allow_bare) { FILE *output = NULL; int fd; int status = 0; int is_bare = !is_from_line(buf.buf, buf.len); if (is_bare && !allow_bare) goto corrupt; fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666); if (fd < 0) die_errno("cannot open output file '%s'", name); output = xfdopen(fd, "w"); /* Copy it out, while searching for a line that begins with * "From " and having something that looks like a date format. */ for (;;) { if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' && buf.buf[buf.len-2] == '\r') { strbuf_setlen(&buf, buf.len-2); strbuf_addch(&buf, '\n'); } if (fwrite(buf.buf, 1, buf.len, output) != buf.len) die_errno("cannot write output"); if (strbuf_getwholeline(&buf, mbox, '\n')) { if (feof(mbox)) { status = 1; break; } die_errno("cannot read mbox"); } if (!is_bare && is_from_line(buf.buf, buf.len)) break; /* done with one message */ } fclose(output); return status; corrupt: if (output) fclose(output); unlink(name); fprintf(stderr, "corrupt mailbox\n"); exit(1); }
/* * Add phony grafts for use with -S; this is primarily to * support git's cvsserver that wants to give a linear history * to its clients. */ static int read_ancestry(const char *graft_file) { FILE *fp = fopen_or_warn(graft_file, "r"); struct strbuf buf = STRBUF_INIT; if (!fp) return -1; while (!strbuf_getwholeline(&buf, fp, '\n')) { /* The format is just "Commit Parent1 Parent2 ...\n" */ struct commit_graft *graft = read_graft_line(&buf); if (graft) register_commit_graft(graft, 0); } fclose(fp); strbuf_release(&buf); return 0; }
static int remove_cmd(int argc, const char **argv, const char *prefix) { unsigned flag = 0; int from_stdin = 0; struct option options[] = { OPT_BIT(0, "ignore-missing", &flag, N_("attempt to remove non-existent note is not an error"), IGNORE_MISSING), OPT_BOOL(0, "stdin", &from_stdin, N_("read object names from the standard input")), OPT_END() }; struct notes_tree *t; int retval = 0; argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); t = init_notes_check("remove"); if (!argc && !from_stdin) { retval = remove_one_note(t, "HEAD", flag); } else { while (*argv) { retval |= remove_one_note(t, *argv, flag); argv++; } } if (from_stdin) { struct strbuf sb = STRBUF_INIT; while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { strbuf_rtrim(&sb); retval |= remove_one_note(t, sb.buf, flag); } strbuf_release(&sb); } if (!retval) commit_notes(t, "Notes removed by 'git notes remove'"); free_notes(t); return retval; }
static int split_mbox(const char *file, const char *dir, int allow_bare, int nr_prec, int skip) { char name[PATH_MAX]; int ret = -1; int peek; FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; if (!f) { error("cannot open mbox %s", file); goto out; } do { peek = fgetc(f); } while (isspace(peek)); ungetc(peek, f); if (strbuf_getwholeline(&buf, f, '\n')) { /* empty stdin is OK */ if (f != stdin) { error("cannot read mbox %s", file); goto out; } file_done = 1; } while (!file_done) { sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); file_done = split_one(f, name, allow_bare); } if (f != stdin) fclose(f); ret = skip; out: return ret; }
static int compute_and_write_prerequisites(int bundle_fd, struct rev_info *revs, int argc, const char **argv) { struct child_process rls = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; FILE *rls_fout; int i; argv_array_pushl(&rls.args, "rev-list", "--boundary", "--pretty=oneline", NULL); for (i = 1; i < argc; i++) argv_array_push(&rls.args, argv[i]); rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { unsigned char sha1[20]; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_sha1_hex(buf.buf + 1, sha1)) { struct object *object = parse_object_or_die(sha1, buf.buf); object->flags |= UNINTERESTING; add_pending_object(revs, object, buf.buf); } } else if (!get_sha1_hex(buf.buf, sha1)) { struct object *object = parse_object_or_die(sha1, buf.buf); object->flags |= SHOWN; } } strbuf_release(&buf); fclose(rls_fout); if (finish_command(&rls)) return error(_("rev-list died")); return 0; }
/* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * work on (i.e. what is left by the previous invocation of "git * rerere" during the current conflict resolution session). */ static void read_rr(struct repository *r, struct string_list *rr) { struct strbuf buf = STRBUF_INIT; FILE *in = fopen_or_warn(git_path_merge_rr(r), "r"); if (!in) return; while (!strbuf_getwholeline(&buf, in, '\0')) { char *path; unsigned char hash[GIT_MAX_RAWSZ]; struct rerere_id *id; int variant; const unsigned hexsz = the_hash_algo->hexsz; /* There has to be the hash, tab, path and then NUL */ if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash)) die(_("corrupt MERGE_RR")); if (buf.buf[hexsz] != '.') { variant = 0; path = buf.buf + hexsz; } else { errno = 0; variant = strtol(buf.buf + hexsz + 1, &path, 10); if (errno) die(_("corrupt MERGE_RR")); } if (*(path++) != '\t') die(_("corrupt MERGE_RR")); buf.buf[hexsz] = '\0'; id = new_rerere_id_hex(buf.buf); id->variant = variant; string_list_insert(rr, path)->util = id; } strbuf_release(&buf); fclose(in); }
static int split_maildir(const char *maildir, const char *dir, int nr_prec, int skip) { char file[PATH_MAX]; char name[PATH_MAX]; int ret = -1; int i; struct string_list list = {NULL, 0, 0, 1}; if (populate_maildir_list(&list, maildir) < 0) goto out; for (i = 0; i < list.nr; i++) { FILE *f; snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].string); f = fopen(file, "r"); if (!f) { error("cannot open mail %s (%s)", file, strerror(errno)); goto out; } if (strbuf_getwholeline(&buf, f, '\n')) { error("cannot read mail %s (%s)", file, strerror(errno)); goto out; } sprintf(name, "%s/%0*d", dir, nr_prec, ++skip); split_one(f, name, 1); fclose(f); } ret = skip; out: string_list_clear(&list, 1); return ret; }
/* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * work on (i.e. what is left by the previous invocation of "git * rerere" during the current conflict resolution session). */ static void read_rr(struct string_list *rr) { struct strbuf buf = STRBUF_INIT; FILE *in = fopen_or_warn(git_path_merge_rr(), "r"); if (!in) return; while (!strbuf_getwholeline(&buf, in, '\0')) { char *path; unsigned char sha1[20]; struct rerere_id *id; int variant; /* There has to be the hash, tab, path and then NUL */ if (buf.len < 42 || get_sha1_hex(buf.buf, sha1)) die("corrupt MERGE_RR"); if (buf.buf[40] != '.') { variant = 0; path = buf.buf + 40; } else { errno = 0; variant = strtol(buf.buf + 41, &path, 10); if (errno) die("corrupt MERGE_RR"); } if (*(path++) != '\t') die("corrupt MERGE_RR"); buf.buf[40] = '\0'; id = new_rerere_id_hex(buf.buf); id->variant = variant; string_list_insert(rr, path)->util = id; } strbuf_release(&buf); fclose(in); }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; struct strbuf buf = STRBUF_INIT; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { unsigned char sha1[20]; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_sha1_hex(buf.buf + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, xstrdup(buf.buf)); } } else if (!get_sha1_hex(buf.buf, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } strbuf_release(&buf); fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (read_ref_full(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf) { int patchlen = 0, found_next = 0; int before = -1, after = -1; 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_sha1_hex(p, next_sha1)) { 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. */ 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) hashclr(next_sha1); return patchlen; }
static void handle_body(void) { struct strbuf prev = STRBUF_INIT; /* Skip up to the first boundary */ if (*content_top) { if (!find_boundary()) goto handle_body_out; } do { /* process any boundary lines */ if (*content_top && is_multipart_boundary(&line)) { /* flush any leftover */ if (prev.len) { handle_filter(&prev); strbuf_reset(&prev); } if (!handle_boundary()) goto handle_body_out; } /* Unwrap transfer encoding */ decode_transfer_encoding(&line); switch (transfer_encoding) { case TE_BASE64: case TE_QP: { struct strbuf **lines, **it, *sb; /* Prepend any previous partial lines */ strbuf_insert(&line, 0, prev.buf, prev.len); strbuf_reset(&prev); /* binary data most likely doesn't have newlines */ if (message_type != TYPE_TEXT) { handle_filter(&line); break; } /* * This is a decoded line that may contain * multiple new lines. Pass only one chunk * at a time to handle_filter() */ lines = strbuf_split(&line, '\n'); for (it = lines; (sb = *it); it++) { if (*(it + 1) == NULL) /* The last line */ if (sb->buf[sb->len - 1] != '\n') { /* Partial line, save it for later. */ strbuf_addbuf(&prev, sb); break; } handle_filter(sb); } /* * The partial chunk is saved in "prev" and will be * appended by the next iteration of read_line_with_nul(). */ strbuf_list_free(lines); break; } default: handle_filter(&line); } } while (!strbuf_getwholeline(&line, fin, '\n')); handle_body_out: strbuf_release(&prev); }
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; const char *p = line; int len; if (!skip_prefix(line, "diff-tree ", &p) && !skip_prefix(line, "commit ", &p) && !skip_prefix(line, "From ", &p) && starts_with(line, "\\ ") && 12 < strlen(line)) continue; if (!get_oid_hex(p, next_oid)) { found_next = 1; break; } /* Ignore commit comments */ if (!patchlen && !starts_with(line, "diff ")) continue; /* Parsing diff header? */ if (before == -1) { if (starts_with(line, "index ")) continue; else if (starts_with(line, "--- ")) before = after = 1; else if (!isalpha(line[0])) break; } /* Looking for a valid hunk header? */ if (before == 0 && after == 0) { if (starts_with(line, "@@ -")) { /* Parse next hunk, but ignore line numbers. */ scan_hunk_header(line, &before, &after); continue; } /* Split at the end of the patch. */ if (!starts_with(line, "diff ")) 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; }
/* * ... and its getline() method implementation */ static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_) { struct rerere_io_file *io = (struct rerere_io_file *)io_; return strbuf_getwholeline(sb, io->input, '\n'); }