static void cleanup_subject(struct strbuf *subject) { char *pos; size_t remove; while (subject->len) { switch (*subject->buf) { case 'r': case 'R': if (subject->len <= 3) break; if (!memcmp(subject->buf + 1, "e:", 2)) { strbuf_remove(subject, 0, 3); continue; } break; case ' ': case '\t': case ':': strbuf_remove(subject, 0, 1); continue; case '[': if ((pos = strchr(subject->buf, ']'))) { remove = pos - subject->buf; if (remove <= (subject->len - remove) * 2) { strbuf_remove(subject, 0, remove + 1); continue; } } else strbuf_remove(subject, 0, 1); break; } strbuf_trim(subject); return; } }
static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject) { size_t at = 0; while (at < subject->len) { char *pos; size_t remove; switch (subject->buf[at]) { case 'r': case 'R': if (subject->len <= at + 3) break; if ((subject->buf[at + 1] == 'e' || subject->buf[at + 1] == 'E') && subject->buf[at + 2] == ':') { strbuf_remove(subject, at, 3); continue; } at++; break; case ' ': case '\t': case ':': strbuf_remove(subject, at, 1); continue; case '[': pos = strchr(subject->buf + at, ']'); if (!pos) break; remove = pos - subject->buf + at + 1; if (!mi->keep_non_patch_brackets_in_subject || (7 <= remove && memmem(subject->buf + at, remove, "PATCH", 5))) strbuf_remove(subject, at, remove); else { at += remove; /* * If the input had a space after the ], keep * it. We don't bother with finding the end of * the space, since we later normalize it * anyway. */ if (isspace(subject->buf[at])) at += 1; } continue; } break; } strbuf_trim(subject); }
static char *replace_encoding_header(char *buf, const char *encoding) { struct strbuf tmp = STRBUF_INIT; size_t start, len; char *cp = buf; /* guess if there is an encoding header before a \n\n */ while (strncmp(cp, "encoding ", strlen("encoding "))) { cp = strchr(cp, '\n'); if (!cp || *++cp == '\n') return buf; } start = cp - buf; cp = strchr(cp, '\n'); if (!cp) return buf; /* should not happen but be defensive */ len = cp + 1 - (buf + start); strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ strbuf_remove(&tmp, start, len); } else { /* just replaces XXXX in 'encoding XXXX\n' */ strbuf_splice(&tmp, start + strlen("encoding "), len - strlen("encoding \n"), encoding, strlen(encoding)); } return strbuf_detach(&tmp, NULL); }
static void format_sanitized_subject(struct strbuf *sb, const char *msg) { size_t trimlen; size_t start_len = sb->len; int space = 2; for (; *msg && *msg != '\n'; msg++) { if (istitlechar(*msg)) { if (space == 1) strbuf_addch(sb, '-'); space = 0; strbuf_addch(sb, *msg); if (*msg == '.') while (*(msg+1) == '.') msg++; } else space |= 1; } /* trim any trailing '.' or '-' characters */ trimlen = 0; while (sb->len - trimlen > start_len && (sb->buf[sb->len - 1 - trimlen] == '.' || sb->buf[sb->len - 1 - trimlen] == '-')) trimlen++; strbuf_remove(sb, sb->len - trimlen, trimlen); }
/* * Extract branch information from rebase/bisect */ static char *read_and_strip_branch(const char *path) { struct strbuf sb = STRBUF_INIT; unsigned char sha1[20]; if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0) goto got_nothing; while (&sb.len && sb.buf[sb.len - 1] == '\n') strbuf_setlen(&sb, sb.len - 1); if (!sb.len) goto got_nothing; if (!prefixcmp(sb.buf, "refs/heads/")) strbuf_remove(&sb,0, strlen("refs/heads/")); else if (!prefixcmp(sb.buf, "refs/")) ; else if (!get_sha1_hex(sb.buf, sha1)) { const char *abbrev; abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); strbuf_reset(&sb); strbuf_addstr(&sb, abbrev); } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */ goto got_nothing; else /* bisect */ ; return strbuf_detach(&sb, NULL); got_nothing: strbuf_release(&sb); return NULL; }
static int parse_bundle_header(int fd, struct bundle_header *header, const char *report_path) { struct strbuf buf = STRBUF_INIT; int status = 0; /* The bundle header begins with the signature */ if (strbuf_getwholeline_fd(&buf, fd, '\n') || strcmp(buf.buf, bundle_signature)) { if (report_path) error("'%s' does not look like a v2 bundle file", report_path); status = -1; goto abort; } /* The bundle header ends with an empty line */ while (!strbuf_getwholeline_fd(&buf, fd, '\n') && buf.len && buf.buf[0] != '\n') { unsigned char sha1[20]; int is_prereq = 0; if (*buf.buf == '-') { is_prereq = 1; strbuf_remove(&buf, 0, 1); } strbuf_rtrim(&buf); /* * Tip lines have object name, SP, and refname. * Prerequisites have object name that is optionally * followed by SP and subject line. */ if (get_sha1_hex(buf.buf, sha1) || (40 <= buf.len && !isspace(buf.buf[40])) || (!is_prereq && buf.len <= 40)) { if (report_path) error("unrecognized header: %s%s (%d)", (is_prereq ? "-" : ""), buf.buf, (int)buf.len); status = -1; break; } else { if (is_prereq) add_to_ref_list(sha1, "", &header->prerequisites); else add_to_ref_list(sha1, buf.buf + 41, &header->references); } } abort: if (status) { close(fd); fd = -1; } strbuf_release(&buf); return fd; }
static void cleanup_space(struct strbuf *sb) { size_t pos, cnt; for (pos = 0; pos < sb->len; pos++) { if (isspace(sb->buf[pos])) { sb->buf[pos] = ' '; for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++); strbuf_remove(sb, pos + 1, cnt); } } }
int strbuf_stripout(struct strbuf *sb, void *buf, size_t len) { len = min(len, sb->len); if (len == 0) goto out; memcpy(buf, sb->buf, len); strbuf_remove(sb, 0, len); out: return len; }
/* return length of 's' in letters, ANSI escapes stripped */ static int item_length(unsigned int colopts, const char *s) { int len, i = 0; struct strbuf str = STRBUF_INIT; strbuf_addstr(&str, s); while ((s = strstr(str.buf + i, "\033[")) != NULL) { int len = strspn(s + 2, "0123456789;"); i = s - str.buf; strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */ } len = utf8_strwidth(str.buf); strbuf_release(&str); return len; }
static char *find_linked_symref(const char *symref, const char *branch, const char *id) { struct strbuf sb = STRBUF_INIT; struct strbuf path = STRBUF_INIT; struct strbuf gitdir = STRBUF_INIT; char *existing = NULL; /* * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside * $GIT_DIR so resolve_ref_unsafe() won't work (it uses * git_path). Parse the ref ourselves. */ if (id) strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref); else strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref); if (!strbuf_readlink(&sb, path.buf, 0)) { if (!starts_with(sb.buf, "refs/") || check_refname_format(sb.buf, 0)) goto done; } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 && starts_with(sb.buf, "ref:")) { strbuf_remove(&sb, 0, strlen("ref:")); strbuf_trim(&sb); } else goto done; if (strcmp(sb.buf, branch)) goto done; if (id) { strbuf_reset(&path); strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) goto done; strbuf_rtrim(&gitdir); } else strbuf_addstr(&gitdir, get_git_common_dir()); strbuf_strip_suffix(&gitdir, ".git"); existing = strbuf_detach(&gitdir, NULL); done: strbuf_release(&path); strbuf_release(&sb); strbuf_release(&gitdir); return existing; }
static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_) { struct rerere_io_mem *io = (struct rerere_io_mem *)io_; char *ep; size_t len; strbuf_release(sb); if (!io->input.len) return -1; ep = strchrnul(io->input.buf, '\n'); if (*ep == '\n') ep++; len = ep - io->input.buf; strbuf_add(sb, io->input.buf, len); strbuf_remove(&io->input, 0, len); return 0; }
static int check_emacsclient_version(void) { struct strbuf buffer = STRBUF_INIT; struct child_process ec_process; const char *argv_ec[] = { "emacsclient", "--version", NULL }; int version; /* emacsclient prints its version number on stderr */ memset(&ec_process, 0, sizeof(ec_process)); ec_process.argv = argv_ec; ec_process.err = -1; ec_process.stdout_to_stderr = 1; if (start_command(&ec_process)) { fprintf(stderr, "Failed to start emacsclient.\n"); return -1; } strbuf_read(&buffer, ec_process.err, 20); close(ec_process.err); /* * Don't bother checking return value, because "emacsclient --version" * seems to always exits with code 1. */ finish_command(&ec_process); if (prefixcmp(buffer.buf, "emacsclient")) { fprintf(stderr, "Failed to parse emacsclient version.\n"); strbuf_release(&buffer); return -1; } strbuf_remove(&buffer, 0, strlen("emacsclient")); version = atoi(buffer.buf); if (version < 22) { fprintf(stderr, "emacsclient version '%d' too old (< 22).\n", version); strbuf_release(&buffer); return -1; } strbuf_release(&buffer); return 0; }
static int create_directory(char *p) { int i, ret = 0; struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, p); strbuf_addstr(&buf, ".farm"); if (mkdir(buf.buf, 0755) < 0) { if (errno != EEXIST) { eprintf("%m\n"); ret = -1; goto err; } } if (!strlen(farm_dir)) memcpy(farm_dir, buf.buf, buf.len); strbuf_addstr(&buf, "/objects"); if (mkdir(buf.buf, 0755) < 0) { if (errno != EEXIST) { eprintf("%m\n"); ret = -1; goto err; } } for (i = 0; i < 256; i++) { strbuf_addf(&buf, "/%02x", i); if (mkdir(buf.buf, 0755) < 0) { if (errno != EEXIST) { eprintf("%m\n"); ret = -1; goto err; } } strbuf_remove(&buf, buf.len - 3, 3); } if (!strlen(farm_obj_dir)) memcpy(farm_obj_dir, buf.buf, buf.len); err: strbuf_release(&buf); return ret; }
static int check_emacsclient_version(void) { struct strbuf buffer = STRBUF_INIT; struct child_process ec_process; const char *argv_ec[] = { "emacsclient", "--version", NULL }; int version; memset(&ec_process, 0, sizeof(ec_process)); ec_process.argv = argv_ec; ec_process.err = -1; ec_process.stdout_to_stderr = 1; if (start_command(&ec_process)) { fprintf(stderr, "Failed to start emacsclient.\n"); return -1; } strbuf_read(&buffer, ec_process.err, 20); close(ec_process.err); finish_command(&ec_process); if (prefixcmp(buffer.buf, "emacsclient")) { fprintf(stderr, "Failed to parse emacsclient version.\n"); strbuf_release(&buffer); return -1; } strbuf_remove(&buffer, 0, strlen("emacsclient")); version = atoi(buffer.buf); if (version < 22) { fprintf(stderr, "emacsclient version '%d' too old (< 22).\n", version); strbuf_release(&buffer); return -1; } strbuf_release(&buffer); return 0; }
int move_window(struct sliding_view *view, off_t off, size_t width) { off_t file_offset; assert(view); assert(view->width <= view->buf.len); assert(!check_offset_overflow(view->off, view->buf.len)); if (check_offset_overflow(off, width)) return -1; if (off < view->off || off + width < view->off + view->width) { printf("invalid delta: window slides left\n"); return -1; } if (view->max_off >= 0 && view->max_off < off + (off_t) width) { printf("delta preimage ends early\n"); return -1; } file_offset = view->off + view->buf.len; if (off < file_offset) { /* Move the overlapping region into place. */ strbuf_remove(&view->buf, 0, off - view->off); } else { /* Seek ahead to skip the gap. */ if (skip_or_whine(view->file, off - file_offset)) return -1; strbuf_setlen(&view->buf, 0); } if (view->buf.len > width) ; /* Already read. */ else if (read_to_fill_or_whine(view->file, &view->buf, width)) return -1; view->off = off; view->width = width; return 0; }
static int create_directory(const char *p) { int i, ret = 0; struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, p); strbuf_addstr(&buf, ".farm"); if (xmkdir(buf.buf, 0755) < 0) { sd_eprintf("%m"); ret = -1; goto err; } if (!strlen(farm_dir)) strbuf_copyout(&buf, farm_dir, sizeof(farm_dir)); strbuf_addstr(&buf, "/objects"); if (xmkdir(buf.buf, 0755) < 0) { sd_eprintf("%m"); ret = -1; goto err; } for (i = 0; i < 256; i++) { strbuf_addf(&buf, "/%02x", i); if (xmkdir(buf.buf, 0755) < 0) { sd_eprintf("%m"); ret = -1; goto err; } strbuf_remove(&buf, buf.len - 3, 3); } if (!strlen(farm_obj_dir)) strbuf_copyout(&buf, farm_obj_dir, sizeof(farm_obj_dir)); err: strbuf_release(&buf); return ret; }
/* * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL, * set is_detached to 1 (0) if the ref is detached (is not detached). * * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses * git_path). Parse the ref ourselves. * * return -1 if the ref is not a proper ref, 0 otherwise (success) */ static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached) { if (is_detached) *is_detached = 0; if (!strbuf_readlink(ref, path_to_ref, 0)) { /* HEAD is symbolic link */ if (!starts_with(ref->buf, "refs/") || check_refname_format(ref->buf, 0)) return -1; } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) { /* textual symref or detached */ if (!starts_with(ref->buf, "ref:")) { if (is_detached) *is_detached = 1; } else { strbuf_remove(ref, 0, strlen("ref:")); strbuf_trim(ref); if (check_refname_format(ref->buf, 0)) return -1; } } else return -1; return 0; }
static void strbuf_cleanup_path(struct strbuf *sb) { char *path = cleanup_path(sb->buf); if (path > sb->buf) strbuf_remove(sb, 0, path - sb->buf); }
/* Get the name for the merge commit's message. */ static void merge_name(const char *remote, struct strbuf *msg) { struct commit *remote_head; unsigned char branch_head[20]; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; char *found_ref; int len, early; strbuf_branchname(&bname, remote); remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); remote_head = get_merge_parent(remote); if (!remote_head) die(_("'%s' does not point to a commit"), remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { if (!prefixcmp(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } if (!prefixcmp(found_ref, "refs/tags/")) { strbuf_addf(msg, "%s\t\ttag '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } if (!prefixcmp(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } } /* See if remote matches <name>^^^.. or <name>~<number> */ for (len = 0, ptr = remote + strlen(remote); remote < ptr && ptr[-1] == '^'; ptr--) len++; if (len) early = 1; else { early = 0; ptr = strrchr(remote, '~'); if (ptr) { int seen_nonzero = 0; len++; /* count ~ */ while (*++ptr && isdigit(*ptr)) { seen_nonzero |= (*ptr != '0'); len++; } if (*ptr) len = 0; /* not ...~<number> */ else if (seen_nonzero) early = 1; else if (len == 1) early = 1; /* "name~" is "name~1"! */ } } if (len) { struct strbuf truname = STRBUF_INIT; strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); if (ref_exists(truname.buf)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->object.sha1), truname.buf + 11, (early ? " (early part)" : "")); strbuf_release(&truname); goto cleanup; } } if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { const char *filename; FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; filename = git_path("FETCH_HEAD"); fp = fopen(filename, "r"); if (!fp) die_errno(_("could not open '%s' for reading"), filename); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); if (ptr) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->object.sha1), remote); cleanup: strbuf_release(&buf); strbuf_release(&bname); }
static void handle_from(const struct strbuf *from) { char *at; size_t el; struct strbuf f; strbuf_init(&f, from->len); strbuf_addbuf(&f, from); at = strchr(f.buf, '@'); if (!at) { parse_bogus_from(from); return; } /* * If we already have one email, don't take any confusing lines */ if (email.len && strchr(at + 1, '@')) { strbuf_release(&f); return; } /* Pick up the string around '@', possibly delimited with <> * pair; that is the email part. */ while (at > f.buf) { char c = at[-1]; if (isspace(c)) break; if (c == '<') { at[-1] = ' '; break; } at--; } el = strcspn(at, " \n\t\r\v\f>"); strbuf_reset(&email); strbuf_add(&email, at, el); strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0)); /* The remainder is name. It could be * * - "John Doe <john.doe@xz>" (a), or * - "john.doe@xz (John Doe)" (b), or * - "John (zzz) Doe <john.doe@xz> (Comment)" (c) * * but we have removed the email part, so * * - remove extra spaces which could stay after email (case 'c'), and * - trim from both ends, possibly removing the () pair at the end * (cases 'a' and 'b'). */ cleanup_space(&f); strbuf_trim(&f); if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') { strbuf_remove(&f, 0, 1); strbuf_setlen(&f, f.len - 1); } get_sane_name(&name, &f, &email); strbuf_release(&f); }
int cmd_commit(int argc, const char **argv, const char *prefix) { int header_len; struct strbuf sb; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; git_config(git_commit_config); argc = parse_and_validate_options(argc, argv, builtin_commit_usage); index_file = prepare_index(argc, argv, prefix); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix)) { rollback_index_files(); return 1; } /* * The commit object */ strbuf_init(&sb, 0); strbuf_addf(&sb, "tree %s\n", sha1_to_hex(active_cache_tree->sha1)); /* Determine parents */ if (initial_commit) { reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; struct commit *commit; reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) die("could not parse HEAD commit"); for (c = commit->parents; c; c = c->next) add_parent(&sb, c->item->object.sha1); } else if (in_merge) { struct strbuf m; FILE *fp; reflog_msg = "commit (merge)"; add_parent(&sb, head_sha1); strbuf_init(&m, 0); fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die("could not open %s for reading: %s", git_path("MERGE_HEAD"), strerror(errno)); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die("Corrupt MERGE_HEAD file (%s)", m.buf); add_parent(&sb, sha1); } fclose(fp); strbuf_release(&m); } else { reflog_msg = "commit"; strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); } strbuf_addf(&sb, "author %s\n", fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME)); strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (!is_encoding_utf8(git_commit_encoding)) strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); strbuf_addch(&sb, '\n'); /* Finally, get the commit message */ header_len = sb.len; if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { rollback_index_files(); die("could not read commit message"); } /* Truncate the message just before the diff, if any. */ p = strstr(sb.buf, "\ndiff --git a/"); if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (sb.len < header_len || message_is_empty(&sb, header_len)) { rollback_index_files(); die("no commit message? aborting commit."); } strbuf_addch(&sb, '\0'); if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf)) fprintf(stderr, commit_utf8_warn); if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) { rollback_index_files(); die("failed to write commit object"); } ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, 0); nl = strchr(sb.buf + header_len, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_remove(&sb, 0, header_len); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die("cannot lock HEAD ref"); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); die("cannot update HEAD ref"); } unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die ("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover."); rerere(); run_hook(get_index_file(), "post-commit", NULL); if (!quiet) print_summary(prefix, commit_sha1); return 0; }
/* Get the name for the merge commit's message. */ static void merge_name(const char *remote, struct strbuf *msg) { struct object *remote_head; unsigned char branch_head[20], buf_sha[20]; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; int len, early; strbuf_branchname(&bname, remote); remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); if (!remote_head) die("'%s' does not point to a commit", remote); strbuf_addstr(&buf, "refs/heads/"); strbuf_addstr(&buf, remote); resolve_ref(buf.buf, branch_head, 0, NULL); if (!hashcmp(remote_head->sha1, branch_head)) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } /* See if remote matches <name>^^^.. or <name>~<number> */ for (len = 0, ptr = remote + strlen(remote); remote < ptr && ptr[-1] == '^'; ptr--) len++; if (len) early = 1; else { early = 0; ptr = strrchr(remote, '~'); if (ptr) { int seen_nonzero = 0; len++; /* count ~ */ while (*++ptr && isdigit(*ptr)) { seen_nonzero |= (*ptr != '0'); len++; } if (*ptr) len = 0; /* not ...~<number> */ else if (seen_nonzero) early = 1; else if (len == 1) early = 1; /* "name~" is "name~1"! */ } } if (len) { struct strbuf truname = STRBUF_INIT; strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); if (resolve_ref(truname.buf, buf_sha, 0, NULL)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), truname.buf + 11, (early ? " (early part)" : "")); strbuf_release(&truname); goto cleanup; } } if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; fp = fopen(git_path("FETCH_HEAD"), "r"); if (!fp) die_errno("could not open '%s' for reading", git_path("FETCH_HEAD")); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); if (ptr) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->sha1), remote); cleanup: strbuf_release(&buf); strbuf_release(&bname); }