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(enum object_type type, unsigned char *sha1_new)21{22fprintf(stderr, " %s: %s\n", typename(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{54unsigned char sha1_old[20], sha1_new[20];55char oldh[41], newh[41];56struct commit *current, *updated;57enum object_type type;5859if (get_sha1_hex(new_head, sha1_new))60die("malformed object name %s", new_head);6162type = sha1_object_info(sha1_new, NULL);63if (type < 0)64die("object %s not found", new_head);6566if (!*name) {67/* Not storing */68if (verbose) {69fprintf(stderr, "* fetched %s\n", note);70show_new(type, sha1_new);71}72return 0;73}7475if (get_sha1(name, sha1_old)) {76char *msg;77just_store:78/* new ref */79if (!strncmp(name, "refs/tags/", 10))80msg = "storing tag";81else82msg = "storing head";83fprintf(stderr, "* %s: storing %s\n",84name, note);85show_new(type, sha1_new);86return update_ref(msg, name, sha1_new, NULL);87}8889if (!hashcmp(sha1_old, sha1_new)) {90if (verbose) {91fprintf(stderr, "* %s: same as %s\n", name, note);92show_new(type, sha1_new);93}94return 0;95}9697if (!strncmp(name, "refs/tags/", 10)) {98fprintf(stderr, "* %s: updating with %s\n", name, note);99show_new(type, sha1_new);100return update_ref("updating tag", name, sha1_new, NULL);101}102103current = lookup_commit_reference(sha1_old);104updated = lookup_commit_reference(sha1_new);105if (!current || !updated)106goto just_store;107108strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));109strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));110111if (in_merge_bases(current, &updated, 1)) {112fprintf(stderr, "* %s: fast forward to %s\n",113name, note);114fprintf(stderr, " old..new: %s..%s\n", oldh, newh);115return update_ref("fast forward", name, sha1_new, sha1_old);116}117if (!force) {118fprintf(stderr,119"* %s: not updating to non-fast forward %s\n",120name, note);121fprintf(stderr,122" old...new: %s...%s\n", oldh, newh);123return 1;124}125fprintf(stderr,126"* %s: forcing update to non-fast forward %s\n",127name, note);128fprintf(stderr, " old...new: %s...%s\n", oldh, newh);129return update_ref("forced-update", name, sha1_new, sha1_old);130}131132static int append_fetch_head(FILE *fp,133const char *head, const char *remote,134const char *remote_name, const char *remote_nick,135const char *local_name, int not_for_merge,136int verbose, int force)137{138struct commit *commit;139int remote_len, i, note_len;140unsigned char sha1[20];141char note[1024];142const char *what, *kind;143144if (get_sha1(head, sha1))145return error("Not a valid object name: %s", head);146commit = lookup_commit_reference(sha1);147if (!commit)148not_for_merge = 1;149150if (!strcmp(remote_name, "HEAD")) {151kind = "";152what = "";153}154else if (!strncmp(remote_name, "refs/heads/", 11)) {155kind = "branch";156what = remote_name + 11;157}158else if (!strncmp(remote_name, "refs/tags/", 10)) {159kind = "tag";160what = remote_name + 10;161}162else if (!strncmp(remote_name, "refs/remotes/", 13)) {163kind = "remote branch";164what = remote_name + 13;165}166else {167kind = "";168what = remote_name;169}170171remote_len = strlen(remote);172for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)173;174remote_len = i + 1;175if (4 < i && !strncmp(".git", remote + i - 3, 4))176remote_len = i - 3;177note_len = sprintf(note, "%s\t%s\t",178sha1_to_hex(commit ? commit->object.sha1 : sha1),179not_for_merge ? "not-for-merge" : "");180if (*what) {181if (*kind)182note_len += sprintf(note + note_len, "%s ", kind);183note_len += sprintf(note + note_len, "'%s' of ", what);184}185note_len += sprintf(note + note_len, "%.*s", remote_len, remote);186fprintf(fp, "%s\n", note);187return update_local_ref(local_name, head, note, verbose, force);188}189190static char *keep;191static void remove_keep(void)192{193if (keep && *keep)194unlink(keep);195}196197static void remove_keep_on_signal(int signo)198{199remove_keep();200signal(SIGINT, SIG_DFL);201raise(signo);202}203204static char *find_local_name(const char *remote_name, const char *refs,205int *force_p, int *not_for_merge_p)206{207const char *ref = refs;208int len = strlen(remote_name);209210while (ref) {211const char *next;212int single_force, not_for_merge;213214while (*ref == '\n')215ref++;216if (!*ref)217break;218next = strchr(ref, '\n');219220single_force = not_for_merge = 0;221if (*ref == '+') {222single_force = 1;223ref++;224}225if (*ref == '.') {226not_for_merge = 1;227ref++;228if (*ref == '+') {229single_force = 1;230ref++;231}232}233if (!strncmp(remote_name, ref, len) && ref[len] == ':') {234const char *local_part = ref + len + 1;235char *ret;236int retlen;237238if (!next)239retlen = strlen(local_part);240else241retlen = next - local_part;242ret = xmalloc(retlen + 1);243memcpy(ret, local_part, retlen);244ret[retlen] = 0;245*force_p = single_force;246*not_for_merge_p = not_for_merge;247return ret;248}249ref = next;250}251return NULL;252}253254static int fetch_native_store(FILE *fp,255const char *remote,256const char *remote_nick,257const char *refs,258int verbose, int force)259{260char buffer[1024];261int err = 0;262263signal(SIGINT, remove_keep_on_signal);264atexit(remove_keep);265266while (fgets(buffer, sizeof(buffer), stdin)) {267int len;268char *cp;269char *local_name;270int single_force, not_for_merge;271272for (cp = buffer; *cp && !isspace(*cp); cp++)273;274if (*cp)275*cp++ = 0;276len = strlen(cp);277if (len && cp[len-1] == '\n')278cp[--len] = 0;279if (!strcmp(buffer, "failed"))280die("Fetch failure: %s", remote);281if (!strcmp(buffer, "pack"))282continue;283if (!strcmp(buffer, "keep")) {284char *od = get_object_directory();285int len = strlen(od) + strlen(cp) + 50;286keep = xmalloc(len);287sprintf(keep, "%s/pack/pack-%s.keep", od, cp);288continue;289}290291local_name = find_local_name(cp, refs,292&single_force, ¬_for_merge);293if (!local_name)294continue;295err |= append_fetch_head(fp,296buffer, remote, cp, remote_nick,297local_name, not_for_merge,298verbose, force || single_force);299}300return err;301}302303static int parse_reflist(const char *reflist)304{305const char *ref;306307printf("refs='");308for (ref = reflist; ref; ) {309const char *next;310while (*ref && isspace(*ref))311ref++;312if (!*ref)313break;314for (next = ref; *next && !isspace(*next); next++)315;316printf("\n%.*s", (int)(next - ref), ref);317ref = next;318}319printf("'\n");320321printf("rref='");322for (ref = reflist; ref; ) {323const char *next, *colon;324while (*ref && isspace(*ref))325ref++;326if (!*ref)327break;328for (next = ref; *next && !isspace(*next); next++)329;330if (*ref == '.')331ref++;332if (*ref == '+')333ref++;334colon = strchr(ref, ':');335putchar('\n');336printf("%.*s", (int)((colon ? colon : next) - ref), ref);337ref = next;338}339printf("'\n");340return 0;341}342343static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,344const char **refs)345{346int i, matchlen, replacelen;347int found_one = 0;348const char *remote = *refs++;349numrefs--;350351if (numrefs == 0) {352fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",353remote);354printf("empty\n");355}356357for (i = 0; i < numrefs; i++) {358const char *ref = refs[i];359const char *lref = ref;360const char *colon;361const char *tail;362const char *ls;363const char *next;364365if (*lref == '+')366lref++;367colon = strchr(lref, ':');368tail = lref + strlen(lref);369if (!(colon &&3702 < colon - lref &&371colon[-1] == '*' &&372colon[-2] == '/' &&3732 < tail - (colon + 1) &&374tail[-1] == '*' &&375tail[-2] == '/')) {376/* not a glob */377if (!found_one++)378printf("explicit\n");379printf("%s\n", ref);380continue;381}382383/* glob */384if (!found_one++)385printf("glob\n");386387/* lref to colon-2 is remote hierarchy name;388* colon+1 to tail-2 is local.389*/390matchlen = (colon-1) - lref;391replacelen = (tail-1) - (colon+1);392for (ls = ls_remote_result; ls; ls = next) {393const char *eol;394unsigned char sha1[20];395int namelen;396397while (*ls && isspace(*ls))398ls++;399next = strchr(ls, '\n');400eol = !next ? (ls + strlen(ls)) : next;401if (!memcmp("^{}", eol-3, 3))402continue;403if (get_sha1_hex(ls, sha1))404continue;405ls += 40;406while (ls < eol && isspace(*ls))407ls++;408/* ls to next (or eol) is the name.409* is it identical to lref to colon-2?410*/411if ((eol - ls) <= matchlen ||412strncmp(ls, lref, matchlen))413continue;414415/* Yes, it is a match */416namelen = eol - ls;417if (lref != ref)418putchar('+');419printf("%.*s:%.*s%.*s\n",420namelen, ls,421replacelen, colon + 1,422namelen - matchlen, ls + matchlen);423}424}425return 0;426}427428int cmd_fetch__tool(int argc, const char **argv, const char *prefix)429{430int verbose = 0;431int force = 0;432433while (1 < argc) {434const char *arg = argv[1];435if (!strcmp("-v", arg))436verbose = 1;437else if (!strcmp("-f", arg))438force = 1;439else440break;441argc--;442argv++;443}444445if (argc <= 1)446return error("Missing subcommand");447448if (!strcmp("append-fetch-head", argv[1])) {449int result;450FILE *fp;451452if (argc != 8)453return error("append-fetch-head takes 6 args");454fp = fopen(git_path("FETCH_HEAD"), "a");455result = append_fetch_head(fp, argv[2], argv[3],456argv[4], argv[5],457argv[6], !!argv[7][0],458verbose, force);459fclose(fp);460return result;461}462if (!strcmp("update-local-ref", argv[1])) {463if (argc != 5)464return error("update-local-ref takes 3 args");465return update_local_ref(argv[2], argv[3], argv[4],466verbose, force);467}468if (!strcmp("native-store", argv[1])) {469int result;470FILE *fp;471472if (argc != 5)473return error("fetch-native-store takes 3 args");474fp = fopen(git_path("FETCH_HEAD"), "a");475result = fetch_native_store(fp, argv[2], argv[3], argv[4],476verbose, force);477fclose(fp);478return result;479}480if (!strcmp("parse-reflist", argv[1])) {481const char *reflist;482if (argc != 3)483return error("parse-reflist takes 1 arg");484reflist = argv[2];485if (!strcmp(reflist, "-"))486reflist = get_stdin();487return parse_reflist(reflist);488}489if (!strcmp("expand-refs-wildcard", argv[1])) {490const char *reflist;491if (argc < 4)492return error("expand-refs-wildcard takes at least 2 args");493reflist = argv[2];494if (!strcmp(reflist, "-"))495reflist = get_stdin();496return expand_refs_wildcard(reflist, argc - 3, argv + 3);497}498499return error("Unknown subcommand: %s", argv[1]);500}