1#include "cache.h"2#include "refs.h"3#include "commit.h"45#define CHUNK_SIZE (1048576)67static char *get_stdin(void)8{9char *data = xmalloc(CHUNK_SIZE);10int offset = 0, read = 0;11read = xread(0, data, CHUNK_SIZE);12while (read == CHUNK_SIZE) {13offset += CHUNK_SIZE;14data = xrealloc(data, offset + CHUNK_SIZE);15read = xread(0, data + offset, CHUNK_SIZE);16}17return data;18}1920static void show_new(char *type, unsigned char *sha1_new)21{22fprintf(stderr, " %s: %s\n", type,23find_unique_abbrev(sha1_new, DEFAULT_ABBREV));24}2526static int update_ref(const char *action,27const char *refname,28unsigned char *sha1,29unsigned char *oldval)30{31int len;32char msg[1024];33char *rla = getenv("GIT_REFLOG_ACTION");34static struct ref_lock *lock;3536if (!rla)37rla = "(reflog update)";38len = snprintf(msg, sizeof(msg), "%s: %s", rla, action);39if (sizeof(msg) <= len)40die("insanely long action");41lock = lock_any_ref_for_update(refname, oldval);42if (!lock)43return 1;44if (write_ref_sha1(lock, sha1, msg) < 0)45return 1;46return 0;47}4849static int update_local_ref(const char *name,50const char *new_head,51const char *note,52int verbose, int force)53{54char type[20];55unsigned char sha1_old[20], sha1_new[20];56char oldh[41], newh[41];57struct commit *current, *updated;5859if (get_sha1_hex(new_head, sha1_new))60die("malformed object name %s", new_head);61if (sha1_object_info(sha1_new, type, NULL))62die("object %s not found", new_head);6364if (!*name) {65/* Not storing */66if (verbose) {67fprintf(stderr, "* fetched %s\n", note);68show_new(type, sha1_new);69}70return 0;71}7273if (get_sha1(name, sha1_old)) {74char *msg;75just_store:76/* new ref */77if (!strncmp(name, "refs/tags/", 10))78msg = "storing tag";79else80msg = "storing head";81fprintf(stderr, "* %s: storing %s\n",82name, note);83show_new(type, sha1_new);84return update_ref(msg, name, sha1_new, NULL);85}8687if (!hashcmp(sha1_old, sha1_new)) {88if (verbose) {89fprintf(stderr, "* %s: same as %s\n", name, note);90show_new(type, sha1_new);91}92return 0;93}9495if (!strncmp(name, "refs/tags/", 10)) {96fprintf(stderr, "* %s: updating with %s\n", name, note);97show_new(type, sha1_new);98return update_ref("updating tag", name, sha1_new, NULL);99}100101current = lookup_commit_reference(sha1_old);102updated = lookup_commit_reference(sha1_new);103if (!current || !updated)104goto just_store;105106strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));107strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));108109if (in_merge_bases(current, &updated, 1)) {110fprintf(stderr, "* %s: fast forward to %s\n",111name, note);112fprintf(stderr, " old..new: %s..%s\n", oldh, newh);113return update_ref("fast forward", name, sha1_new, sha1_old);114}115if (!force) {116fprintf(stderr,117"* %s: not updating to non-fast forward %s\n",118name, note);119fprintf(stderr,120" old...new: %s...%s\n", oldh, newh);121return 1;122}123fprintf(stderr,124"* %s: forcing update to non-fast forward %s\n",125name, note);126fprintf(stderr, " old...new: %s...%s\n", oldh, newh);127return update_ref("forced-update", name, sha1_new, sha1_old);128}129130static int append_fetch_head(FILE *fp,131const char *head, const char *remote,132const char *remote_name, const char *remote_nick,133const char *local_name, int not_for_merge,134int verbose, int force)135{136struct commit *commit;137int remote_len, i, note_len;138unsigned char sha1[20];139char note[1024];140const char *what, *kind;141142if (get_sha1(head, sha1))143return error("Not a valid object name: %s", head);144commit = lookup_commit_reference(sha1);145if (!commit)146not_for_merge = 1;147148if (!strcmp(remote_name, "HEAD")) {149kind = "";150what = "";151}152else if (!strncmp(remote_name, "refs/heads/", 11)) {153kind = "branch";154what = remote_name + 11;155}156else if (!strncmp(remote_name, "refs/tags/", 10)) {157kind = "tag";158what = remote_name + 10;159}160else if (!strncmp(remote_name, "refs/remotes/", 13)) {161kind = "remote branch";162what = remote_name + 13;163}164else {165kind = "";166what = remote_name;167}168169remote_len = strlen(remote);170for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)171;172remote_len = i + 1;173if (4 < i && !strncmp(".git", remote + i - 3, 4))174remote_len = i - 3;175note_len = sprintf(note, "%s\t%s\t",176sha1_to_hex(commit ? commit->object.sha1 : sha1),177not_for_merge ? "not-for-merge" : "");178if (*what) {179if (*kind)180note_len += sprintf(note + note_len, "%s ", kind);181note_len += sprintf(note + note_len, "'%s' of ", what);182}183note_len += sprintf(note + note_len, "%.*s", remote_len, remote);184fprintf(fp, "%s\n", note);185return update_local_ref(local_name, head, note, verbose, force);186}187188static char *keep;189static void remove_keep(void)190{191if (keep && *keep)192unlink(keep);193}194195static void remove_keep_on_signal(int signo)196{197remove_keep();198signal(SIGINT, SIG_DFL);199raise(signo);200}201202static char *find_local_name(const char *remote_name, const char *refs,203int *force_p, int *not_for_merge_p)204{205const char *ref = refs;206int len = strlen(remote_name);207208while (ref) {209const char *next;210int single_force, not_for_merge;211212while (*ref == '\n')213ref++;214if (!*ref)215break;216next = strchr(ref, '\n');217218single_force = not_for_merge = 0;219if (*ref == '+') {220single_force = 1;221ref++;222}223if (*ref == '.') {224not_for_merge = 1;225ref++;226if (*ref == '+') {227single_force = 1;228ref++;229}230}231if (!strncmp(remote_name, ref, len) && ref[len] == ':') {232const char *local_part = ref + len + 1;233char *ret;234int retlen;235236if (!next)237retlen = strlen(local_part);238else239retlen = next - local_part;240ret = xmalloc(retlen + 1);241memcpy(ret, local_part, retlen);242ret[retlen] = 0;243*force_p = single_force;244*not_for_merge_p = not_for_merge;245return ret;246}247ref = next;248}249return NULL;250}251252static int fetch_native_store(FILE *fp,253const char *remote,254const char *remote_nick,255const char *refs,256int verbose, int force)257{258char buffer[1024];259int err = 0;260261signal(SIGINT, remove_keep_on_signal);262atexit(remove_keep);263264while (fgets(buffer, sizeof(buffer), stdin)) {265int len;266char *cp;267char *local_name;268int single_force, not_for_merge;269270for (cp = buffer; *cp && !isspace(*cp); cp++)271;272if (*cp)273*cp++ = 0;274len = strlen(cp);275if (len && cp[len-1] == '\n')276cp[--len] = 0;277if (!strcmp(buffer, "failed"))278die("Fetch failure: %s", remote);279if (!strcmp(buffer, "pack"))280continue;281if (!strcmp(buffer, "keep")) {282char *od = get_object_directory();283int len = strlen(od) + strlen(cp) + 50;284keep = xmalloc(len);285sprintf(keep, "%s/pack/pack-%s.keep", od, cp);286continue;287}288289local_name = find_local_name(cp, refs,290&single_force, ¬_for_merge);291if (!local_name)292continue;293err |= append_fetch_head(fp,294buffer, remote, cp, remote_nick,295local_name, not_for_merge,296verbose, force || single_force);297}298return err;299}300301static int parse_reflist(const char *reflist)302{303const char *ref;304305printf("refs='");306for (ref = reflist; ref; ) {307const char *next;308while (*ref && isspace(*ref))309ref++;310if (!*ref)311break;312for (next = ref; *next && !isspace(*next); next++)313;314printf("\n%.*s", (int)(next - ref), ref);315ref = next;316}317printf("'\n");318319printf("rref='");320for (ref = reflist; ref; ) {321const char *next, *colon;322while (*ref && isspace(*ref))323ref++;324if (!*ref)325break;326for (next = ref; *next && !isspace(*next); next++)327;328if (*ref == '.')329ref++;330if (*ref == '+')331ref++;332colon = strchr(ref, ':');333putchar('\n');334printf("%.*s", (int)((colon ? colon : next) - ref), ref);335ref = next;336}337printf("'\n");338return 0;339}340341static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,342const char **refs)343{344int i, matchlen, replacelen;345int found_one = 0;346const char *remote = *refs++;347numrefs--;348349if (numrefs == 0) {350fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",351remote);352printf("empty\n");353}354355for (i = 0; i < numrefs; i++) {356const char *ref = refs[i];357const char *lref = ref;358const char *colon;359const char *tail;360const char *ls;361const char *next;362363if (*lref == '+')364lref++;365colon = strchr(lref, ':');366tail = lref + strlen(lref);367if (!(colon &&3682 < colon - lref &&369colon[-1] == '*' &&370colon[-2] == '/' &&3712 < tail - (colon + 1) &&372tail[-1] == '*' &&373tail[-2] == '/')) {374/* not a glob */375if (!found_one++)376printf("explicit\n");377printf("%s\n", ref);378continue;379}380381/* glob */382if (!found_one++)383printf("glob\n");384385/* lref to colon-2 is remote hierarchy name;386* colon+1 to tail-2 is local.387*/388matchlen = (colon-1) - lref;389replacelen = (tail-1) - (colon+1);390for (ls = ls_remote_result; ls; ls = next) {391const char *eol;392unsigned char sha1[20];393int namelen;394395while (*ls && isspace(*ls))396ls++;397next = strchr(ls, '\n');398eol = !next ? (ls + strlen(ls)) : next;399if (!memcmp("^{}", eol-3, 3))400continue;401if (get_sha1_hex(ls, sha1))402continue;403ls += 40;404while (ls < eol && isspace(*ls))405ls++;406/* ls to next (or eol) is the name.407* is it identical to lref to colon-2?408*/409if ((eol - ls) <= matchlen ||410strncmp(ls, lref, matchlen))411continue;412413/* Yes, it is a match */414namelen = eol - ls;415if (lref != ref)416putchar('+');417printf("%.*s:%.*s%.*s\n",418namelen, ls,419replacelen, colon + 1,420namelen - matchlen, ls + matchlen);421}422}423return 0;424}425426int cmd_fetch__tool(int argc, const char **argv, const char *prefix)427{428int verbose = 0;429int force = 0;430431while (1 < argc) {432const char *arg = argv[1];433if (!strcmp("-v", arg))434verbose = 1;435else if (!strcmp("-f", arg))436force = 1;437else438break;439argc--;440argv++;441}442443if (argc <= 1)444return error("Missing subcommand");445446if (!strcmp("append-fetch-head", argv[1])) {447int result;448FILE *fp;449450if (argc != 8)451return error("append-fetch-head takes 6 args");452fp = fopen(git_path("FETCH_HEAD"), "a");453result = append_fetch_head(fp, argv[2], argv[3],454argv[4], argv[5],455argv[6], !!argv[7][0],456verbose, force);457fclose(fp);458return result;459}460if (!strcmp("update-local-ref", argv[1])) {461if (argc != 5)462return error("update-local-ref takes 3 args");463return update_local_ref(argv[2], argv[3], argv[4],464verbose, force);465}466if (!strcmp("native-store", argv[1])) {467int result;468FILE *fp;469470if (argc != 5)471return error("fetch-native-store takes 3 args");472fp = fopen(git_path("FETCH_HEAD"), "a");473result = fetch_native_store(fp, argv[2], argv[3], argv[4],474verbose, force);475fclose(fp);476return result;477}478if (!strcmp("parse-reflist", argv[1])) {479const char *reflist;480if (argc != 3)481return error("parse-reflist takes 1 arg");482reflist = argv[2];483if (!strcmp(reflist, "-"))484reflist = get_stdin();485return parse_reflist(reflist);486}487if (!strcmp("expand-refs-wildcard", argv[1])) {488const char *reflist;489if (argc < 4)490return error("expand-refs-wildcard takes at least 2 args");491reflist = argv[2];492if (!strcmp(reflist, "-"))493reflist = get_stdin();494return expand_refs_wildcard(reflist, argc - 3, argv + 3);495}496497return error("Unknown subcommand: %s", argv[1]);498}