git
/
transport.c
1649 строк · 43.9 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "advice.h"5#include "config.h"6#include "environment.h"7#include "hex.h"8#include "transport.h"9#include "hook.h"10#include "pkt-line.h"11#include "fetch-pack.h"12#include "remote.h"13#include "connect.h"14#include "send-pack.h"15#include "bundle.h"16#include "gettext.h"17#include "refs.h"18#include "refspec.h"19#include "branch.h"20#include "url.h"21#include "submodule.h"22#include "string-list.h"23#include "oid-array.h"24#include "sigchain.h"25#include "trace2.h"26#include "transport-internal.h"27#include "protocol.h"28#include "object-name.h"29#include "color.h"30#include "bundle-uri.h"31
32static int transport_use_color = -1;33static char transport_colors[][COLOR_MAXLEN] = {34GIT_COLOR_RESET,35GIT_COLOR_RED /* REJECTED */36};37
38enum color_transport {39TRANSPORT_COLOR_RESET = 0,40TRANSPORT_COLOR_REJECTED = 141};42
43static int transport_color_config(void)44{
45const char *keys[] = {46"color.transport.reset",47"color.transport.rejected"48}, *key = "color.transport";49char *value;50int i;51static int initialized;52
53if (initialized)54return 0;55initialized = 1;56
57if (!git_config_get_string(key, &value))58transport_use_color = git_config_colorbool(key, value);59
60if (!want_color_stderr(transport_use_color))61return 0;62
63for (i = 0; i < ARRAY_SIZE(keys); i++)64if (!git_config_get_string(keys[i], &value)) {65if (!value)66return config_error_nonbool(keys[i]);67if (color_parse(value, transport_colors[i]) < 0)68return -1;69}70
71return 0;72}
73
74static const char *transport_get_color(enum color_transport ix)75{
76if (want_color_stderr(transport_use_color))77return transport_colors[ix];78return "";79}
80
81static void set_upstreams(struct transport *transport, struct ref *refs,82int pretend)83{
84struct ref *ref;85for (ref = refs; ref; ref = ref->next) {86const char *localname;87const char *tmp;88const char *remotename;89int flag = 0;90/*91* Check suitability for tracking. Must be successful /
92* already up-to-date ref create/modify (not delete).
93*/
94if (ref->status != REF_STATUS_OK &&95ref->status != REF_STATUS_UPTODATE)96continue;97if (!ref->peer_ref)98continue;99if (is_null_oid(&ref->new_oid))100continue;101
102/* Follow symbolic refs (mainly for HEAD). */103localname = ref->peer_ref->name;104remotename = ref->name;105tmp = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),106localname, RESOLVE_REF_READING,107NULL, &flag);108if (tmp && flag & REF_ISSYMREF &&109starts_with(tmp, "refs/heads/"))110localname = tmp;111
112/* Both source and destination must be local branches. */113if (!localname || !starts_with(localname, "refs/heads/"))114continue;115if (!remotename || !starts_with(remotename, "refs/heads/"))116continue;117
118if (!pretend) {119int flag = transport->verbose < 0 ? 0 : BRANCH_CONFIG_VERBOSE;120install_branch_config(flag, localname + 11,121transport->remote->name, remotename);122} else if (transport->verbose >= 0)123printf(_("Would set upstream of '%s' to '%s' of '%s'\n"),124localname + 11, remotename + 11,125transport->remote->name);126}127}
128
129struct bundle_transport_data {130int fd;131struct bundle_header header;132unsigned get_refs_from_bundle_called : 1;133};134
135static void get_refs_from_bundle_inner(struct transport *transport)136{
137struct bundle_transport_data *data = transport->data;138
139data->get_refs_from_bundle_called = 1;140
141if (data->fd > 0)142close(data->fd);143data->fd = read_bundle_header(transport->url, &data->header);144if (data->fd < 0)145die(_("could not read bundle '%s'"), transport->url);146
147transport->hash_algo = data->header.hash_algo;148}
149
150static struct ref *get_refs_from_bundle(struct transport *transport,151int for_push,152struct transport_ls_refs_options *transport_options UNUSED)153{
154struct bundle_transport_data *data = transport->data;155struct ref *result = NULL;156int i;157
158if (for_push)159return NULL;160
161get_refs_from_bundle_inner(transport);162
163for (i = 0; i < data->header.references.nr; i++) {164struct string_list_item *e = data->header.references.items + i;165const char *name = e->string;166struct ref *ref = alloc_ref(name);167struct object_id *oid = e->util;168oidcpy(&ref->old_oid, oid);169ref->next = result;170result = ref;171}172return result;173}
174
175static int fetch_refs_from_bundle(struct transport *transport,176int nr_heads UNUSED,177struct ref **to_fetch UNUSED)178{
179struct bundle_transport_data *data = transport->data;180struct strvec extra_index_pack_args = STRVEC_INIT;181int ret;182
183if (transport->progress)184strvec_push(&extra_index_pack_args, "-v");185
186if (!data->get_refs_from_bundle_called)187get_refs_from_bundle_inner(transport);188ret = unbundle(the_repository, &data->header, data->fd,189&extra_index_pack_args,190fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0);191transport->hash_algo = data->header.hash_algo;192return ret;193}
194
195static int close_bundle(struct transport *transport)196{
197struct bundle_transport_data *data = transport->data;198if (data->fd > 0)199close(data->fd);200bundle_header_release(&data->header);201free(data);202return 0;203}
204
205struct git_transport_data {206struct git_transport_options options;207struct child_process *conn;208int fd[2];209unsigned finished_handshake : 1;210enum protocol_version version;211struct oid_array extra_have;212struct oid_array shallow;213};214
215static int set_git_option(struct git_transport_options *opts,216const char *name, const char *value)217{
218if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {219opts->uploadpack = value;220return 0;221} else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {222opts->receivepack = value;223return 0;224} else if (!strcmp(name, TRANS_OPT_THIN)) {225opts->thin = !!value;226return 0;227} else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {228opts->followtags = !!value;229return 0;230} else if (!strcmp(name, TRANS_OPT_KEEP)) {231opts->keep = !!value;232return 0;233} else if (!strcmp(name, TRANS_OPT_UPDATE_SHALLOW)) {234opts->update_shallow = !!value;235return 0;236} else if (!strcmp(name, TRANS_OPT_DEPTH)) {237if (!value)238opts->depth = 0;239else {240char *end;241opts->depth = strtol(value, &end, 0);242if (*end)243die(_("transport: invalid depth option '%s'"), value);244}245return 0;246} else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {247opts->deepen_since = value;248return 0;249} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {250opts->deepen_not = (const struct string_list *)value;251return 0;252} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {253opts->deepen_relative = !!value;254return 0;255} else if (!strcmp(name, TRANS_OPT_FROM_PROMISOR)) {256opts->from_promisor = !!value;257return 0;258} else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) {259list_objects_filter_die_if_populated(&opts->filter_options);260parse_list_objects_filter(&opts->filter_options, value);261return 0;262} else if (!strcmp(name, TRANS_OPT_REFETCH)) {263opts->refetch = !!value;264return 0;265} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {266opts->reject_shallow = !!value;267return 0;268}269return 1;270}
271
272static int connect_setup(struct transport *transport, int for_push)273{
274struct git_transport_data *data = transport->data;275int flags = transport->verbose > 0 ? CONNECT_VERBOSE : 0;276
277if (data->conn)278return 0;279
280switch (transport->family) {281case TRANSPORT_FAMILY_ALL: break;282case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;283case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;284}285
286data->conn = git_connect(data->fd, transport->url,287for_push ?288"git-receive-pack" :289"git-upload-pack",290for_push ?291data->options.receivepack :292data->options.uploadpack,293flags);294
295return 0;296}
297
298static void die_if_server_options(struct transport *transport)299{
300if (!transport->server_options || !transport->server_options->nr)301return;302advise(_("see protocol.version in 'git help config' for more details"));303die(_("server options require protocol version 2 or later"));304}
305
306/*
307* Obtains the protocol version from the transport and writes it to
308* transport->data->version, first connecting if not already connected.
309*
310* If the protocol version is one that allows skipping the listing of remote
311* refs, and must_list_refs is 0, the listing of remote refs is skipped and
312* this function returns NULL. Otherwise, this function returns the list of
313* remote refs.
314*/
315static struct ref *handshake(struct transport *transport, int for_push,316struct transport_ls_refs_options *options,317int must_list_refs)318{
319struct git_transport_data *data = transport->data;320struct ref *refs = NULL;321struct packet_reader reader;322size_t sid_len;323const char *server_sid;324
325connect_setup(transport, for_push);326
327packet_reader_init(&reader, data->fd[0], NULL, 0,328PACKET_READ_CHOMP_NEWLINE |329PACKET_READ_GENTLE_ON_EOF |330PACKET_READ_DIE_ON_ERR_PACKET);331
332data->version = discover_version(&reader);333switch (data->version) {334case protocol_v2:335if (server_feature_v2("session-id", &server_sid))336trace2_data_string("transfer", NULL, "server-sid", server_sid);337if (must_list_refs)338get_remote_refs(data->fd[1], &reader, &refs, for_push,339options,340transport->server_options,341transport->stateless_rpc);342break;343case protocol_v1:344case protocol_v0:345die_if_server_options(transport);346get_remote_heads(&reader, &refs,347for_push ? REF_NORMAL : 0,348&data->extra_have,349&data->shallow);350server_sid = server_feature_value("session-id", &sid_len);351if (server_sid) {352char *sid = xstrndup(server_sid, sid_len);353trace2_data_string("transfer", NULL, "server-sid", sid);354free(sid);355}356break;357case protocol_unknown_version:358BUG("unknown protocol version");359}360data->finished_handshake = 1;361transport->hash_algo = reader.hash_algo;362
363if (reader.line_peeked)364BUG("buffer must be empty at the end of handshake()");365
366return refs;367}
368
369static struct ref *get_refs_via_connect(struct transport *transport, int for_push,370struct transport_ls_refs_options *options)371{
372return handshake(transport, for_push, options, 1);373}
374
375static int get_bundle_uri(struct transport *transport)376{
377struct git_transport_data *data = transport->data;378struct packet_reader reader;379int stateless_rpc = transport->stateless_rpc;380
381if (!transport->bundles) {382CALLOC_ARRAY(transport->bundles, 1);383init_bundle_list(transport->bundles);384}385
386if (!data->finished_handshake) {387struct ref *refs = handshake(transport, 0, NULL, 0);388
389if (refs)390free_refs(refs);391}392
393/*394* "Support" protocol v0 and v2 without bundle-uri support by
395* silently degrading to a NOOP.
396*/
397if (!server_supports_v2("bundle-uri"))398return 0;399
400packet_reader_init(&reader, data->fd[0], NULL, 0,401PACKET_READ_CHOMP_NEWLINE |402PACKET_READ_GENTLE_ON_EOF);403
404return get_remote_bundle_uri(data->fd[1], &reader,405transport->bundles, stateless_rpc);406}
407
408static int fetch_refs_via_pack(struct transport *transport,409int nr_heads, struct ref **to_fetch)410{
411int ret = 0;412struct git_transport_data *data = transport->data;413struct ref *refs = NULL;414struct fetch_pack_args args;415struct ref *refs_tmp = NULL;416
417memset(&args, 0, sizeof(args));418args.uploadpack = data->options.uploadpack;419args.keep_pack = data->options.keep;420args.lock_pack = 1;421args.use_thin_pack = data->options.thin;422args.include_tag = data->options.followtags;423args.verbose = (transport->verbose > 1);424args.quiet = (transport->verbose < 0);425args.no_progress = !transport->progress;426args.depth = data->options.depth;427args.deepen_since = data->options.deepen_since;428args.deepen_not = data->options.deepen_not;429args.deepen_relative = data->options.deepen_relative;430args.check_self_contained_and_connected =431data->options.check_self_contained_and_connected;432args.cloning = transport->cloning;433args.update_shallow = data->options.update_shallow;434args.from_promisor = data->options.from_promisor;435list_objects_filter_copy(&args.filter_options,436&data->options.filter_options);437args.refetch = data->options.refetch;438args.stateless_rpc = transport->stateless_rpc;439args.server_options = transport->server_options;440args.negotiation_tips = data->options.negotiation_tips;441args.reject_shallow_remote = transport->smart_options->reject_shallow;442
443if (!data->finished_handshake) {444int i;445int must_list_refs = 0;446for (i = 0; i < nr_heads; i++) {447if (!to_fetch[i]->exact_oid) {448must_list_refs = 1;449break;450}451}452refs_tmp = handshake(transport, 0, NULL, must_list_refs);453}454
455if (data->version == protocol_unknown_version)456BUG("unknown protocol version");457else if (data->version <= protocol_v1)458die_if_server_options(transport);459
460if (data->options.acked_commits) {461if (data->version < protocol_v2) {462warning(_("--negotiate-only requires protocol v2"));463ret = -1;464} else if (!server_supports_feature("fetch", "wait-for-done", 0)) {465warning(_("server does not support wait-for-done"));466ret = -1;467} else {468negotiate_using_fetch(data->options.negotiation_tips,469transport->server_options,470transport->stateless_rpc,471data->fd,472data->options.acked_commits);473ret = 0;474}475goto cleanup;476}477
478refs = fetch_pack(&args, data->fd,479refs_tmp ? refs_tmp : transport->remote_refs,480to_fetch, nr_heads, &data->shallow,481&transport->pack_lockfiles, data->version);482
483data->finished_handshake = 0;484data->options.self_contained_and_connected =485args.self_contained_and_connected;486data->options.connectivity_checked = args.connectivity_checked;487
488if (!refs)489ret = -1;490if (report_unmatched_refs(to_fetch, nr_heads))491ret = -1;492
493cleanup:494close(data->fd[0]);495if (data->fd[1] >= 0)496close(data->fd[1]);497if (finish_connect(data->conn))498ret = -1;499data->conn = NULL;500
501free_refs(refs_tmp);502free_refs(refs);503list_objects_filter_release(&args.filter_options);504return ret;505}
506
507static int push_had_errors(struct ref *ref)508{
509for (; ref; ref = ref->next) {510switch (ref->status) {511case REF_STATUS_NONE:512case REF_STATUS_UPTODATE:513case REF_STATUS_OK:514break;515default:516return 1;517}518}519return 0;520}
521
522int transport_refs_pushed(struct ref *ref)523{
524for (; ref; ref = ref->next) {525switch(ref->status) {526case REF_STATUS_NONE:527case REF_STATUS_UPTODATE:528break;529default:530return 1;531}532}533return 0;534}
535
536static void update_one_tracking_ref(struct remote *remote, char *refname,537struct object_id *new_oid, int deletion,538int verbose)539{
540struct refspec_item rs;541
542memset(&rs, 0, sizeof(rs));543rs.src = refname;544rs.dst = NULL;545
546if (!remote_find_tracking(remote, &rs)) {547if (verbose)548fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);549if (deletion)550refs_delete_ref(get_main_ref_store(the_repository),551NULL, rs.dst, NULL, 0);552else553refs_update_ref(get_main_ref_store(the_repository),554"update by push", rs.dst, new_oid,555NULL, 0, 0);556free(rs.dst);557}558}
559
560void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)561{
562char *refname;563struct object_id *new_oid;564struct ref_push_report *report;565
566if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)567return;568
569report = ref->report;570if (!report)571update_one_tracking_ref(remote, ref->name, &ref->new_oid,572ref->deletion, verbose);573else574for (; report; report = report->next) {575refname = report->ref_name ? (char *)report->ref_name : ref->name;576new_oid = report->new_oid ? report->new_oid : &ref->new_oid;577update_one_tracking_ref(remote, refname, new_oid,578is_null_oid(new_oid), verbose);579}580}
581
582static void print_ref_status(char flag, const char *summary,583struct ref *to, struct ref *from, const char *msg,584struct ref_push_report *report,585int porcelain, int summary_width)586{
587const char *to_name;588
589if (report && report->ref_name)590to_name = report->ref_name;591else592to_name = to->name;593
594if (porcelain) {595if (from)596fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to_name);597else598fprintf(stdout, "%c\t:%s\t", flag, to_name);599if (msg)600fprintf(stdout, "%s (%s)\n", summary, msg);601else602fprintf(stdout, "%s\n", summary);603} else {604const char *red = "", *reset = "";605if (push_had_errors(to)) {606red = transport_get_color(TRANSPORT_COLOR_REJECTED);607reset = transport_get_color(TRANSPORT_COLOR_RESET);608}609fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width,610summary, reset);611if (from)612fprintf(stderr, "%s -> %s",613prettify_refname(from->name),614prettify_refname(to_name));615else616fputs(prettify_refname(to_name), stderr);617if (msg) {618fputs(" (", stderr);619fputs(msg, stderr);620fputc(')', stderr);621}622fputc('\n', stderr);623}624}
625
626static void print_ok_ref_status(struct ref *ref,627struct ref_push_report *report,628int porcelain, int summary_width)629{
630struct object_id *old_oid;631struct object_id *new_oid;632const char *ref_name;633int forced_update;634
635if (report && report->old_oid)636old_oid = report->old_oid;637else638old_oid = &ref->old_oid;639if (report && report->new_oid)640new_oid = report->new_oid;641else642new_oid = &ref->new_oid;643if (report && report->forced_update)644forced_update = report->forced_update;645else646forced_update = ref->forced_update;647if (report && report->ref_name)648ref_name = report->ref_name;649else650ref_name = ref->name;651
652if (ref->deletion)653print_ref_status('-', "[deleted]", ref, NULL, NULL,654report, porcelain, summary_width);655else if (is_null_oid(old_oid))656print_ref_status('*',657(starts_with(ref_name, "refs/tags/")658? "[new tag]"659: (starts_with(ref_name, "refs/heads/")660? "[new branch]"661: "[new reference]")),662ref, ref->peer_ref, NULL,663report, porcelain, summary_width);664else {665struct strbuf quickref = STRBUF_INIT;666char type;667const char *msg;668
669strbuf_add_unique_abbrev(&quickref, old_oid,670DEFAULT_ABBREV);671if (forced_update) {672strbuf_addstr(&quickref, "...");673type = '+';674msg = "forced update";675} else {676strbuf_addstr(&quickref, "..");677type = ' ';678msg = NULL;679}680strbuf_add_unique_abbrev(&quickref, new_oid,681DEFAULT_ABBREV);682
683print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg,684report, porcelain, summary_width);685strbuf_release(&quickref);686}687}
688
689static int print_one_push_report(struct ref *ref, const char *dest, int count,690struct ref_push_report *report,691int porcelain, int summary_width)692{
693if (!count) {694char *url = transport_anonymize_url(dest);695fprintf(porcelain ? stdout : stderr, "To %s\n", url);696free(url);697}698
699switch(ref->status) {700case REF_STATUS_NONE:701print_ref_status('X', "[no match]", ref, NULL, NULL,702report, porcelain, summary_width);703break;704case REF_STATUS_REJECT_NODELETE:705print_ref_status('!', "[rejected]", ref, NULL,706"remote does not support deleting refs",707report, porcelain, summary_width);708break;709case REF_STATUS_UPTODATE:710print_ref_status('=', "[up to date]", ref,711ref->peer_ref, NULL,712report, porcelain, summary_width);713break;714case REF_STATUS_REJECT_NONFASTFORWARD:715print_ref_status('!', "[rejected]", ref, ref->peer_ref,716"non-fast-forward",717report, porcelain, summary_width);718break;719case REF_STATUS_REJECT_ALREADY_EXISTS:720print_ref_status('!', "[rejected]", ref, ref->peer_ref,721"already exists",722report, porcelain, summary_width);723break;724case REF_STATUS_REJECT_FETCH_FIRST:725print_ref_status('!', "[rejected]", ref, ref->peer_ref,726"fetch first",727report, porcelain, summary_width);728break;729case REF_STATUS_REJECT_NEEDS_FORCE:730print_ref_status('!', "[rejected]", ref, ref->peer_ref,731"needs force",732report, porcelain, summary_width);733break;734case REF_STATUS_REJECT_STALE:735print_ref_status('!', "[rejected]", ref, ref->peer_ref,736"stale info",737report, porcelain, summary_width);738break;739case REF_STATUS_REJECT_REMOTE_UPDATED:740print_ref_status('!', "[rejected]", ref, ref->peer_ref,741"remote ref updated since checkout",742report, porcelain, summary_width);743break;744case REF_STATUS_REJECT_SHALLOW:745print_ref_status('!', "[rejected]", ref, ref->peer_ref,746"new shallow roots not allowed",747report, porcelain, summary_width);748break;749case REF_STATUS_REMOTE_REJECT:750print_ref_status('!', "[remote rejected]", ref,751ref->deletion ? NULL : ref->peer_ref,752ref->remote_status,753report, porcelain, summary_width);754break;755case REF_STATUS_EXPECTING_REPORT:756print_ref_status('!', "[remote failure]", ref,757ref->deletion ? NULL : ref->peer_ref,758"remote failed to report status",759report, porcelain, summary_width);760break;761case REF_STATUS_ATOMIC_PUSH_FAILED:762print_ref_status('!', "[rejected]", ref, ref->peer_ref,763"atomic push failed",764report, porcelain, summary_width);765break;766case REF_STATUS_OK:767print_ok_ref_status(ref, report, porcelain, summary_width);768break;769}770
771return 1;772}
773
774static int print_one_push_status(struct ref *ref, const char *dest, int count,775int porcelain, int summary_width)776{
777struct ref_push_report *report;778int n = 0;779
780if (!ref->report)781return print_one_push_report(ref, dest, count,782NULL, porcelain, summary_width);783
784for (report = ref->report; report; report = report->next)785print_one_push_report(ref, dest, count + n++,786report, porcelain, summary_width);787return n;788}
789
790static int measure_abbrev(const struct object_id *oid, int sofar)791{
792char hex[GIT_MAX_HEXSZ + 1];793int w = repo_find_unique_abbrev_r(the_repository, hex, oid,794DEFAULT_ABBREV);795
796return (w < sofar) ? sofar : w;797}
798
799int transport_summary_width(const struct ref *refs)800{
801int maxw = -1;802
803for (; refs; refs = refs->next) {804maxw = measure_abbrev(&refs->old_oid, maxw);805maxw = measure_abbrev(&refs->new_oid, maxw);806}807if (maxw < 0)808maxw = FALLBACK_DEFAULT_ABBREV;809return (2 * maxw + 3);810}
811
812void transport_print_push_status(const char *dest, struct ref *refs,813int verbose, int porcelain, unsigned int *reject_reasons)814{
815struct ref *ref;816int n = 0;817char *head;818int summary_width = transport_summary_width(refs);819
820if (transport_color_config() < 0)821warning(_("could not parse transport.color.* config"));822
823head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",824RESOLVE_REF_READING, NULL, NULL);825
826if (verbose) {827for (ref = refs; ref; ref = ref->next)828if (ref->status == REF_STATUS_UPTODATE)829n += print_one_push_status(ref, dest, n,830porcelain, summary_width);831}832
833for (ref = refs; ref; ref = ref->next)834if (ref->status == REF_STATUS_OK)835n += print_one_push_status(ref, dest, n,836porcelain, summary_width);837
838*reject_reasons = 0;839for (ref = refs; ref; ref = ref->next) {840if (ref->status != REF_STATUS_NONE &&841ref->status != REF_STATUS_UPTODATE &&842ref->status != REF_STATUS_OK)843n += print_one_push_status(ref, dest, n,844porcelain, summary_width);845if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {846if (head != NULL && !strcmp(head, ref->name))847*reject_reasons |= REJECT_NON_FF_HEAD;848else849*reject_reasons |= REJECT_NON_FF_OTHER;850} else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {851*reject_reasons |= REJECT_ALREADY_EXISTS;852} else if (ref->status == REF_STATUS_REJECT_FETCH_FIRST) {853*reject_reasons |= REJECT_FETCH_FIRST;854} else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {855*reject_reasons |= REJECT_NEEDS_FORCE;856} else if (ref->status == REF_STATUS_REJECT_REMOTE_UPDATED) {857*reject_reasons |= REJECT_REF_NEEDS_UPDATE;858}859}860free(head);861}
862
863static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)864{
865struct git_transport_data *data = transport->data;866struct send_pack_args args;867int ret = 0;868
869if (transport_color_config() < 0)870return -1;871
872if (!data->finished_handshake)873get_refs_via_connect(transport, 1, NULL);874
875memset(&args, 0, sizeof(args));876args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);877args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);878args.use_thin_pack = data->options.thin;879args.verbose = (transport->verbose > 0);880args.quiet = (transport->verbose < 0);881args.progress = transport->progress;882args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);883args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);884args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);885args.push_options = transport->push_options;886args.url = transport->url;887
888if (flags & TRANSPORT_PUSH_CERT_ALWAYS)889args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;890else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED)891args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;892else893args.push_cert = SEND_PACK_PUSH_CERT_NEVER;894
895switch (data->version) {896case protocol_v2:897die(_("support for protocol v2 not implemented yet"));898break;899case protocol_v1:900case protocol_v0:901ret = send_pack(&args, data->fd, data->conn, remote_refs,902&data->extra_have);903break;904case protocol_unknown_version:905BUG("unknown protocol version");906}907
908close(data->fd[1]);909close(data->fd[0]);910/*911* Atomic push may abort the connection early and close the pipe,
912* which may cause an error for `finish_connect()`. Ignore this error
913* for atomic git-push.
914*/
915if (ret || args.atomic)916finish_connect(data->conn);917else918ret = finish_connect(data->conn);919data->conn = NULL;920data->finished_handshake = 0;921
922return ret;923}
924
925static int connect_git(struct transport *transport, const char *name,926const char *executable, int fd[2])927{
928struct git_transport_data *data = transport->data;929data->conn = git_connect(data->fd, transport->url,930name, executable, 0);931fd[0] = data->fd[0];932fd[1] = data->fd[1];933return 0;934}
935
936static int disconnect_git(struct transport *transport)937{
938struct git_transport_data *data = transport->data;939if (data->conn) {940if (data->finished_handshake && !transport->stateless_rpc)941packet_flush(data->fd[1]);942close(data->fd[0]);943if (data->fd[1] >= 0)944close(data->fd[1]);945finish_connect(data->conn);946}947
948list_objects_filter_release(&data->options.filter_options);949free(data);950return 0;951}
952
953static struct transport_vtable taken_over_vtable = {954.get_refs_list = get_refs_via_connect,955.get_bundle_uri = get_bundle_uri,956.fetch_refs = fetch_refs_via_pack,957.push_refs = git_transport_push,958.disconnect = disconnect_git959};960
961void transport_take_over(struct transport *transport,962struct child_process *child)963{
964struct git_transport_data *data;965
966if (!transport->smart_options)967BUG("taking over transport requires non-NULL "968"smart_options field.");969
970CALLOC_ARRAY(data, 1);971data->options = *transport->smart_options;972data->conn = child;973data->fd[0] = data->conn->out;974data->fd[1] = data->conn->in;975data->finished_handshake = 0;976transport->data = data;977
978transport->vtable = &taken_over_vtable;979transport->smart_options = &(data->options);980
981transport->cannot_reuse = 1;982}
983
984static int is_file(const char *url)985{
986struct stat buf;987if (stat(url, &buf))988return 0;989return S_ISREG(buf.st_mode);990}
991
992static int external_specification_len(const char *url)993{
994return strchr(url, ':') - url;995}
996
997static const struct string_list *protocol_allow_list(void)998{
999static int enabled = -1;1000static struct string_list allowed = STRING_LIST_INIT_DUP;1001
1002if (enabled < 0) {1003const char *v = getenv("GIT_ALLOW_PROTOCOL");1004if (v) {1005string_list_split(&allowed, v, ':', -1);1006string_list_sort(&allowed);1007enabled = 1;1008} else {1009enabled = 0;1010}1011}1012
1013return enabled ? &allowed : NULL;1014}
1015
1016enum protocol_allow_config {1017PROTOCOL_ALLOW_NEVER = 0,1018PROTOCOL_ALLOW_USER_ONLY,1019PROTOCOL_ALLOW_ALWAYS
1020};1021
1022static enum protocol_allow_config parse_protocol_config(const char *key,1023const char *value)1024{
1025if (!strcasecmp(value, "always"))1026return PROTOCOL_ALLOW_ALWAYS;1027else if (!strcasecmp(value, "never"))1028return PROTOCOL_ALLOW_NEVER;1029else if (!strcasecmp(value, "user"))1030return PROTOCOL_ALLOW_USER_ONLY;1031
1032die(_("unknown value for config '%s': %s"), key, value);1033}
1034
1035static enum protocol_allow_config get_protocol_config(const char *type)1036{
1037char *key = xstrfmt("protocol.%s.allow", type);1038char *value;1039
1040/* first check the per-protocol config */1041if (!git_config_get_string(key, &value)) {1042enum protocol_allow_config ret =1043parse_protocol_config(key, value);1044free(key);1045free(value);1046return ret;1047}1048free(key);1049
1050/* if defined, fallback to user-defined default for unknown protocols */1051if (!git_config_get_string("protocol.allow", &value)) {1052enum protocol_allow_config ret =1053parse_protocol_config("protocol.allow", value);1054free(value);1055return ret;1056}1057
1058/* fallback to built-in defaults */1059/* known safe */1060if (!strcmp(type, "http") ||1061!strcmp(type, "https") ||1062!strcmp(type, "git") ||1063!strcmp(type, "ssh"))1064return PROTOCOL_ALLOW_ALWAYS;1065
1066/* known scary; err on the side of caution */1067if (!strcmp(type, "ext"))1068return PROTOCOL_ALLOW_NEVER;1069
1070/* unknown; by default let them be used only directly by the user */1071return PROTOCOL_ALLOW_USER_ONLY;1072}
1073
1074int is_transport_allowed(const char *type, int from_user)1075{
1076const struct string_list *allow_list = protocol_allow_list();1077if (allow_list)1078return string_list_has_string(allow_list, type);1079
1080switch (get_protocol_config(type)) {1081case PROTOCOL_ALLOW_ALWAYS:1082return 1;1083case PROTOCOL_ALLOW_NEVER:1084return 0;1085case PROTOCOL_ALLOW_USER_ONLY:1086if (from_user < 0)1087from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1);1088return from_user;1089}1090
1091BUG("invalid protocol_allow_config type");1092}
1093
1094void transport_check_allowed(const char *type)1095{
1096if (!is_transport_allowed(type, -1))1097die(_("transport '%s' not allowed"), type);1098}
1099
1100static struct transport_vtable bundle_vtable = {1101.get_refs_list = get_refs_from_bundle,1102.fetch_refs = fetch_refs_from_bundle,1103.disconnect = close_bundle1104};1105
1106static struct transport_vtable builtin_smart_vtable = {1107.get_refs_list = get_refs_via_connect,1108.get_bundle_uri = get_bundle_uri,1109.fetch_refs = fetch_refs_via_pack,1110.push_refs = git_transport_push,1111.connect = connect_git,1112.disconnect = disconnect_git1113};1114
1115struct transport *transport_get(struct remote *remote, const char *url)1116{
1117const char *helper;1118char *helper_to_free = NULL;1119const char *p;1120struct transport *ret = xcalloc(1, sizeof(*ret));1121
1122ret->progress = isatty(2);1123string_list_init_dup(&ret->pack_lockfiles);1124
1125CALLOC_ARRAY(ret->bundles, 1);1126init_bundle_list(ret->bundles);1127
1128if (!remote)1129BUG("No remote provided to transport_get()");1130
1131ret->got_remote_refs = 0;1132ret->remote = remote;1133helper = remote->foreign_vcs;1134
1135if (!url)1136url = remote->url.v[0];1137ret->url = url;1138
1139p = url;1140while (is_urlschemechar(p == url, *p))1141p++;1142if (starts_with(p, "::"))1143helper = helper_to_free = xstrndup(url, p - url);1144
1145if (helper) {1146transport_helper_init(ret, helper);1147free(helper_to_free);1148} else if (starts_with(url, "rsync:")) {1149die(_("git-over-rsync is no longer supported"));1150} else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {1151struct bundle_transport_data *data = xcalloc(1, sizeof(*data));1152bundle_header_init(&data->header);1153transport_check_allowed("file");1154ret->data = data;1155ret->vtable = &bundle_vtable;1156ret->smart_options = NULL;1157} else if (!is_url(url)1158|| starts_with(url, "file://")1159|| starts_with(url, "git://")1160|| starts_with(url, "ssh://")1161|| starts_with(url, "git+ssh://") /* deprecated - do not use */1162|| starts_with(url, "ssh+git://") /* deprecated - do not use */1163) {1164/*1165* These are builtin smart transports; "allowed" transports
1166* will be checked individually in git_connect.
1167*/
1168struct git_transport_data *data = xcalloc(1, sizeof(*data));1169list_objects_filter_init(&data->options.filter_options);1170ret->data = data;1171ret->vtable = &builtin_smart_vtable;1172ret->smart_options = &(data->options);1173
1174data->conn = NULL;1175data->finished_handshake = 0;1176} else {1177/* Unknown protocol in URL. Pass to external handler. */1178int len = external_specification_len(url);1179char *handler = xmemdupz(url, len);1180transport_helper_init(ret, handler);1181free(handler);1182}1183
1184if (ret->smart_options) {1185ret->smart_options->thin = 1;1186ret->smart_options->uploadpack = "git-upload-pack";1187if (remote->uploadpack)1188ret->smart_options->uploadpack = remote->uploadpack;1189ret->smart_options->receivepack = "git-receive-pack";1190if (remote->receivepack)1191ret->smart_options->receivepack = remote->receivepack;1192}1193
1194ret->hash_algo = &hash_algos[GIT_HASH_SHA1];1195
1196return ret;1197}
1198
1199const struct git_hash_algo *transport_get_hash_algo(struct transport *transport)1200{
1201return transport->hash_algo;1202}
1203
1204int transport_set_option(struct transport *transport,1205const char *name, const char *value)1206{
1207int git_reports = 1, protocol_reports = 1;1208
1209if (transport->smart_options)1210git_reports = set_git_option(transport->smart_options,1211name, value);1212
1213if (transport->vtable->set_option)1214protocol_reports = transport->vtable->set_option(transport,1215name, value);1216
1217/* If either report is 0, report 0 (success). */1218if (!git_reports || !protocol_reports)1219return 0;1220/* If either reports -1 (invalid value), report -1. */1221if ((git_reports == -1) || (protocol_reports == -1))1222return -1;1223/* Otherwise if both report unknown, report unknown. */1224return 1;1225}
1226
1227void transport_set_verbosity(struct transport *transport, int verbosity,1228int force_progress)1229{
1230if (verbosity >= 1)1231transport->verbose = verbosity <= 3 ? verbosity : 3;1232if (verbosity < 0)1233transport->verbose = -1;1234
1235/**1236* Rules used to determine whether to report progress (processing aborts
1237* when a rule is satisfied):
1238*
1239* . Report progress, if force_progress is 1 (ie. --progress).
1240* . Don't report progress, if force_progress is 0 (ie. --no-progress).
1241* . Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
1242* . Report progress if isatty(2) is 1.
1243**/
1244if (force_progress >= 0)1245transport->progress = !!force_progress;1246else1247transport->progress = verbosity >= 0 && isatty(2);1248}
1249
1250static void die_with_unpushed_submodules(struct string_list *needs_pushing)1251{
1252int i;1253
1254fprintf(stderr, _("The following submodule paths contain changes that can\n"1255"not be found on any remote:\n"));1256for (i = 0; i < needs_pushing->nr; i++)1257fprintf(stderr, " %s\n", needs_pushing->items[i].string);1258fprintf(stderr, _("\nPlease try\n\n"1259" git push --recurse-submodules=on-demand\n\n"1260"or cd to the path and use\n\n"1261" git push\n\n"1262"to push them to a remote.\n\n"));1263
1264string_list_clear(needs_pushing, 0);1265
1266die(_("Aborting."));1267}
1268
1269static int run_pre_push_hook(struct transport *transport,1270struct ref *remote_refs)1271{
1272int ret = 0, x;1273struct ref *r;1274struct child_process proc = CHILD_PROCESS_INIT;1275struct strbuf buf;1276const char *hook_path = find_hook(the_repository, "pre-push");1277
1278if (!hook_path)1279return 0;1280
1281strvec_push(&proc.args, hook_path);1282strvec_push(&proc.args, transport->remote->name);1283strvec_push(&proc.args, transport->url);1284
1285proc.in = -1;1286proc.trace2_hook_name = "pre-push";1287
1288if (start_command(&proc)) {1289finish_command(&proc);1290return -1;1291}1292
1293sigchain_push(SIGPIPE, SIG_IGN);1294
1295strbuf_init(&buf, 256);1296
1297for (r = remote_refs; r; r = r->next) {1298if (!r->peer_ref) continue;1299if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;1300if (r->status == REF_STATUS_REJECT_STALE) continue;1301if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue;1302if (r->status == REF_STATUS_UPTODATE) continue;1303
1304strbuf_reset(&buf);1305strbuf_addf( &buf, "%s %s %s %s\n",1306r->peer_ref->name, oid_to_hex(&r->new_oid),1307r->name, oid_to_hex(&r->old_oid));1308
1309if (write_in_full(proc.in, buf.buf, buf.len) < 0) {1310/* We do not mind if a hook does not read all refs. */1311if (errno != EPIPE)1312ret = -1;1313break;1314}1315}1316
1317strbuf_release(&buf);1318
1319x = close(proc.in);1320if (!ret)1321ret = x;1322
1323sigchain_pop(SIGPIPE);1324
1325x = finish_command(&proc);1326if (!ret)1327ret = x;1328
1329return ret;1330}
1331
1332int transport_push(struct repository *r,1333struct transport *transport,1334struct refspec *rs, int flags,1335unsigned int *reject_reasons)1336{
1337struct ref *remote_refs = NULL;1338struct ref *local_refs = NULL;1339int match_flags = MATCH_REFS_NONE;1340int verbose = (transport->verbose > 0);1341int quiet = (transport->verbose < 0);1342int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;1343int pretend = flags & TRANSPORT_PUSH_DRY_RUN;1344int push_ret, err;1345int ret = -1;1346struct transport_ls_refs_options transport_options =1347TRANSPORT_LS_REFS_OPTIONS_INIT;1348
1349*reject_reasons = 0;1350
1351if (transport_color_config() < 0)1352goto done;1353
1354if (!transport->vtable->push_refs)1355goto done;1356
1357local_refs = get_local_heads();1358
1359if (check_push_refs(local_refs, rs) < 0)1360goto done;1361
1362refspec_ref_prefixes(rs, &transport_options.ref_prefixes);1363
1364trace2_region_enter("transport_push", "get_refs_list", r);1365remote_refs = transport->vtable->get_refs_list(transport, 1,1366&transport_options);1367trace2_region_leave("transport_push", "get_refs_list", r);1368
1369transport_ls_refs_options_release(&transport_options);1370
1371if (flags & TRANSPORT_PUSH_ALL)1372match_flags |= MATCH_REFS_ALL;1373if (flags & TRANSPORT_PUSH_MIRROR)1374match_flags |= MATCH_REFS_MIRROR;1375if (flags & TRANSPORT_PUSH_PRUNE)1376match_flags |= MATCH_REFS_PRUNE;1377if (flags & TRANSPORT_PUSH_FOLLOW_TAGS)1378match_flags |= MATCH_REFS_FOLLOW_TAGS;1379
1380if (match_push_refs(local_refs, &remote_refs, rs, match_flags))1381goto done;1382
1383if (transport->smart_options &&1384transport->smart_options->cas &&1385!is_empty_cas(transport->smart_options->cas))1386apply_push_cas(transport->smart_options->cas,1387transport->remote, remote_refs);1388
1389set_ref_status_for_push(remote_refs,1390flags & TRANSPORT_PUSH_MIRROR,1391flags & TRANSPORT_PUSH_FORCE);1392
1393if (!(flags & TRANSPORT_PUSH_NO_HOOK))1394if (run_pre_push_hook(transport, remote_refs))1395goto done;1396
1397if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |1398TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&1399!is_bare_repository()) {1400struct ref *ref = remote_refs;1401struct oid_array commits = OID_ARRAY_INIT;1402
1403trace2_region_enter("transport_push", "push_submodules", r);1404for (; ref; ref = ref->next)1405if (!is_null_oid(&ref->new_oid))1406oid_array_append(&commits,1407&ref->new_oid);1408
1409if (!push_unpushed_submodules(r,1410&commits,1411transport->remote,1412rs,1413transport->push_options,1414pretend)) {1415oid_array_clear(&commits);1416trace2_region_leave("transport_push", "push_submodules", r);1417die(_("failed to push all needed submodules"));1418}1419oid_array_clear(&commits);1420trace2_region_leave("transport_push", "push_submodules", r);1421}1422
1423if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||1424((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |1425TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&1426!pretend)) && !is_bare_repository()) {1427struct ref *ref = remote_refs;1428struct string_list needs_pushing = STRING_LIST_INIT_DUP;1429struct oid_array commits = OID_ARRAY_INIT;1430
1431trace2_region_enter("transport_push", "check_submodules", r);1432for (; ref; ref = ref->next)1433if (!is_null_oid(&ref->new_oid))1434oid_array_append(&commits,1435&ref->new_oid);1436
1437if (find_unpushed_submodules(r,1438&commits,1439transport->remote->name,1440&needs_pushing)) {1441oid_array_clear(&commits);1442trace2_region_leave("transport_push", "check_submodules", r);1443die_with_unpushed_submodules(&needs_pushing);1444}1445string_list_clear(&needs_pushing, 0);1446oid_array_clear(&commits);1447trace2_region_leave("transport_push", "check_submodules", r);1448}1449
1450if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY)) {1451trace2_region_enter("transport_push", "push_refs", r);1452push_ret = transport->vtable->push_refs(transport, remote_refs, flags);1453trace2_region_leave("transport_push", "push_refs", r);1454} else1455push_ret = 0;1456err = push_had_errors(remote_refs);1457ret = push_ret | err;1458
1459if (!quiet || err)1460transport_print_push_status(transport->url, remote_refs,1461verbose | porcelain, porcelain,1462reject_reasons);1463
1464if (flags & TRANSPORT_PUSH_SET_UPSTREAM)1465set_upstreams(transport, remote_refs, pretend);1466
1467if (!(flags & (TRANSPORT_PUSH_DRY_RUN |1468TRANSPORT_RECURSE_SUBMODULES_ONLY))) {1469struct ref *ref;1470for (ref = remote_refs; ref; ref = ref->next)1471transport_update_tracking_ref(transport->remote, ref, verbose);1472}1473
1474if (porcelain && !push_ret)1475puts("Done");1476else if (!quiet && !ret && !transport_refs_pushed(remote_refs))1477/* stable plumbing output; do not modify or localize */1478fprintf(stderr, "Everything up-to-date\n");1479
1480done:1481free_refs(local_refs);1482free_refs(remote_refs);1483return ret;1484}
1485
1486const struct ref *transport_get_remote_refs(struct transport *transport,1487struct transport_ls_refs_options *transport_options)1488{
1489if (!transport->got_remote_refs) {1490transport->remote_refs =1491transport->vtable->get_refs_list(transport, 0,1492transport_options);1493transport->got_remote_refs = 1;1494}1495
1496return transport->remote_refs;1497}
1498
1499void transport_ls_refs_options_release(struct transport_ls_refs_options *opts)1500{
1501strvec_clear(&opts->ref_prefixes);1502free((char *)opts->unborn_head_target);1503}
1504
1505int transport_fetch_refs(struct transport *transport, struct ref *refs)1506{
1507int rc;1508int nr_heads = 0, nr_alloc = 0, nr_refs = 0;1509struct ref **heads = NULL;1510struct ref *rm;1511
1512for (rm = refs; rm; rm = rm->next) {1513nr_refs++;1514if (rm->peer_ref &&1515!is_null_oid(&rm->old_oid) &&1516oideq(&rm->peer_ref->old_oid, &rm->old_oid))1517continue;1518ALLOC_GROW(heads, nr_heads + 1, nr_alloc);1519heads[nr_heads++] = rm;1520}1521
1522if (!nr_heads) {1523/*1524* When deepening of a shallow repository is requested,
1525* then local and remote refs are likely to still be equal.
1526* Just feed them all to the fetch method in that case.
1527* This condition shouldn't be met in a non-deepening fetch
1528* (see builtin/fetch.c:quickfetch()).
1529*/
1530ALLOC_ARRAY(heads, nr_refs);1531for (rm = refs; rm; rm = rm->next)1532heads[nr_heads++] = rm;1533}1534
1535rc = transport->vtable->fetch_refs(transport, nr_heads, heads);1536
1537free(heads);1538return rc;1539}
1540
1541int transport_get_remote_bundle_uri(struct transport *transport)1542{
1543int value = 0;1544const struct transport_vtable *vtable = transport->vtable;1545
1546/* Check config only once. */1547if (transport->got_remote_bundle_uri)1548return 0;1549transport->got_remote_bundle_uri = 1;1550
1551/*1552* Don't request bundle-uri from the server unless configured to
1553* do so by the transfer.bundleURI=true config option.
1554*/
1555if (git_config_get_bool("transfer.bundleuri", &value) || !value)1556return 0;1557
1558if (!transport->bundles->baseURI)1559transport->bundles->baseURI = xstrdup(transport->url);1560
1561if (!vtable->get_bundle_uri)1562return error(_("bundle-uri operation not supported by protocol"));1563
1564if (vtable->get_bundle_uri(transport) < 0)1565return error(_("could not retrieve server-advertised bundle-uri list"));1566return 0;1567}
1568
1569void transport_unlock_pack(struct transport *transport, unsigned int flags)1570{
1571int in_signal_handler = !!(flags & TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);1572int i;1573
1574for (i = 0; i < transport->pack_lockfiles.nr; i++)1575if (in_signal_handler)1576unlink(transport->pack_lockfiles.items[i].string);1577else1578unlink_or_warn(transport->pack_lockfiles.items[i].string);1579if (!in_signal_handler)1580string_list_clear(&transport->pack_lockfiles, 0);1581}
1582
1583int transport_connect(struct transport *transport, const char *name,1584const char *exec, int fd[2])1585{
1586if (transport->vtable->connect)1587return transport->vtable->connect(transport, name, exec, fd);1588else1589die(_("operation not supported by protocol"));1590}
1591
1592int transport_disconnect(struct transport *transport)1593{
1594int ret = 0;1595if (transport->vtable->disconnect)1596ret = transport->vtable->disconnect(transport);1597if (transport->got_remote_refs)1598free_refs((void *)transport->remote_refs);1599clear_bundle_list(transport->bundles);1600free(transport->bundles);1601free(transport);1602return ret;1603}
1604
1605/*
1606* Strip username (and password) from a URL and return
1607* it in a newly allocated string.
1608*/
1609char *transport_anonymize_url(const char *url)1610{
1611char *scheme_prefix, *anon_part;1612size_t anon_len, prefix_len = 0;1613
1614anon_part = strchr(url, '@');1615if (url_is_local_not_ssh(url) || !anon_part)1616goto literal_copy;1617
1618anon_len = strlen(++anon_part);1619scheme_prefix = strstr(url, "://");1620if (!scheme_prefix) {1621if (!strchr(anon_part, ':'))1622/* cannot be "me@there:/path/name" */1623goto literal_copy;1624} else {1625const char *cp;1626/* make sure scheme is reasonable */1627for (cp = url; cp < scheme_prefix; cp++) {1628switch (*cp) {1629/* RFC 1738 2.1 */1630case '+': case '.': case '-':1631break; /* ok */1632default:1633if (isalnum(*cp))1634break;1635/* it isn't */1636goto literal_copy;1637}1638}1639/* @ past the first slash does not count */1640cp = strchr(scheme_prefix + 3, '/');1641if (cp && cp < anon_part)1642goto literal_copy;1643prefix_len = scheme_prefix - url + 3;1644}1645return xstrfmt("%.*s%.*s", (int)prefix_len, url,1646(int)anon_len, anon_part);1647literal_copy:1648return xstrdup(url);1649}
1650