/* * Copyright 1995, 2000 Perforce Software. All rights reserved. * * This file is part of Perforce - the FAST SCM System. */ /* * clientinit() - Perforce client DVCS init * */ # define NEED_CHDIR # define NEED_TYPES # define NEED_STAT # define NEED_FLOCK # include # include # include # include # include # include # include # include # include # include # include "client.h" # include "clientuser.h" # include "serverhelper.h" static ErrorId cloneHelp = { ErrorOf( 0, 0, E_INFO, 0, 0), "\n" " clone -- Clone a new personal server from a shared server\n" "\n" " p4 [-u user][-d dir][-c client] clone [-m depth -v -p port] -r remote\n" " p4 [-u user][-d dir][-c client] clone [-m depth -v -p port] [-f] file\n" "\n" " This command initializes a new Perforce repository in the current\n" " directory (or the directory specified by the -d flag).\n" "\n" " In order to run 'p4 clone', you must have up-to-date and matching\n" " versions of the 'p4' and 'p4d' executables in your operating system\n" " path. You can download these executables from http://www.perforce.com\n" "\n" " Perforce database files will be stored in the directory named\n" " \".p4root\". Perforce configuration settings will be stored in\n" " P4CONFIG and P4IGNORE files in the top level of your directory.\n" " It is not necessary to view or update these files, but you should\n" " be aware that they exist.\n" "\n" " The -m flag performs a shallow fetch; only the last number of\n" " specified revisions of each file are fetched.\n" "\n" " The -p flag specifies the address of the target server you wish\n" " to clone from, overriding the value of $P4PORT in the environment\n" " and the default (perforce:1666).\n" "\n" " The -r flag specifies a remote spec installed on the remote server\n" " to use as a template for the clone and stream setup. See 'p4 help\n" " remote' for more information about how the clone command interprets\n" " the remote spec during the setup steps.\n" "\n" " The -f flag specifies a file pattern in the remote server to use as\n" " the path to clone; this file specification will also be used to\n" " determine the stream setup in the local server.\n" "\n" " The -v flag specifies verbose mode.\n" "\n" " When the clone completes, a default remote spec will be created,\n" " called origin. To update your local repository with the latest\n" " changes from the target server, run 'p4 fetch'.\n" "\n" " For more information about using Perforce, run 'p4 help' after you\n" " have run 'p4 clone', or visit http://www.perforce.com.\n" }; static ErrorId initHelp = { ErrorOf( 0, 0, E_INFO, 0, 0), "\n" " init -- Initialize a new personal server.\n" "\n" " p4 [-u user][-d dir][-c client] init [-hq][-c stream][-p port]\n" " p4 [-u user][-d dir][-c client] init [-hq][-c stream][-Cx -xi -n]\n" "\n" " This command initializes a new Perforce repository in the current\n" " directory (or the directory specified by the -d flag).\n" "\n" " In order to run 'p4 init', you must have up-to-date and matching\n" " versions of the 'p4' and 'p4d' executables in your operating system\n" " path. You can download these executables from http://www.perforce.com\n" "\n" " Perforce database files will be stored in the directory named\n" " \".p4root\". Perforce configuration settings will be stored in\n" " P4CONFIG and P4IGNORE files in the top level of your directory.\n" " It is not necessary to view or update these files, but you should\n" " be aware that they exist.\n" "\n" " After initializing your new repository, run 'p4 reconcile' to mark\n" " all of your source files to be added to Perforce, then 'p4 submit'\n" " to submit them.\n" "\n" " The -c flag configures the installation to create the specified stream\n" " as the mainline stream rather than the default '//stream/main'.\n" "\n" " The -p flag specifies the address of a target server you wish to\n" " discover the case sensitivity and unicode settings from. This will\n" " make your local repository compatible with that server.\n" "\n" " The -q flag suppresses informational messages (including if\n" " p4 init has already been run.)\n" "\n" " The -Cx flag sets the case sensitivity of the new Perforce\n" " installation. You may specify either -C0 or -C1. The -C0 flag\n" " specifies case-sensitive operation; the -C1 flag specifies\n" " case-insensitive operation. (See discovery note below).\n" "\n" " The -n flag configures the installation without unicode support.\n" " The -xi flag configures the installation with unicode support.\n" " Without -n or -xi, the installation will decide on unicode support\n" " based on the P4CHARSET enviroment being set.\n" "\n" " Note: If neither -Cx, -xi or -n flags are set, init will try and\n" " discover the correct settings from a server already set in your\n" " environment. If init cannot find a server then it will fail\n" " initialization.\n" "\n" " For more information about using Perforce, run 'p4 help' after you\n" " have run 'p4 init', or visit http://www.perforce.com.\n" }; static ErrorId initUsage = { ErrorOf( 0, 0, E_FAILED, 0, 0), "Usage: p4 [-u user][-d dir][-c client] init [-hq][-c stream][-p port | -Cx -xi -n]\n" "\tUse p4 init -h for detailed help." }; static ErrorId cloneUsage = { ErrorOf( 0, 0, E_FAILED, 0, 0), "Usage: p4 [-u user][-d dir][-c client] clone [-p ] [-m depth -v][-r | [-f] filespec]\n" "\tUse p4 clone -h for detailed help." }; static ErrorId initUnicodeMixup = { ErrorOf( 0, 0, E_FAILED, 0, 0), "p4 init does not allow '-n' and '-xi' to be used together" }; static ErrorId initCaseFlagMixup = { ErrorOf( 0, 0, E_FAILED, 0, 0), "Specify either -C0 or -C1." }; static ErrorId initServerFail = { ErrorOf( 0, 0, E_FAILED, 0, 0), "\np4d server failed to initialize. A 2015.1 or later p4d server\n" "must be in your path and runable." }; static ErrorId pasiveDiscoverFail = { ErrorOf( 0, 0, E_FAILED, 0, 0 ), "No available server to discover configuration, needs flags to\n" "specify case sensitivity and Unicode settings:\n" "p4 init [-C0|-C1] (case-sensitive or case-insensitive)\n" "p4 init [-xi|-n] (with Unicode support or without it)\n" "Note, it is important to initialize with the same case sensitivity\n" "and Unicode settings as the server you wish to push/fetch from.\n" "For more information, run: 'p4 help init'." }; static ErrorId discoverSucceeded = { ErrorOf( 0, 0, E_INFO, 0, 3 ), "Matching server configuration from '%port%':\n" "%case%, %unicode%" }; static ErrorId discoverUnknownUser = { ErrorOf( 0, 0, E_WARN, 0, 1 ), "warning: your user '%user%' unknown at this server!" }; static void SetProgAndVersion( ServerHelper *clone ) { StrBuf v; v << ID_REL "/" << ID_OS << "/" << ID_PATCH; clone->SetVersion( v.Text() ); clone->SetProg( "p4" ); } int clientInitHelp( int doClone, Error *e ) { ClientUser cuser; e->Set( doClone ? cloneHelp : initHelp ); cuser.Message( e ); e->Clear(); return 0; } int clientInitInit( int ac, char **av, Options &preops, Error *e ); int clientInitClone( int ac, char **av, Options &preops, Error *e ); int clientInit( int ac, char **av, Options &preops, int doClone, Error *e ) { if( doClone ) return clientInitClone( ac, av, preops, e ); else return clientInitInit( ac, av, preops, e ); } int clientInitInit( int ac, char **av, Options &preops, Error *e ) { Options opts; ClientUserProgress cuser; StrPtr *sp; StrPtr *port = 0; int unicode = -1; StrPtr *caseflag = 0; int ecode = 0; Error e1; int longOpts[] = { Options::Help, Options::Quiet, 0 }; opts.ParseLong( ac, av, "hp:c:nqC#x:", longOpts, OPT_NONE, initUsage, e ); if( e->Test() ) return 1; if( opts[ 'h' ] ) return clientInitHelp( 0, e ); if( opts['p'] && ( opts['x'] || opts['n'] || opts['C'] ) ) e->Set( initUsage ); if( e->Test() ) return 1; ServerHelper ruser( e ); if( e->Test() ) return 1; SetProgAndVersion( &ruser ); // Set the DVCS directory if( ( sp = preops['d'] ) ) ruser.SetDvcsDir( sp ); if( ( sp = preops['v'] ) ) ruser.DoDebug( sp ); // turn-on quiet mode if( opts['q'] ) ruser.SetQuiet(); // Set the port if( ( sp = opts['p'] ) || ( sp = preops['p'] )) port = sp; // Was case sensitivity specified on the commandline? if( ( caseflag = opts['C'] ) ) { // Validate it now (it'll be overriden by the Discover()) ruser.SetCaseFlag( caseflag, e ); if( e->Test() ) { e->Clear(); e->Set( initCaseFlagMixup ); } } // Set the default stream if( !e->Test() && ( sp = opts['c'] ) ) ruser.SetDefaultStream( sp, e ); if( e->Test() ) goto finish; // Was unicode specified on the commandline? if( ( sp = opts[ 'x' ] ) && sp->Text()[0] == 'i' ) { unicode = 1; if( opts[ 'n' ] ) { e->Set( initUnicodeMixup ); goto finish; } } if( opts[ 'n' ] ) unicode = 0; // Set the username and client // If null pointers are passed, the values come from the envrionment ruser.SetUserClient( preops['u'], preops['c'] ); // check if already initialized // P4INITROOT and .p4root dir if( ruser.Exists( &cuser, &e1 ) || ruser.GotError() ) { ecode = 1; goto finish; } // If there're any gaps in our configuration, query the server to // fill in the gaps if( !( ( opts['x'] || opts['n'] ) && opts['C'] ) ) ruser.Discover( port, &cuser, &e1 ); if( unicode >= 0 ) ruser.SetUnicode( unicode ); if( caseflag ) ruser.SetCaseFlag( caseflag, e ); if( !( ( opts['x'] || opts['n'] ) && opts['C'] ) ) { if( ruser.GotError() ) { if( !opts['p'] ) { // passive discovery failed, require options ruser.ClearError(); e->Clear(); e->Set( pasiveDiscoverFail ); } ecode = 1; goto finish; } else if( !e->Test() ) { e->Set( discoverSucceeded ); *e << ruser.Server(); if( ruser.GetCaseFlag() == "-C0" ) *e << "case-sensitive (-C0)"; else *e << "case-insensitive (-C1)"; if( ruser.Unicode() ) *e << "unicode (-xi)"; else *e << "non-unicode (-n)"; cuser.Message( e ); e->Clear(); if( ruser.UserName() == "*unknown*" ) { e->Set( discoverUnknownUser ) << ruser.GetUser(); cuser.Message( e ); e->Clear(); } } } else ruser.ClearError(); // Error messages flush before printing, but info's do not // flush now so that the message order is correct. fflush( stderr ); // Make it happen... ecode = ruser.InitLocalServer( &cuser, &e1 ); finish: if( e->Test() ) { cuser.Message( e ); ecode = 1; e->Clear(); } else if( ruser.GotError() ) cuser.Message( ruser.GetError() ); return ecode; } int clientInitClone( int ac, char **av, Options &preops, Error *e ) { Options opts; ClientUserProgress cuser; StrPtr *sp; StrPtr *port = 0; int depth = 0; int ecode = 0; Error e1; int longOpts[] = { Options::Help, Options::Quiet, 0 }; opts.ParseLong( ac, av, "Ahp:r:f:m#v", longOpts, OPT_OPT, cloneUsage, e ); if( e->Test() ) return 1; if( opts[ 'h' ] ) return clientInitHelp( 1, e ); StrPtr *remoteSpec = opts[ 'r' ]; StrPtr *fileSpec = opts[ 'f' ]; StrBuf filePattern; if( ( fileSpec || remoteSpec ) && ac ) { e->Set( cloneUsage ); return 1; } if( !fileSpec && ac ) { filePattern.Set( av[0] ); fileSpec = &filePattern; } if( ( remoteSpec && fileSpec ) || ( !remoteSpec && !fileSpec ) ) e->Set( cloneUsage ); if( e->Test() ) return 1; ServerHelper ruser( e ); if( e->Test() ) return 1; SetProgAndVersion( &ruser ); // Set the DVCS directory if( ( sp = preops['d'] ) ) ruser.SetDvcsDir( sp ); // Set the P4PASSWD if( ( sp = preops[ 'P' ] ) ) ruser.SetPassword( sp ); // turn-off quiet mode if( ( sp = preops['v'] ) ) ruser.DoDebug( sp ); // Set the port if( ( sp = opts['p'] ) || ( sp = preops['p'] ) ) port = sp; // Set the clone depth if( ( sp = opts['m'] ) ) depth = sp->Atoi(); // Set the username and client // If null pointers are passed, the values come from the envrionment ruser.SetUserClient( preops['u'], preops['c'] ); // check if already initialized // P4INITROOT and .p4root dir if( ruser.Exists( &cuser, &e1 ) || ruser.GotError() ) { ecode = 1; goto finish; } // Get the remote, or build our own if( remoteSpec ) ruser.LoadRemote( port, remoteSpec, &cuser, &e1 ); if( fileSpec ) ruser.MakeRemote( port, fileSpec, &cuser, &e1 ); if( ruser.GotError() ) { ecode = 1; goto finish; } // Make it happen... if( !( ecode = ruser.InitLocalServer( &cuser, &e1 ) ) ) { if( !e->Test() ) ruser.FirstFetch( depth, opts[ 'A' ] ? 1 : 0, opts['v'], &cuser, &e1 ); } finish: if( e->Test() ) { cuser.Message( e ); ecode = 1; e->Clear(); } else if( ruser.GotError() ) cuser.Message( ruser.GetError() ); return ecode; }