1#include "cache.h"2#include "refs.h"3#include "commit.h"45#define CHUNK_SIZE 102467static char *get_stdin(void)8{9int offset = 0;10char *data = xmalloc(CHUNK_SIZE);1112while (1) {13int cnt = xread(0, data + offset, CHUNK_SIZE);14if (cnt < 0)15die("error reading standard input: %s",16strerror(errno));17if (cnt == 0) {18data[offset] = 0;19break;20}21offset += cnt;22data = xrealloc(data, offset + CHUNK_SIZE);23}24return data;25}2627static void show_new(enum object_type type, unsigned char *sha1_new)28{29fprintf(stderr, " %s: %s\n", typename(type),30find_unique_abbrev(sha1_new, DEFAULT_ABBREV));31}3233static int update_ref(const char *action,34const char *refname,35unsigned char *sha1,36unsigned char *oldval)37{38int len;39char msg[1024];40char *rla = getenv("GIT_REFLOG_ACTION");41static struct ref_lock *lock;4243if (!rla)44rla = "(reflog update)";45len = snprintf(msg, sizeof(msg), "%s: %s", rla, action);46if (sizeof(msg) <= len)47die("insanely long action");48lock = lock_any_ref_for_update(refname, oldval);49if (!lock)50return 1;51if (write_ref_sha1(lock, sha1, msg) < 0)52return 1;53return 0;54}5556static int update_local_ref(const char *name,57const char *new_head,58const char *note,59int verbose, int force)60{61unsigned char sha1_old[20], sha1_new[20];62char oldh[41], newh[41];63struct commit *current, *updated;64enum object_type type;6566if (get_sha1_hex(new_head, sha1_new))67die("malformed object name %s", new_head);6869type = sha1_object_info(sha1_new, NULL);70if (type < 0)71die("object %s not found", new_head);7273if (!*name) {74/* Not storing */75if (verbose) {76fprintf(stderr, "* fetched %s\n", note);77show_new(type, sha1_new);78}79return 0;80}8182if (get_sha1(name, sha1_old)) {83char *msg;84just_store:85/* new ref */86if (!strncmp(name, "refs/tags/", 10))87msg = "storing tag";88else89msg = "storing head";90fprintf(stderr, "* %s: storing %s\n",91name, note);92show_new(type, sha1_new);93return update_ref(msg, name, sha1_new, NULL);94}9596if (!hashcmp(sha1_old, sha1_new)) {97if (verbose) {98fprintf(stderr, "* %s: same as %s\n", name, note);99show_new(type, sha1_new);100}101return 0;102}103104if (!strncmp(name, "refs/tags/", 10)) {105fprintf(stderr, "* %s: updating with %s\n", name, note);106show_new(type, sha1_new);107return update_ref("updating tag", name, sha1_new, NULL);108}109110current = lookup_commit_reference(sha1_old);111updated = lookup_commit_reference(sha1_new);112if (!current || !updated)113goto just_store;114115strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));116strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));117118if (in_merge_bases(current, &updated, 1)) {119fprintf(stderr, "* %s: fast forward to %s\n",120name, note);121fprintf(stderr, " old..new: %s..%s\n", oldh, newh);122return update_ref("fast forward", name, sha1_new, sha1_old);123}124if (!force) {125fprintf(stderr,126"* %s: not updating to non-fast forward %s\n",127name, note);128fprintf(stderr,129" old...new: %s...%s\n", oldh, newh);130return 1;131}132fprintf(stderr,133"* %s: forcing update to non-fast forward %s\n",134name, note);135fprintf(stderr, " old...new: %s...%s\n", oldh, newh);136return update_ref("forced-update", name, sha1_new, sha1_old);137}138139static int append_fetch_head(FILE *fp,140const char *head, const char *remote,141const char *remote_name, const char *remote_nick,142const char *local_name, int not_for_merge,143int verbose, int force)144{145struct commit *commit;146int remote_len, i, note_len;147unsigned char sha1[20];148char note[1024];149const char *what, *kind;150151if (get_sha1(head, sha1))152return error("Not a valid object name: %s", head);153commit = lookup_commit_reference(sha1);154if (!commit)155not_for_merge = 1;156157if (!strcmp(remote_name, "HEAD")) {158kind = "";159what = "";160}161else if (!strncmp(remote_name, "refs/heads/", 11)) {162kind = "branch";163what = remote_name + 11;164}165else if (!strncmp(remote_name, "refs/tags/", 10)) {166kind = "tag";167what = remote_name + 10;168}169else if (!strncmp(remote_name, "refs/remotes/", 13)) {170kind = "remote branch";171what = remote_name + 13;172}173else {174kind = "";175what = remote_name;176}177178remote_len = strlen(remote);179for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)180;181remote_len = i + 1;182if (4 < i && !strncmp(".git", remote + i - 3, 4))183remote_len = i - 3;184note_len = sprintf(note, "%s\t%s\t",185sha1_to_hex(commit ? commit->object.sha1 : sha1),186not_for_merge ? "not-for-merge" : "");187if (*what) {188if (*kind)189note_len += sprintf(note + note_len, "%s ", kind);190note_len += sprintf(note + note_len, "'%s' of ", what);191}192note_len += sprintf(note + note_len, "%.*s", remote_len, remote);193fprintf(fp, "%s\n", note);194return update_local_ref(local_name, head, note, verbose, force);195}196197static char *keep;198static void remove_keep(void)199{200if (keep && *keep)201unlink(keep);202}203204static void remove_keep_on_signal(int signo)205{206remove_keep();207signal(SIGINT, SIG_DFL);208raise(signo);209}210211static char *find_local_name(const char *remote_name, const char *refs,212int *force_p, int *not_for_merge_p)213{214const char *ref = refs;215int len = strlen(remote_name);216217while (ref) {218const char *next;219int single_force, not_for_merge;220221while (*ref == '\n')222ref++;223if (!*ref)224break;225next = strchr(ref, '\n');226227single_force = not_for_merge = 0;228if (*ref == '+') {229single_force = 1;230ref++;231}232if (*ref == '.') {233not_for_merge = 1;234ref++;235if (*ref == '+') {236single_force = 1;237ref++;238}239}240if (!strncmp(remote_name, ref, len) && ref[len] == ':') {241const char *local_part = ref + len + 1;242char *ret;243int retlen;244245if (!next)246retlen = strlen(local_part);247else248retlen = next - local_part;249ret = xmalloc(retlen + 1);250memcpy(ret, local_part, retlen);251ret[retlen] = 0;252*force_p = single_force;253*not_for_merge_p = not_for_merge;254return ret;255}256ref = next;257}258return NULL;259}260261static int fetch_native_store(FILE *fp,262const char *remote,263const char *remote_nick,264const char *refs,265int verbose, int force)266{267char buffer[1024];268int err = 0;269270signal(SIGINT, remove_keep_on_signal);271atexit(remove_keep);272273while (fgets(buffer, sizeof(buffer), stdin)) {274int len;275char *cp;276char *local_name;277int single_force, not_for_merge;278279for (cp = buffer; *cp && !isspace(*cp); cp++)280;281if (*cp)282*cp++ = 0;283len = strlen(cp);284if (len && cp[len-1] == '\n')285cp[--len] = 0;286if (!strcmp(buffer, "failed"))287die("Fetch failure: %s", remote);288if (!strcmp(buffer, "pack"))289continue;290if (!strcmp(buffer, "keep")) {291char *od = get_object_directory();292int len = strlen(od) + strlen(cp) + 50;293keep = xmalloc(len);294sprintf(keep, "%s/pack/pack-%s.keep", od, cp);295continue;296}297298local_name = find_local_name(cp, refs,299&single_force, ¬_for_merge);300if (!local_name)301continue;302err |= append_fetch_head(fp,303buffer, remote, cp, remote_nick,304local_name, not_for_merge,305verbose, force || single_force);306}307return err;308}309310static int parse_reflist(const char *reflist)311{312const char *ref;313314printf("refs='");315for (ref = reflist; ref; ) {316const char *next;317while (*ref && isspace(*ref))318ref++;319if (!*ref)320break;321for (next = ref; *next && !isspace(*next); next++)322;323printf("\n%.*s", (int)(next - ref), ref);324ref = next;325}326printf("'\n");327328printf("rref='");329for (ref = reflist; ref; ) {330const char *next, *colon;331while (*ref && isspace(*ref))332ref++;333if (!*ref)334break;335for (next = ref; *next && !isspace(*next); next++)336;337if (*ref == '.')338ref++;339if (*ref == '+')340ref++;341colon = strchr(ref, ':');342putchar('\n');343printf("%.*s", (int)((colon ? colon : next) - ref), ref);344ref = next;345}346printf("'\n");347return 0;348}349350static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,351const char **refs)352{353int i, matchlen, replacelen;354int found_one = 0;355const char *remote = *refs++;356numrefs--;357358if (numrefs == 0) {359fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",360remote);361printf("empty\n");362}363364for (i = 0; i < numrefs; i++) {365const char *ref = refs[i];366const char *lref = ref;367const char *colon;368const char *tail;369const char *ls;370const char *next;371372if (*lref == '+')373lref++;374colon = strchr(lref, ':');375tail = lref + strlen(lref);376if (!(colon &&3772 < colon - lref &&378colon[-1] == '*' &&379colon[-2] == '/' &&3802 < tail - (colon + 1) &&381tail[-1] == '*' &&382tail[-2] == '/')) {383/* not a glob */384if (!found_one++)385printf("explicit\n");386printf("%s\n", ref);387continue;388}389390/* glob */391if (!found_one++)392printf("glob\n");393394/* lref to colon-2 is remote hierarchy name;395* colon+1 to tail-2 is local.396*/397matchlen = (colon-1) - lref;398replacelen = (tail-1) - (colon+1);399for (ls = ls_remote_result; ls; ls = next) {400const char *eol;401unsigned char sha1[20];402int namelen;403404while (*ls && isspace(*ls))405ls++;406next = strchr(ls, '\n');407eol = !next ? (ls + strlen(ls)) : next;408if (!memcmp("^{}", eol-3, 3))409continue;410if (eol - ls < 40)411continue;412if (get_sha1_hex(ls, sha1))413continue;414ls += 40;415while (ls < eol && isspace(*ls))416ls++;417/* ls to next (or eol) is the name.418* is it identical to lref to colon-2?419*/420if ((eol - ls) <= matchlen ||421strncmp(ls, lref, matchlen))422continue;423424/* Yes, it is a match */425namelen = eol - ls;426if (lref != ref)427putchar('+');428printf("%.*s:%.*s%.*s\n",429namelen, ls,430replacelen, colon + 1,431namelen - matchlen, ls + matchlen);432}433}434return 0;435}436437int cmd_fetch__tool(int argc, const char **argv, const char *prefix)438{439int verbose = 0;440int force = 0;441442while (1 < argc) {443const char *arg = argv[1];444if (!strcmp("-v", arg))445verbose = 1;446else if (!strcmp("-f", arg))447force = 1;448else449break;450argc--;451argv++;452}453454if (argc <= 1)455return error("Missing subcommand");456457if (!strcmp("append-fetch-head", argv[1])) {458int result;459FILE *fp;460461if (argc != 8)462return error("append-fetch-head takes 6 args");463fp = fopen(git_path("FETCH_HEAD"), "a");464result = append_fetch_head(fp, argv[2], argv[3],465argv[4], argv[5],466argv[6], !!argv[7][0],467verbose, force);468fclose(fp);469return result;470}471if (!strcmp("native-store", argv[1])) {472int result;473FILE *fp;474475if (argc != 5)476return error("fetch-native-store takes 3 args");477fp = fopen(git_path("FETCH_HEAD"), "a");478result = fetch_native_store(fp, argv[2], argv[3], argv[4],479verbose, force);480fclose(fp);481return result;482}483if (!strcmp("parse-reflist", argv[1])) {484const char *reflist;485if (argc != 3)486return error("parse-reflist takes 1 arg");487reflist = argv[2];488if (!strcmp(reflist, "-"))489reflist = get_stdin();490return parse_reflist(reflist);491}492if (!strcmp("expand-refs-wildcard", argv[1])) {493const char *reflist;494if (argc < 4)495return error("expand-refs-wildcard takes at least 2 args");496reflist = argv[2];497if (!strcmp(reflist, "-"))498reflist = get_stdin();499return expand_refs_wildcard(reflist, argc - 3, argv + 3);500}501502return error("Unknown subcommand: %s", argv[1]);503}