linkgit:git-clone[1]), deepen or shorten the history to the specified
number of commits. Tags for the deepened commits are not fetched.
+ --deepen=<depth>::
+ Similar to --depth, except it specifies the number of commits
+ from the current shallow boundary instead of from the tip of
+ each remote branch history.
+
+ --shallow-since=<date>::
+ Deepen or shorten the history of a shallow repository to
+ include all reachable commits after <date>.
+
+ --shallow-exclude=<revision>::
+ Deepen or shorten the history of a shallow repository to
+ exclude commits reachable from a specified remote branch or tag.
+ This option can be specified multiple times.
+
--unshallow::
If the source repository is complete, convert a shallow
repository to a complete one, removing all the limitations
-p::
--prune::
- After fetching, remove any remote-tracking references that no
+ Before fetching, remove any remote-tracking references that no
longer exist on the remote. Tags are not subject to pruning
if they are fetched only because of the default tag
auto-following or due to a --tags option. However, if tags
to whatever else would otherwise be fetched. Using this
option alone does not subject tags to pruning, even if --prune
is used (though tags may be pruned anyway if they are also the
- destination of an explicit refspec; see '--prune').
+ destination of an explicit refspec; see `--prune`).
--recurse-submodules[=yes|on-demand|no]::
This option controls if and under what conditions new commits of
--no-recurse-submodules::
Disable recursive fetching of submodules (this has the same effect as
- using the '--recurse-submodules=no' option).
+ using the `--recurse-submodules=no` option).
--submodule-prefix=<path>::
Prepend <path> to paths printed in informative messages
--upload-pack <upload-pack>::
When given, and the repository to fetch from is handled
- by 'git fetch-pack', '--exec=<upload-pack>' is passed to
+ by 'git fetch-pack', `--exec=<upload-pack>` is passed to
the command to specify non-default path for the command
run on the other end.
by default when it is attached to a terminal, unless -q
is specified. This flag forces progress status even if the
standard error stream is not directed to a terminal.
+
+-4::
+--ipv4::
+ Use IPv4 addresses only, ignoring IPv6 addresses.
+
+-6::
+--ipv6::
+ Use IPv6 addresses only, ignoring IPv4 addresses.
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch]
- [--recursive | --recurse-submodules] [--] <repository>
- [<directory>]
+ [--recursive | --recurse-submodules] [--[no-]shallow-submodules]
+ [--jobs <n>] [--] <repository> [<directory>]
DESCRIPTION
-----------
its source repository, you can simply run `git repack -a` to copy all
objects from the source repository into a pack in the cloned repository.
---reference <repository>::
+--reference[-if-able] <repository>::
If the reference repository is on the local machine,
automatically setup `.git/objects/info/alternates` to
obtain objects from the reference repository. Using
an already existing repository as an alternate will
require fewer objects to be copied from the repository
being cloned, reducing network and local storage costs.
+ When using the `--reference-if-able`, a non existing
+ directory is skipped with a warning instead of aborting
+ the clone.
+
*NOTE*: see the NOTE for the `--shared` option, and also the
`--dissociate` option.
Create a 'shallow' clone with a history truncated to the
specified number of commits. Implies `--single-branch` unless
`--no-single-branch` is given to fetch the histories near the
- tips of all branches.
+ tips of all branches. If you want to clone submodules shallowly,
+ also pass `--shallow-submodules`.
+ --shallow-since=<date>::
+ Create a shallow clone with a history after the specified time.
+
+ --shallow-exclude=<revision>::
+ Create a shallow clone with a history, excluding commits
+ reachable from a specified remote branch or tag. This option
+ can be specified multiple times.
+
--[no-]single-branch::
Clone only the history leading to the tip of a single branch,
either specified by the `--branch` option or the primary
repository does not have a worktree/checkout (i.e. if any of
`--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+--[no-]shallow-submodules::
+ All submodules which are cloned will be shallow with a depth of 1.
+
--separate-git-dir=<git dir>::
Instead of placing the cloned repository where it is supposed
to be, place the cloned repository at the specified directory,
The result is Git repository can be separated from working
tree.
+-j <n>::
+--jobs <n>::
+ The number of submodules fetched at the same time.
+ Defaults to the `submodule.fetchJobs` option.
<repository>::
The (possibly remote) repository to clone from. See the
option, then the refs from stdin are processed after those
on the command line.
+
-If '--stateless-rpc' is specified together with this option then
+If `--stateless-rpc` is specified together with this option then
the list of refs must be in packet format (pkt-line). Each ref must
be in a separate packet, and the list must end with a flush packet.
-q::
--quiet::
- Pass '-q' flag to 'git unpack-objects'; this makes the
+ Pass `-q` flag to 'git unpack-objects'; this makes the
cloning process less verbose.
-k::
'git-upload-pack' treats the special depth 2147483647 as
infinite even if there is an ancestor-chain that long.
+ --shallow-since=<date>::
+ Deepen or shorten the history of a shallow'repository to
+ include all reachable commits after <date>.
+
+ --shallow-exclude=<revision>::
+ Deepen or shorten the history of a shallow repository to
+ exclude commits reachable from a specified remote branch or tag.
+ This option can be specified multiple times.
+
+ --deepen-relative::
+ Argument --depth specifies the number of commits from the
+ current shallow boundary instead of from the tip of each
+ remote branch history.
+
--no-progress::
Do not show the progress.
The remote heads to update from. This is relative to
$GIT_DIR (e.g. "HEAD", "refs/heads/master"). When
unspecified, update from all heads the remote side has.
++
+If the remote has enabled the options `uploadpack.allowTipSHA1InWant` or
+`uploadpack.allowReachableSHA1InWant`, they may alternatively be 40-hex
+sha1s present on the remote.
SEE ALSO
--------
it is either the name of a configured remote or a URL. The second
argument specifies a URL; it is usually of the form
'<transport>://<address>', but any arbitrary string is possible.
-The 'GIT_DIR' environment variable is set up for the remote helper
+The `GIT_DIR` environment variable is set up for the remote helper
and can be used to determine where to store additional data or from
which directory to invoke auxiliary Git commands.
the first argument is '<address>', and if it is encountered in a
configured remote, the first argument is the name of that remote.
-Additionally, when a configured remote has 'remote.<name>.vcs' set to
+Additionally, when a configured remote has `remote.<name>.vcs` set to
'<transport>', Git explicitly invokes 'git remote-<transport>' with
'<name>' as the first argument. If set, the second argument is
-'remote.<name>.url'; otherwise, the second argument is omitted.
+`remote.<name>.url`; otherwise, the second argument is omitted.
INPUT FORMAT
------------
'export-marks' <file>::
This modifies the 'export' capability, instructing Git to dump the
internal marks table to <file> when complete. For details,
- read up on '--export-marks=<file>' in linkgit:git-fast-export[1].
+ read up on `--export-marks=<file>` in linkgit:git-fast-export[1].
'import-marks' <file>::
This modifies the 'export' capability, instructing Git to load the
marks specified in <file> before processing any input. For details,
- read up on '--import-marks=<file>' in linkgit:git-fast-export[1].
+ read up on `--import-marks=<file>` in linkgit:git-fast-export[1].
'signed-tags'::
This modifies the 'export' capability, instructing Git to pass
- '--signed-tags=verbatim' to linkgit:git-fast-export[1]. In the
- absence of this capability, Git will use '--signed-tags=warn-strip'.
+ `--signed-tags=verbatim` to linkgit:git-fast-export[1]. In the
+ absence of this capability, Git will use `--signed-tags=warn-strip`.
is followed by a blank line). For example, the following would
be two batches of 'push', the first asking the remote-helper
to push the local ref 'master' to the remote ref 'master' and
- the local 'HEAD' to the remote 'branch', and the second
+ the local `HEAD` to the remote 'branch', and the second
asking to push ref 'foo' to ref 'bar' (forced update requested
by the '+').
+
'option depth' <depth>::
Deepens the history of a shallow repository.
+ 'option deepen-since <timestamp>::
+ Deepens the history of a shallow repository based on time.
+
+ 'option deepen-not <ref>::
+ Deepens the history of a shallow repository excluding ref.
+ Multiple options add up.
+
+ 'option deepen-relative {'true'|'false'}::
+ Deepens the history of a shallow repository relative to
+ current boundary. Only valid when used with "option depth".
+
'option followtags' {'true'|'false'}::
If enabled the helper should automatically fetch annotated
tag objects if the object the tag points at was transferred
shallow-line = PKT-LINE("shallow" SP obj-id)
- depth-request = PKT-LINE("deepen" SP depth)
+ depth-request = PKT-LINE("deepen" SP depth) /
+ PKT-LINE("deepen-since" SP timestamp) /
+ PKT-LINE("deepen-not" SP ref)
first-want = PKT-LINE("want" SP obj-id SP capability-list)
additional-want = PKT-LINE("want" SP obj-id)
ready to make a packfile, it will blindly ACK all 'have' obj-ids
back to the client.
- * the server will then send a 'NACK' and then wait for another response
+ * the server will then send a 'NAK' and then wait for another response
from the client - either a 'done' or another list of 'have' lines.
In multi_ack_detailed mode:
fetching protocol. Each reference obj-id and name on the server is sent
in packet-line format to the client, followed by a flush-pkt. The only
real difference is that the capability listing is different - the only
-possible values are 'report-status', 'delete-refs' and 'ofs-delta'.
+possible values are 'report-status', 'delete-refs', 'ofs-delta' and
+'push-options'.
Reference Update Request and Packfile Transfer
----------------------------------------------
the server, the obj-id the client would like to update it to and the name
of the reference.
-This list is followed by a flush-pkt and then the packfile that should
-contain all the objects that the server will need to complete the new
-references.
+This list is followed by a flush-pkt. Then the push options are transmitted
+one per packet followed by another flush-pkt. After that the packfile that
+should contain all the objects that the server will need to complete the new
+references will be sent.
----
update-request = *shallow ( command-list | push-cert ) [packfile]
A push certificate begins with a set of header lines. After the
header and an empty line, the protocol commands follow, one per
-line. Note that the the trailing LF in push-cert PKT-LINEs is _not_
+line. Note that the trailing LF in push-cert PKT-LINEs is _not_
optional; it must be present.
Currently, the following header fields are defined:
the fetch-pack/upload-pack protocol so clients can request shallow
clones.
+ deepen-since
+ ------------
+
+ This capability adds "deepen-since" command to fetch-pack/upload-pack
+ protocol so the client can request shallow clones that are cut at a
+ specific time, instead of depth. Internally it's equivalent of doing
+ "rev-list --max-age=<timestamp>" on the server side. "deepen-since"
+ cannot be used with "deepen".
+
+ deepen-not
+ ----------
+
+ This capability adds "deepen-not" command to fetch-pack/upload-pack
+ protocol so the client can request shallow clones that are cut at a
+ specific revision, instead of depth. Internally it's equivalent of
+ doing "rev-list --not <rev>" on the server side. "deepen-not"
+ cannot be used with "deepen", but can be used with "deepen-since".
+
+ deepen-relative
+ ---------------
+
+ If this capability is requested by the client, the semantics of
+ "deepen" command is changed. The "depth" argument is the depth from
+ the current shallow boundary, instead of the depth from remote refs.
+
no-progress
-----------
will update the refs in one atomic transaction. Either all refs are
updated or none.
+push-options
+------------
+
+If the server sends the 'push-options' capability it is able to accept
+push options after the update commands have been sent, but before the
+packfile is streamed. If the pushing client requests this capability,
+the server will pass the options to the pre- and post- receive hooks
+that process this push request.
+
allow-tip-sha1-in-want
----------------------
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
- static char *option_template, *option_depth;
+static int option_shallow_submodules;
+ static int deepen;
+ static char *option_template, *option_depth, *option_since;
static char *option_origin = NULL;
static char *option_branch = NULL;
+ static struct string_list option_not = STRING_LIST_INIT_NODUP;
static const char *real_git_dir;
static char *option_upload_pack = "git-upload-pack";
static int option_verbosity;
static int option_progress = -1;
-static struct string_list option_config;
-static struct string_list option_reference;
+static enum transport_family family;
+static struct string_list option_config = STRING_LIST_INIT_NODUP;
+static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
+static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
static int option_dissociate;
+static int max_jobs = -1;
static struct option builtin_clone_options[] = {
OPT__VERBOSITY(&option_verbosity),
N_("initialize submodules in the clone")),
OPT_BOOL(0, "recurse-submodules", &option_recursive,
N_("initialize submodules in the clone")),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
N_("directory from which templates will be used")),
- OPT_STRING_LIST(0, "reference", &option_reference, N_("repo"),
+ OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"),
N_("reference repository")),
+ OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference,
+ N_("repo"), N_("reference repository")),
OPT_BOOL(0, "dissociate", &option_dissociate,
N_("use --reference only while cloning")),
OPT_STRING('o', "origin", &option_origin, N_("name"),
N_("path to git-upload-pack on the remote")),
OPT_STRING(0, "depth", &option_depth, N_("depth"),
N_("create a shallow clone of that depth")),
+ OPT_STRING(0, "shallow-since", &option_since, N_("time"),
+ N_("create a shallow clone since a specific time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
+ N_("deepen history of shallow clone by excluding rev")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
+ N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
N_("set config inside the new repository")),
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
OPT_END()
};
-static const char *argv_submodule[] = {
- "submodule", "update", "--init", "--recursive", NULL
-};
-
static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
{
static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
if (!len || (len == 1 && *start == '/'))
- die("No directory name could be guessed.\n"
- "Please specify a directory on the command line");
+ die(_("No directory name could be guessed.\n"
+ "Please specify a directory on the command line"));
if (is_bare)
dir = xstrfmt("%.*s.git", (int)len, start);
static int add_one_reference(struct string_list_item *item, void *cb_data)
{
- char *ref_git;
- const char *repo;
- struct strbuf alternate = STRBUF_INIT;
-
- /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */
- ref_git = xstrdup(real_path(item->string));
-
- repo = read_gitfile(ref_git);
- if (!repo)
- repo = read_gitfile(mkpath("%s/.git", ref_git));
- if (repo) {
- free(ref_git);
- ref_git = xstrdup(repo);
- }
+ struct strbuf err = STRBUF_INIT;
+ int *required = cb_data;
+ char *ref_git = compute_alternate_path(item->string, &err);
- if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
- char *ref_git_git = mkpathdup("%s/.git", ref_git);
- free(ref_git);
- ref_git = ref_git_git;
- } else if (!is_directory(mkpath("%s/objects", ref_git))) {
+ if (!ref_git) {
+ if (*required)
+ die("%s", err.buf);
+ else
+ fprintf(stderr,
+ _("info: Could not add alternate for '%s': %s\n"),
+ item->string, err.buf);
+ } else {
struct strbuf sb = STRBUF_INIT;
- if (get_common_dir(&sb, ref_git))
- die(_("reference repository '%s' as a linked checkout is not supported yet."),
- item->string);
- die(_("reference repository '%s' is not a local repository."),
- item->string);
+ strbuf_addf(&sb, "%s/objects", ref_git);
+ add_to_alternates_file(sb.buf);
+ strbuf_release(&sb);
}
- if (!access(mkpath("%s/shallow", ref_git), F_OK))
- die(_("reference repository '%s' is shallow"), item->string);
-
- if (!access(mkpath("%s/info/grafts", ref_git), F_OK))
- die(_("reference repository '%s' is grafted"), item->string);
-
- strbuf_addf(&alternate, "%s/objects", ref_git);
- add_to_alternates_file(alternate.buf);
- strbuf_release(&alternate);
+ strbuf_release(&err);
free(ref_git);
return 0;
}
static void setup_reference(void)
{
- for_each_string_list(&option_reference, add_one_reference, NULL);
+ int required = 1;
+ for_each_string_list(&option_required_reference,
+ add_one_reference, &required);
+ required = 0;
+ for_each_string_list(&option_optional_reference,
+ add_one_reference, &required);
}
static void copy_alternates(struct strbuf *src, struct strbuf *dst,
const struct ref *rm = mapped_refs;
if (check_connectivity) {
- if (transport->progress)
- fprintf(stderr, _("Checking connectivity... "));
- if (check_everything_connected_with_transport(iterate_ref_map,
- 0, &rm, transport))
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
+
+ opt.transport = transport;
+ opt.progress = transport->progress;
+
+ if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
- if (transport->progress)
- fprintf(stderr, _("done.\n"));
}
if (refs) {
if (create_symref(head_ref.buf,
remote_head_points_at->peer_ref->name,
msg) < 0)
- die("unable to update %s", head_ref.buf);
+ die(_("unable to update %s"), head_ref.buf);
strbuf_release(&head_ref);
}
}
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
if (create_symref("HEAD", our->name, NULL) < 0)
- die("unable to update HEAD");
+ die(_("unable to update HEAD"));
if (!option_bare) {
update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
}
}
-static int checkout(void)
+static int checkout(int submodule_progress)
{
unsigned char sha1[20];
char *head;
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(sha1), "1", NULL);
- if (!err && option_recursive)
- err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+ if (!err && option_recursive) {
+ struct argv_array args = ARGV_ARRAY_INIT;
+ argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
+
+ if (option_shallow_submodules == 1)
+ argv_array_push(&args, "--depth=1");
+
+ if (max_jobs != -1)
+ argv_array_pushf(&args, "--jobs=%d", max_jobs);
+
+ if (submodule_progress)
+ argv_array_push(&args, "--progress");
+
+ err = run_command_v_opt(args.argv, RUN_GIT_CMD);
+ argv_array_clear(&args);
+ }
return err;
}
static int write_one_config(const char *key, const char *value, void *data)
{
- return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+ return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0);
}
static void write_config(struct string_list *config)
for (i = 0; i < config->nr; i++) {
if (git_config_parse_parameter(config->items[i].string,
write_one_config, NULL) < 0)
- die("unable to write parameters to config file");
+ die(_("unable to write parameters to config file"));
}
}
const char *src_ref_prefix = "refs/heads/";
struct remote *remote;
int err = 0, complete_refs_before_fetch = 1;
+ int submodule_progress;
struct refspec *refspec;
const char *fetch_pattern;
usage_msg_opt(_("You must specify a repository to clone."),
builtin_clone_usage, builtin_clone_options);
+ if (option_depth || option_since || option_not.nr)
+ deepen = 1;
if (option_single_branch == -1)
- option_single_branch = option_depth ? 1 : 0;
+ option_single_branch = deepen ? 1 : 0;
if (option_mirror)
option_bare = 1;
set_git_work_tree(work_tree);
}
- junk_git_dir = git_dir;
+ junk_git_dir = real_git_dir ? real_git_dir : git_dir;
if (safe_create_leading_directories_const(git_dir) < 0)
die(_("could not create leading directories of '%s'"), git_dir);
- set_git_dir_init(git_dir, real_git_dir, 0);
- if (real_git_dir) {
- git_dir = real_git_dir;
- junk_git_dir = real_git_dir;
- }
-
if (0 <= option_verbosity) {
if (option_bare)
fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir);
else
fprintf(stderr, _("Cloning into '%s'...\n"), dir);
}
- init_db(option_template, INIT_DB_QUIET);
+
+ if (option_recursive) {
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
+ else if (option_required_reference.nr) {
+ string_list_append(&option_config,
+ "submodule.alternateLocation=superproject");
+ string_list_append(&option_config,
+ "submodule.alternateErrorStrategy=die");
+ } else if (option_optional_reference.nr) {
+ string_list_append(&option_config,
+ "submodule.alternateLocation=superproject");
+ string_list_append(&option_config,
+ "submodule.alternateErrorStrategy=info");
+ }
+ }
+
+ init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+
+ if (real_git_dir)
+ git_dir = real_git_dir;
+
write_config(&option_config);
git_config(git_default_config, NULL);
git_config_set(key.buf, repo);
strbuf_reset(&key);
- if (option_reference.nr)
+ if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
fetch_pattern = value.buf;
remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
transport_set_verbosity(transport, option_verbosity, option_progress);
+ transport->family = family;
path = get_repo_path(remote->url[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
if (is_local) {
if (option_depth)
warning(_("--depth is ignored in local clones; use file:// instead."));
+ if (option_since)
+ warning(_("--shallow-since is ignored in local clones; use file:// instead."));
+ if (option_not.nr)
+ warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
if (!access(mkpath("%s/shallow", path), F_OK)) {
if (option_local > 0)
warning(_("source repository is shallow, ignoring --local"));
if (option_depth)
transport_set_option(transport, TRANS_OPT_DEPTH,
option_depth);
+ if (option_since)
+ transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,
+ option_since);
+ if (option_not.nr)
+ transport_set_option(transport, TRANS_OPT_DEEPEN_NOT,
+ (const char *)&option_not);
if (option_single_branch)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
transport_set_option(transport, TRANS_OPT_UPLOADPACK,
option_upload_pack);
- if (transport->smart_options && !option_depth)
+ if (transport->smart_options && !deepen)
transport->smart_options->check_self_contained_and_connected = 1;
refs = transport_get_remote_refs(transport);
update_head(our_head_points_at, remote_head, reflog_msg.buf);
+ /*
+ * We want to show progress for recursive submodule clones iff
+ * we did so for the main clone. But only the transport knows
+ * the final decision for this flag, so we need to rescue the value
+ * before we free the transport.
+ */
+ submodule_progress = transport->progress;
+
transport_unlock_pack(transport);
transport_disconnect(transport);
}
junk_mode = JUNK_LEAVE_REPO;
- err = checkout();
+ err = checkout(submodule_progress);
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
-static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
- const char *name, int namelen)
+static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+ const char *name)
{
- struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+ struct ref *ref;
struct object_id oid;
- const int chunksz = GIT_SHA1_HEXSZ + 1;
- if (namelen > chunksz && name[chunksz - 1] == ' ' &&
- !get_oid_hex(name, &oid)) {
- oidcpy(&ref->old_oid, &oid);
- name += chunksz;
- namelen -= chunksz;
+ if (!get_oid_hex(name, &oid)) {
+ if (name[GIT_SHA1_HEXSZ] == ' ') {
+ /* <sha1> <ref>, find refname */
+ name += GIT_SHA1_HEXSZ + 1;
+ } else if (name[GIT_SHA1_HEXSZ] == '\0') {
+ ; /* <sha1>, leave sha1 as name */
+ } else {
+ /* <ref>, clear cruft from oid */
+ oidclr(&oid);
+ }
+ } else {
+ /* <ref>, clear cruft from get_oid_hex */
+ oidclr(&oid);
}
- memcpy(ref->name, name, namelen);
- ref->name[namelen] = '\0';
+ ref = alloc_ref(name);
+ oidcpy(&ref->old_oid, &oid);
(*nr)++;
ALLOC_GROW(*sought, *nr, *alloc);
(*sought)[*nr - 1] = ref;
}
-static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
- const char *string)
-{
- add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
-}
-
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
{
int i, ret;
struct child_process *conn;
struct fetch_pack_args args;
struct sha1_array shallow = SHA1_ARRAY_INIT;
+ struct string_list deepen_not = STRING_LIST_INIT_DUP;
packet_trace_identity("fetch-pack");
for (i = 1; i < argc && *argv[i] == '-'; i++) {
const char *arg = argv[i];
- if (starts_with(arg, "--upload-pack=")) {
- args.uploadpack = arg + 14;
+ if (skip_prefix(arg, "--upload-pack=", &arg)) {
+ args.uploadpack = arg;
continue;
}
- if (starts_with(arg, "--exec=")) {
- args.uploadpack = arg + 7;
+ if (skip_prefix(arg, "--exec=", &arg)) {
+ args.uploadpack = arg;
continue;
}
if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
args.verbose = 1;
continue;
}
- if (starts_with(arg, "--depth=")) {
- args.depth = strtol(arg + 8, NULL, 0);
+ if (skip_prefix(arg, "--depth=", &arg)) {
+ args.depth = strtol(arg, NULL, 0);
+ continue;
+ }
+ if (skip_prefix(arg, "--shallow-since=", &arg)) {
+ args.deepen_since = xstrdup(arg);
+ continue;
+ }
+ if (skip_prefix(arg, "--shallow-exclude=", &arg)) {
+ string_list_append(&deepen_not, arg);
+ continue;
+ }
+ if (!strcmp(arg, "--deepen-relative")) {
+ args.deepen_relative = 1;
continue;
}
if (!strcmp("--no-progress", arg)) {
}
usage(fetch_pack_usage);
}
+ if (deepen_not.nr)
+ args.deepen_not = &deepen_not;
if (i < argc)
dest = argv[i++];
#include "submodule.h"
#include "connected.h"
#include "argv-array.h"
+#include "utf8.h"
static const char * const builtin_fetch_usage[] = {
N_("git fetch [<options>] [<repository> [<refspec>...]]"),
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
- static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
+ static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
- static int tags = TAGS_DEFAULT, unshallow, update_shallow;
+ static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_children = 1;
+static int max_children = -1;
+static enum transport_family family;
static const char *depth;
+ static const char *deepen_since;
static const char *upload_pack;
+ static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
static struct strbuf default_rla = STRBUF_INIT;
static struct transport *gtransport;
static struct transport *gsecondary;
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
OPT_STRING(0, "depth", &depth, N_("depth"),
N_("deepen history of shallow clone")),
+ OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+ N_("deepen history of shallow repository based on time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+ N_("deepen history of shallow clone by excluding rev")),
+ OPT_INTEGER(0, "deepen", &deepen_relative,
+ N_("deepen history of shallow clone")),
{ OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
N_("convert to a complete repository"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
N_("accept refs that update .git/shallow")),
{ OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
OPT_END()
};
: STORE_REF_ERROR_OTHER;
}
-#define REFCOL_WIDTH 10
+static int refcol_width = 10;
+static int compact_format;
+
+static void adjust_refcol_width(const struct ref *ref)
+{
+ int max, rlen, llen, len;
+
+ /* uptodate lines are only shown on high verbosity level */
+ if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))
+ return;
+
+ max = term_columns();
+ rlen = utf8_strwidth(prettify_refname(ref->name));
+
+ llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+ /*
+ * rough estimation to see if the output line is too long and
+ * should not be counted (we can't do precise calculation
+ * anyway because we don't know if the error explanation part
+ * will be printed in update_local_ref)
+ */
+ if (compact_format) {
+ llen = 0;
+ max = max * 2 / 3;
+ }
+ len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+ if (len >= max)
+ return;
+
+ /*
+ * Not precise calculation for compact mode because '*' can
+ * appear on the left hand side of '->' and shrink the column
+ * back.
+ */
+ if (refcol_width < rlen)
+ refcol_width = rlen;
+}
+
+static void prepare_format_display(struct ref *ref_map)
+{
+ struct ref *rm;
+ const char *format = "full";
+
+ git_config_get_string_const("fetch.output", &format);
+ if (!strcasecmp(format, "full"))
+ compact_format = 0;
+ else if (!strcasecmp(format, "compact"))
+ compact_format = 1;
+ else
+ die(_("configuration fetch.output contains invalid value %s"),
+ format);
+
+ for (rm = ref_map; rm; rm = rm->next) {
+ if (rm->status == REF_STATUS_REJECT_SHALLOW ||
+ !rm->peer_ref ||
+ !strcmp(rm->name, "HEAD"))
+ continue;
+
+ adjust_refcol_width(rm);
+ }
+}
+
+static void print_remote_to_local(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+}
+
+static int find_and_replace(struct strbuf *haystack,
+ const char *needle,
+ const char *placeholder)
+{
+ const char *p = strstr(haystack->buf, needle);
+ int plen, nlen;
+
+ if (!p)
+ return 0;
+
+ if (p > haystack->buf && p[-1] != '/')
+ return 0;
+
+ plen = strlen(p);
+ nlen = strlen(needle);
+ if (plen > nlen && p[nlen] != '/')
+ return 0;
+
+ strbuf_splice(haystack, p - haystack->buf, nlen,
+ placeholder, strlen(placeholder));
+ return 1;
+}
+
+static void print_compact(struct strbuf *display,
+ const char *remote, const char *local)
+{
+ struct strbuf r = STRBUF_INIT;
+ struct strbuf l = STRBUF_INIT;
+
+ if (!strcmp(remote, local)) {
+ strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+ return;
+ }
+
+ strbuf_addstr(&r, remote);
+ strbuf_addstr(&l, local);
+
+ if (!find_and_replace(&r, local, "*"))
+ find_and_replace(&l, remote, "*");
+ print_remote_to_local(display, r.buf, l.buf);
+
+ strbuf_release(&r);
+ strbuf_release(&l);
+}
+
+static void format_display(struct strbuf *display, char code,
+ const char *summary, const char *error,
+ const char *remote, const char *local)
+{
+ strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
+ if (!compact_format)
+ print_remote_to_local(display, remote, local);
+ else
+ print_compact(display, remote, local);
+ if (error)
+ strbuf_addf(display, " (%s)", error);
+}
static int update_local_ref(struct ref *ref,
const char *remote,
if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
- strbuf_addf(display, "= %-*s %-*s -> %s",
- TRANSPORT_SUMMARY(_("[up to date]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '=', _("[up to date]"), NULL,
+ remote, pretty_ref);
return 0;
}
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- strbuf_addf(display,
- _("! %-*s %-*s -> %s (can't fetch in current branch)"),
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref);
+ format_display(display, '!', _("[rejected]"),
+ _("can't fetch in current branch"),
+ remote, pretty_ref);
return 1;
}
starts_with(ref->name, "refs/tags/")) {
int r;
r = s_update_ref("updating tag", ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '-',
- TRANSPORT_SUMMARY(_("[tag update]")),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref(msg, ref, 0);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : '*',
- TRANSPORT_SUMMARY(what),
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : '*', what,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("fast-forward", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s%s",
- r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref.buf,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _(" (unable to update local ref)") : "");
+ format_display(display, r ? '!' : ' ', quickref.buf,
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_oid.hash);
r = s_update_ref("forced-update", ref, 1);
- strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
- r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref.buf,
- REFCOL_WIDTH, remote, pretty_ref,
- r ? _("unable to update local ref") : _("forced update"));
+ format_display(display, r ? '!' : '+', quickref.buf,
+ r ? _("unable to update local ref") : _("forced update"),
+ remote, pretty_ref);
strbuf_release(&quickref);
return r;
} else {
- strbuf_addf(display, "! %-*s %-*s -> %s %s",
- TRANSPORT_SUMMARY(_("[rejected]")),
- REFCOL_WIDTH, remote, pretty_ref,
- _("(non-fast-forward)"));
+ format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
+ remote, pretty_ref);
return 1;
}
}
fp = fopen(filename, "a");
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
if (raw_url)
url = transport_anonymize_url(raw_url);
url = xstrdup("foreign");
rm = ref_map;
- if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+ if (check_connected(iterate_ref_map, &rm, NULL)) {
rc = error(_("%s did not send all necessary objects\n"), url);
goto abort;
}
+ prepare_format_display(ref_map);
+
/*
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
rc |= update_local_ref(ref, what, rm, ¬e);
free(ref);
} else
- strbuf_addf(¬e, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH,
- *kind ? kind : "branch",
- REFCOL_WIDTH,
- *what ? what : "HEAD");
+ format_display(¬e, '*',
+ *kind ? kind : "branch", NULL,
+ *what ? what : "HEAD",
+ "FETCH_HEAD");
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
static int quickfetch(struct ref *ref_map)
{
struct ref *rm = ref_map;
+ struct check_connected_options opt = CHECK_CONNECTED_INIT;
/*
* If we are deepening a shallow clone we already have these
* really need to perform. Claiming failure now will ensure
* we perform the network exchange to deepen our history.
*/
- if (depth)
+ if (deepen)
return -1;
- return check_everything_connected(iterate_ref_map, 1, &rm);
+ opt.quiet = 1;
+ return check_connected(iterate_ref_map, &rm, &opt);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs(&refnames);
+ result = delete_refs(&refnames, 0);
string_list_clear(&refnames, 0);
}
if (verbosity >= 0) {
for (ref = stale_refs; ref; ref = ref->next) {
+ struct strbuf sb = STRBUF_INIT;
if (!shown_url) {
fprintf(stderr, _("From %.*s\n"), url_len, url);
shown_url = 1;
}
- fprintf(stderr, " x %-*s %-*s -> %s\n",
- TRANSPORT_SUMMARY(_("[deleted]")),
- REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
+ format_display(&sb, '-', _("[deleted]"), NULL,
+ _("(none)"), prettify_refname(ref->name));
+ fprintf(stderr, " %s\n",sb.buf);
+ strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error(_("cannot open %s: %s\n"), filename, strerror(errno));
+ return error_errno(_("cannot open %s"), filename);
fclose(fp);
return 0;
}
name, transport->url);
}
- static struct transport *prepare_transport(struct remote *remote)
+ static struct transport *prepare_transport(struct remote *remote, int deepen)
{
struct transport *transport;
transport = transport_get(remote, NULL);
transport_set_verbosity(transport, verbosity, progress);
+ transport->family = family;
if (upload_pack)
set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
if (keep)
set_option(transport, TRANS_OPT_KEEP, "yes");
if (depth)
set_option(transport, TRANS_OPT_DEPTH, depth);
+ if (deepen && deepen_since)
+ set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
+ if (deepen && deepen_not.nr)
+ set_option(transport, TRANS_OPT_DEEPEN_NOT,
+ (const char *)&deepen_not);
+ if (deepen_relative)
+ set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
if (update_shallow)
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
return transport;
static void backfill_tags(struct transport *transport, struct ref *ref_map)
{
- if (transport->cannot_reuse) {
- gsecondary = prepare_transport(transport->remote);
+ int cannot_reuse;
+
+ /*
+ * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
+ * when remote helper is used (setting it to an empty string
+ * is not unsetting). We could extend the remote helper
+ * protocol for that, but for now, just force a new connection
+ * without deepen-since. Similar story for deepen-not.
+ */
+ cannot_reuse = transport->cannot_reuse ||
+ deepen_since || deepen_not.nr;
+ if (cannot_reuse) {
+ gsecondary = prepare_transport(transport->remote, 0);
transport = gsecondary;
}
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+ transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
fetch_refs(transport, ref_map);
if (gsecondary) {
size_t wordlen = strcspn(value, " \t\n");
if (wordlen >= 1)
- string_list_append(g->list,
+ string_list_append_nodup(g->list,
xstrndup(value, wordlen));
value += wordlen + (value[wordlen] != '\0');
}
git_config(get_remote_group, &g);
if (list->nr == prev_nr) {
- struct remote *remote;
- if (!remote_is_configured(name))
+ struct remote *remote = remote_get(name);
+ if (!remote_is_configured(remote))
return 0;
- remote = remote_get(name);
string_list_append(list, remote->name);
}
return 1;
die(_("No remote repository specified. Please, specify either a URL or a\n"
"remote name from which new revisions should be fetched."));
- gtransport = prepare_transport(remote);
+ gtransport = prepare_transport(remote, 1);
if (prune < 0) {
/* no command line request */
if (argc > 0) {
int j = 0;
int i;
- refs = xcalloc(argc + 1, sizeof(const char *));
+ refs = xcalloc(st_add(argc, 1), sizeof(const char *));
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "tag")) {
i++;
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
- struct string_list list = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote;
int result = 0;
struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
+ if (deepen_relative) {
+ if (deepen_relative < 0)
+ die(_("Negative depth in --deepen is not supported"));
+ if (depth)
+ die(_("--deepen and --depth are mutually exclusive"));
+ depth = xstrfmt("%d", deepen_relative);
+ }
if (unshallow) {
if (depth)
die(_("--depth and --unshallow cannot be used together"));
/* no need to be strict, transport_set_option() will validate it again */
if (depth && atoi(depth) < 1)
die(_("depth %s is not a positive number"), depth);
+ if (depth || deepen_since || deepen_not.nr)
+ deepen = 1;
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
if (recurse_submodules_default) {
argv_array_clear(&options);
}
- /* All names were strdup()ed or strndup()ed */
- list.strdup_strings = 1;
string_list_clear(&list, 0);
close_all_packs();
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
CMIT_FMT_EMAIL,
+ CMIT_FMT_MBOXRD,
CMIT_FMT_USERFORMAT,
CMIT_FMT_UNSPECIFIED
};
+static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
+{
+ return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
+}
+
struct pretty_print_context {
/*
* Callers should tweak these to change the behavior of pp_* functions.
int preserve_subject;
struct date_mode date_mode;
unsigned date_mode_explicit:1;
+ int expand_tabs_in_log;
int need_8bit_cte;
char *notes_message;
struct reflog_walk_info *reflog_info;
* should not be counted on by callers.
*/
struct string_list in_body_headers;
+ int graph_width;
};
struct userformat_want {
const char *line_separator);
extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
extern int commit_format_is_empty(enum cmit_fmt);
+extern const char *skip_blank_lines(const char *msg);
extern void format_commit_message(const struct commit *commit,
const char *format, struct strbuf *sb,
const struct pretty_print_context *context);
extern int is_repository_shallow(void);
extern struct commit_list *get_shallow_commits(struct object_array *heads,
int depth, int shallow_flag, int not_shallow_flag);
+ extern struct commit_list *get_shallow_commits_by_rev_list(
+ int ac, const char **av, int shallow_flag, int not_shallow_flag);
extern void set_alternate_shallow_file(const char *path, int override);
extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
const struct sha1_array *extra);
struct merge_remote_desc {
struct object *obj; /* the named object, could be a tag */
- const char *name;
+ char name[FLEX_ARRAY];
};
#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
+extern void set_merge_remote_desc(struct commit *commit,
+ const char *name, struct object *obj);
/*
* Given "name" from the command line to merge, find the commit object
struct strbuf *message, struct strbuf *signature);
extern int remove_signature(struct strbuf *buf);
-extern void print_commit_list(struct commit_list *list,
- const char *format_cur,
- const char *format_last);
-
/*
* Check the signature of the given commit. The result of the check is stored
* in sig->check_result, 'G' for a good signature, 'U' for a good signature
static int unpack_limit = 100;
static int prefer_ofs_delta = 1;
static int no_done;
+ static int deepen_since_ok;
+ static int deepen_not_ok;
static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
#define ALLOW_REACHABLE_SHA1 02
static unsigned int allow_unadvertised_object_request;
+ __attribute__((format (printf, 2, 3)))
+ static inline void print_verbose(const struct fetch_pack_args *args,
+ const char *fmt, ...)
+ {
+ va_list params;
+
+ if (!args->verbose)
+ return;
+
+ va_start(params, fmt);
+ vfprintf(stderr, fmt, params);
+ va_end(params);
+ fputc('\n', stderr);
+ }
+
static void rev_list_push(struct commit *commit, int mark)
{
if (!(commit->object.flags & mark)) {
static void consume_shallow_list(struct fetch_pack_args *args, int fd)
{
- if (args->stateless_rpc && args->depth > 0) {
+ if (args->stateless_rpc && args->deepen) {
/* If we sent a depth we will get back "duplicate"
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
continue;
if (starts_with(line, "unshallow "))
continue;
- die("git fetch-pack: expected shallow list");
+ die(_("git fetch-pack: expected shallow list"));
}
}
}
const char *arg;
if (!len)
- die("git fetch-pack: expected ACK/NAK, got EOF");
+ die(_("git fetch-pack: expected ACK/NAK, got EOF"));
if (!strcmp(line, "NAK"))
return NAK;
if (skip_prefix(line, "ACK ", &arg)) {
return ACK;
}
}
- die("git fetch_pack: expected ACK/NAK, got '%s'", line);
+ die(_("git fetch_pack: expected ACK/NAK, got '%s'"), line);
}
static void send_request(struct fetch_pack_args *args,
#define INITIAL_FLUSH 16
#define PIPESAFE_FLUSH 32
-#define LARGE_FLUSH 1024
+#define LARGE_FLUSH 16384
static int next_flush(struct fetch_pack_args *args, int count)
{
- int flush_limit = args->stateless_rpc ? LARGE_FLUSH : PIPESAFE_FLUSH;
-
- if (count < flush_limit)
- count <<= 1;
- else
- count += flush_limit;
+ if (args->stateless_rpc) {
+ if (count < LARGE_FLUSH)
+ count <<= 1;
+ else
+ count = count * 11 / 10;
+ } else {
+ if (count < PIPESAFE_FLUSH)
+ count <<= 1;
+ else
+ count += PIPESAFE_FLUSH;
+ }
return count;
}
size_t state_len = 0;
if (args->stateless_rpc && multi_ack == 1)
- die("--stateless-rpc requires multi_ack_detailed");
+ die(_("--stateless-rpc requires multi_ack_detailed"));
if (marked)
for_each_ref(clear_marks, NULL);
marked = 1;
if (no_done) strbuf_addstr(&c, " no-done");
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
+ if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
if (args->no_progress) strbuf_addstr(&c, " no-progress");
if (args->include_tag) strbuf_addstr(&c, " include-tag");
if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta");
+ if (deepen_since_ok) strbuf_addstr(&c, " deepen-since");
+ if (deepen_not_ok) strbuf_addstr(&c, " deepen-not");
if (agent_supported) strbuf_addf(&c, " agent=%s",
git_user_agent_sanitized());
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
write_shallow_commits(&req_buf, 1, NULL);
if (args->depth > 0)
packet_buf_write(&req_buf, "deepen %d", args->depth);
+ if (args->deepen_since) {
+ unsigned long max_age = approxidate(args->deepen_since);
+ packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+ }
+ if (args->deepen_not) {
+ int i;
+ for (i = 0; i < args->deepen_not->nr; i++) {
+ struct string_list_item *s = args->deepen_not->items + i;
+ packet_buf_write(&req_buf, "deepen-not %s", s->string);
+ }
+ }
packet_buf_flush(&req_buf);
state_len = req_buf.len;
- if (args->depth > 0) {
+ if (args->deepen) {
char *line;
const char *arg;
unsigned char sha1[20];
while ((line = packet_read_line(fd[0], NULL))) {
if (skip_prefix(line, "shallow ", &arg)) {
if (get_sha1_hex(arg, sha1))
- die("invalid shallow line: %s", line);
+ die(_("invalid shallow line: %s"), line);
register_shallow(sha1);
continue;
}
if (skip_prefix(line, "unshallow ", &arg)) {
if (get_sha1_hex(arg, sha1))
- die("invalid unshallow line: %s", line);
+ die(_("invalid unshallow line: %s"), line);
if (!lookup_object(sha1))
- die("object not found: %s", line);
+ die(_("object not found: %s"), line);
/* make sure that it is parsed as shallow */
if (!parse_object(sha1))
- die("error in object: %s", line);
+ die(_("error in object: %s"), line);
if (unregister_shallow(sha1))
- die("no shallow found: %s", line);
+ die(_("no shallow found: %s"), line);
continue;
}
- die("expected shallow/unshallow, got %s", line);
+ die(_("expected shallow/unshallow, got %s"), line);
}
} else if (!args->stateless_rpc)
send_request(args, fd[1], &req_buf);
retval = -1;
while ((sha1 = get_rev())) {
packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
- if (args->verbose)
- fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
+ print_verbose(args, "have %s", sha1_to_hex(sha1));
in_vain++;
if (flush_at <= ++count) {
int ack;
consume_shallow_list(args, fd[0]);
do {
ack = get_ack(fd[0], result_sha1);
- if (args->verbose && ack)
- fprintf(stderr, "got ack %d %s\n", ack,
- sha1_to_hex(result_sha1));
+ if (ack)
+ print_verbose(args, _("got %s %d %s"), "ack",
+ ack, sha1_to_hex(result_sha1));
switch (ack) {
case ACK:
flushes = 0;
struct commit *commit =
lookup_commit(result_sha1);
if (!commit)
- die("invalid commit %s", sha1_to_hex(result_sha1));
+ die(_("invalid commit %s"), sha1_to_hex(result_sha1));
if (args->stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
const char *hex = sha1_to_hex(result_sha1);
packet_buf_write(&req_buf, "have %s\n", hex);
state_len = req_buf.len;
- }
+ /*
+ * Reset in_vain because an ack
+ * for this commit has not been
+ * seen.
+ */
+ in_vain = 0;
+ } else if (!args->stateless_rpc
+ || ack != ACK_common)
+ in_vain = 0;
mark_common(commit, 0, 1);
retval = 0;
- in_vain = 0;
got_continue = 1;
if (ack == ACK_ready) {
clear_prio_queue(&rev_list);
} while (ack);
flushes--;
if (got_continue && MAX_IN_VAIN < in_vain) {
- if (args->verbose)
- fprintf(stderr, "giving up\n");
+ print_verbose(args, _("giving up"));
break; /* give up */
}
}
packet_buf_write(&req_buf, "done\n");
send_request(args, fd[1], &req_buf);
}
- if (args->verbose)
- fprintf(stderr, "done\n");
+ print_verbose(args, _("done"));
if (retval != 0) {
multi_ack = 0;
flushes++;
while (flushes || multi_ack) {
int ack = get_ack(fd[0], result_sha1);
if (ack) {
- if (args->verbose)
- fprintf(stderr, "got ack (%d) %s\n", ack,
- sha1_to_hex(result_sha1));
+ print_verbose(args, _("got %s (%d) %s"), "ack",
+ ack, sha1_to_hex(result_sha1));
if (ack == ACK)
return 0;
multi_ack = 1;
unsigned long cutoff)
{
while (complete && cutoff <= complete->item->date) {
- if (args->verbose)
- fprintf(stderr, "Marking %s as complete\n",
- oid_to_hex(&complete->item->object.oid));
+ print_verbose(args, _("Marking %s as complete"),
+ oid_to_hex(&complete->item->object.oid));
pop_most_recent_commit(&complete, COMPLETE);
}
}
}
if (!keep && args->fetch_all &&
- (!args->depth || !starts_with(ref->name, "refs/tags/")))
+ (!args->deepen || !starts_with(ref->name, "refs/tags/")))
keep = 1;
if (keep) {
}
}
- if (!args->depth) {
+ if (!args->deepen) {
for_each_ref(mark_complete_oid, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
commit_list_sort_by_date(&complete);
o = lookup_object(remote);
if (!o || !(o->flags & COMPLETE)) {
retval = 0;
- if (!args->verbose)
- continue;
- fprintf(stderr,
- "want %s (%s)\n", sha1_to_hex(remote),
- ref->name);
+ print_verbose(args, "want %s (%s)", sha1_to_hex(remote),
+ ref->name);
continue;
}
- if (!args->verbose)
- continue;
- fprintf(stderr,
- "already have %s (%s)\n", sha1_to_hex(remote),
- ref->name);
+ print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote),
+ ref->name);
}
return retval;
}
static int sideband_demux(int in, int out, void *data)
{
int *xd = data;
+ int ret;
- int ret = recv_sideband("fetch-pack", xd[0], out);
+ ret = recv_sideband("fetch-pack", xd[0], out);
close(out);
return ret;
}
demux.proc = sideband_demux;
demux.data = xd;
demux.out = -1;
+ demux.isolate_sigpipe = 1;
if (start_async(&demux))
- die("fetch-pack: unable to fork off sideband"
- " demultiplexer");
+ die(_("fetch-pack: unable to fork off sideband demultiplexer"));
}
else
demux.out = xd[0];
if (!args->keep_pack && unpack_limit) {
if (read_pack_header(demux.out, &header))
- die("protocol error: bad pack header");
+ die(_("protocol error: bad pack header"));
pass_header = 1;
if (ntohl(header.hdr_entries) < unpack_limit)
do_keep = 0;
cmd.in = demux.out;
cmd.git_cmd = 1;
if (start_command(&cmd))
- die("fetch-pack: unable to fork off %s", cmd_name);
+ die(_("fetch-pack: unable to fork off %s"), cmd_name);
if (do_keep && pack_lockfile) {
*pack_lockfile = index_pack_lockfile(cmd.out);
close(cmd.out);
args->check_self_contained_and_connected &&
ret == 0;
else
- die("%s failed", cmd_name);
+ die(_("%s failed"), cmd_name);
if (use_sideband && finish_async(&demux))
- die("error in sideband demultiplexer");
+ die(_("error in sideband demultiplexer"));
return 0;
}
int agent_len;
sort_ref_list(&ref, ref_compare_name);
- qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
+ QSORT(sought, nr_sought, cmp_ref_by_name);
if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
- die("Server does not support shallow clients");
+ die(_("Server does not support shallow clients"));
+ if (args->depth > 0 || args->deepen_since || args->deepen_not)
+ args->deepen = 1;
if (server_supports("multi_ack_detailed")) {
- if (args->verbose)
- fprintf(stderr, "Server supports multi_ack_detailed\n");
+ print_verbose(args, _("Server supports multi_ack_detailed"));
multi_ack = 2;
if (server_supports("no-done")) {
- if (args->verbose)
- fprintf(stderr, "Server supports no-done\n");
+ print_verbose(args, _("Server supports no-done"));
if (args->stateless_rpc)
no_done = 1;
}
}
else if (server_supports("multi_ack")) {
- if (args->verbose)
- fprintf(stderr, "Server supports multi_ack\n");
+ print_verbose(args, _("Server supports multi_ack"));
multi_ack = 1;
}
if (server_supports("side-band-64k")) {
- if (args->verbose)
- fprintf(stderr, "Server supports side-band-64k\n");
+ print_verbose(args, _("Server supports side-band-64k"));
use_sideband = 2;
}
else if (server_supports("side-band")) {
- if (args->verbose)
- fprintf(stderr, "Server supports side-band\n");
+ print_verbose(args, _("Server supports side-band"));
use_sideband = 1;
}
if (server_supports("allow-tip-sha1-in-want")) {
- if (args->verbose)
- fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
+ print_verbose(args, _("Server supports allow-tip-sha1-in-want"));
allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
}
if (server_supports("allow-reachable-sha1-in-want")) {
- if (args->verbose)
- fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
+ print_verbose(args, _("Server supports allow-reachable-sha1-in-want"));
allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
}
if (!server_supports("thin-pack"))
args->no_progress = 0;
if (!server_supports("include-tag"))
args->include_tag = 0;
- if (server_supports("ofs-delta")) {
- if (args->verbose)
- fprintf(stderr, "Server supports ofs-delta\n");
- } else
+ if (server_supports("ofs-delta"))
+ print_verbose(args, _("Server supports ofs-delta"));
+ else
prefer_ofs_delta = 0;
if ((agent_feature = server_feature_value("agent", &agent_len))) {
agent_supported = 1;
- if (args->verbose && agent_len)
- fprintf(stderr, "Server version is %.*s\n",
- agent_len, agent_feature);
+ if (agent_len)
+ print_verbose(args, _("Server version is %.*s"),
+ agent_len, agent_feature);
}
+ if (server_supports("deepen-since"))
+ deepen_since_ok = 1;
+ else if (args->deepen_since)
+ die(_("Server does not support --shallow-since"));
+ if (server_supports("deepen-not"))
+ deepen_not_ok = 1;
+ else if (args->deepen_not)
+ die(_("Server does not support --shallow-exclude"));
+ if (!server_supports("deepen-relative") && args->deepen_relative)
+ die(_("Server does not support --deepen"));
if (everything_local(args, &ref, sought, nr_sought)) {
packet_flush(fd[1]);
/* When cloning, it is not unusual to have
* no common commit.
*/
- warning("no common commits");
+ warning(_("no common commits"));
if (args->stateless_rpc)
packet_flush(fd[1]);
- if (args->depth > 0)
+ if (args->deepen)
setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
NULL);
else if (si->nr_ours || si->nr_theirs)
else
alternate_shallow_file = NULL;
if (get_pack(args, fd, pack_lockfile))
- die("git fetch-pack: fetch failed.");
+ die(_("git fetch-pack: fetch failed."));
all_done:
return ref;
int *status;
int i;
- if (args->depth > 0 && alternate_shallow_file) {
+ if (args->deepen && alternate_shallow_file) {
if (*alternate_shallow_file == '\0') { /* --unshallow */
unlink_or_warn(git_path_shallow());
rollback_lock_file(&shallow_lock);
if (!ref) {
packet_flush(fd[1]);
- die("no matching remote head");
+ die(_("no matching remote head"));
}
prepare_shallow_info(&si, shallow);
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
#include "object.h"
#include "tag.h"
+/*
+ * List of all available backends
+ */
+static struct ref_storage_be *refs_backends = &refs_be_files;
+
+static struct ref_storage_be *find_ref_storage_backend(const char *name)
+{
+ struct ref_storage_be *be;
+ for (be = refs_backends; be; be = be->next)
+ if (!strcmp(be->name, name))
+ return be;
+ return NULL;
+}
+
+int ref_storage_backend_exists(const char *name)
+{
+ return find_ref_storage_backend(name) != NULL;
+}
+
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refs
int refname_is_safe(const char *refname)
{
- if (starts_with(refname, "refs/")) {
+ const char *rest;
+
+ if (skip_prefix(refname, "refs/", &rest)) {
char *buf;
int result;
+ size_t restlen = strlen(rest);
+
+ /* rest must not be empty, or start or end with "/" */
+ if (!restlen || *rest == '/' || rest[restlen - 1] == '/')
+ return 0;
- buf = xmalloc(strlen(refname) + 1);
/*
* Does the refname try to escape refs/?
* For example: refs/foo/../bar is safe but refs/foo/../../bar
* is not.
*/
- result = !normalize_path_copy(buf, refname + strlen("refs/"));
+ buf = xmallocz(restlen);
+ result = !normalize_path_copy(buf, rest) && !strcmp(buf, rest);
free(buf);
return result;
}
- while (*refname) {
+
+ do {
if (!isupper(*refname) && *refname != '_')
return 0;
refname++;
- }
+ } while (*refname);
return 1;
}
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
{
char *last_branch = substitute_branch_name(&str, &len);
+ int refs_found = expand_ref(str, len, sha1, ref);
+ free(last_branch);
+ return refs_found;
+ }
+
+ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
+ {
const char **p, *r;
int refs_found = 0;
warning("ignoring broken ref %s.", fullref);
}
}
- free(last_branch);
return refs_found;
}
filename = git_path("%s", pseudoref);
fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
if (fd < 0) {
- strbuf_addf(err, "Could not open '%s' for writing: %s",
+ strbuf_addf(err, "could not open '%s' for writing: %s",
filename, strerror(errno));
return -1;
}
if (read_ref(pseudoref, actual_old_sha1))
die("could not read ref '%s'", pseudoref);
if (hashcmp(actual_old_sha1, old_sha1)) {
- strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
+ strbuf_addf(err, "unexpected sha1 when writing '%s'", pseudoref);
rollback_lock_file(&lock);
goto done;
}
}
if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
- strbuf_addf(err, "Could not write to '%s'", filename);
+ strbuf_addf(err, "could not write to '%s'", filename);
rollback_lock_file(&lock);
goto done;
}
free(transaction);
}
-static struct ref_update *add_update(struct ref_transaction *transaction,
- const char *refname)
+struct ref_update *ref_transaction_add_update(
+ struct ref_transaction *transaction,
+ const char *refname, unsigned int flags,
+ const unsigned char *new_sha1,
+ const unsigned char *old_sha1,
+ const char *msg)
{
- size_t len = strlen(refname) + 1;
- struct ref_update *update = xcalloc(1, sizeof(*update) + len);
+ struct ref_update *update;
+
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: update called for transaction that is not open");
- memcpy((char *)update->refname, refname, len); /* includes NUL */
+ if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF))
+ die("BUG: REF_ISPRUNING set without REF_NODEREF");
+
+ FLEX_ALLOC_STR(update, refname, refname);
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
transaction->updates[transaction->nr++] = update;
+
+ update->flags = flags;
+
+ if (flags & REF_HAVE_NEW)
+ hashcpy(update->new_sha1, new_sha1);
+ if (flags & REF_HAVE_OLD)
+ hashcpy(update->old_sha1, old_sha1);
+ if (msg)
+ update->msg = xstrdup(msg);
return update;
}
unsigned int flags, const char *msg,
struct strbuf *err)
{
- struct ref_update *update;
-
assert(err);
- if (transaction->state != REF_TRANSACTION_OPEN)
- die("BUG: update called for transaction that is not open");
-
- if (new_sha1 && !is_null_sha1(new_sha1) &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- strbuf_addf(err, "refusing to update ref with bad name %s",
+ if ((new_sha1 && !is_null_sha1(new_sha1)) ?
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
+ !refname_is_safe(refname)) {
+ strbuf_addf(err, "refusing to update ref with bad name '%s'",
refname);
return -1;
}
- update = add_update(transaction, refname);
- if (new_sha1) {
- hashcpy(update->new_sha1, new_sha1);
- flags |= REF_HAVE_NEW;
- }
- if (old_sha1) {
- hashcpy(update->old_sha1, old_sha1);
- flags |= REF_HAVE_OLD;
- }
- update->flags = flags;
- if (msg)
- update->msg = xstrdup(msg);
+ flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
+
+ ref_transaction_add_update(transaction, refname, flags,
+ new_sha1, old_sha1, msg);
return 0;
}
flags, NULL, err);
}
+int update_ref_oid(const char *msg, const char *refname,
+ const struct object_id *new_oid, const struct object_id *old_oid,
+ unsigned int flags, enum action_on_err onerr)
+{
+ return update_ref(msg, refname, new_oid ? new_oid->hash : NULL,
+ old_oid ? old_oid->hash : NULL, flags, onerr);
+}
+
int update_ref(const char *msg, const char *refname,
const unsigned char *new_sha1, const unsigned char *old_sha1,
unsigned int flags, enum action_on_err onerr)
/* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
- scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
+ scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
offset = 0;
for (i = 0; i < nr_rules; i++) {
return NULL;
}
-int rename_ref_available(const char *oldname, const char *newname)
+int rename_ref_available(const char *old_refname, const char *new_refname)
{
struct string_list skip = STRING_LIST_INIT_NODUP;
struct strbuf err = STRBUF_INIT;
- int ret;
+ int ok;
- string_list_insert(&skip, oldname);
- ret = !verify_refname_available(newname, NULL, &skip, &err);
- if (!ret)
+ string_list_insert(&skip, old_refname);
+ ok = !verify_refname_available(new_refname, NULL, &skip, &err);
+ if (!ok)
error("%s", err.buf);
string_list_clear(&skip, 0);
strbuf_release(&err);
+ return ok;
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+ struct object_id oid;
+ int flag;
+
+ if (submodule) {
+ if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+ return fn("HEAD", &oid, 0, cb_data);
+
+ return 0;
+ }
+
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+ return fn("HEAD", &oid, flag, cb_data);
+
+ return 0;
+}
+
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+ return head_ref_submodule(NULL, fn, cb_data);
+}
+
+/*
+ * Call fn for each reference in the specified submodule for which the
+ * refname begins with prefix. If trim is non-zero, then trim that
+ * many characters off the beginning of each refname before passing
+ * the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to
+ * include broken references in the iteration. If fn ever returns a
+ * non-zero value, stop the iteration and return that value;
+ * otherwise, return 0.
+ */
+static int do_for_each_ref(const char *submodule, const char *prefix,
+ each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+ struct ref_store *refs = get_ref_store(submodule);
+ struct ref_iterator *iter;
+
+ if (!refs)
+ return 0;
+
+ iter = refs->be->iterator_begin(refs, prefix, flags);
+ iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+ return do_for_each_ref_iterator(iter, fn, cb_data);
+}
+
+int for_each_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+{
+ unsigned int flag = 0;
+
+ if (broken)
+ flag = DO_FOR_EACH_INCLUDE_BROKEN;
+ return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+ each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, git_replace_ref_base, fn,
+ strlen(git_replace_ref_base), 0, cb_data);
+}
+
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+ strbuf_addf(&buf, "%srefs/", get_git_namespace());
+ ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+ strbuf_release(&buf);
return ret;
}
+
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(NULL, "", fn, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+}
+
+/* This function needs to return a meaningful errno on failure */
+static const char *resolve_ref_recursively(struct ref_store *refs,
+ const char *refname,
+ int resolve_flags,
+ unsigned char *sha1, int *flags)
+{
+ static struct strbuf sb_refname = STRBUF_INIT;
+ int unused_flags;
+ int symref_count;
+
+ if (!flags)
+ flags = &unused_flags;
+
+ *flags = 0;
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * missing refs and refs that were present but invalid,
+ * to complain about the latter to stderr.
+ *
+ * We don't know whether the ref exists, so don't set
+ * REF_ISBROKEN yet.
+ */
+ *flags |= REF_BAD_NAME;
+ }
+
+ for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
+ unsigned int read_flags = 0;
+
+ if (refs->be->read_raw_ref(refs, refname,
+ sha1, &sb_refname, &read_flags)) {
+ *flags |= read_flags;
+ if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
+ return NULL;
+ hashclr(sha1);
+ if (*flags & REF_BAD_NAME)
+ *flags |= REF_ISBROKEN;
+ return refname;
+ }
+
+ *flags |= read_flags;
+
+ if (!(read_flags & REF_ISSYMREF)) {
+ if (*flags & REF_BAD_NAME) {
+ hashclr(sha1);
+ *flags |= REF_ISBROKEN;
+ }
+ return refname;
+ }
+
+ refname = sb_refname.buf;
+ if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+ hashclr(sha1);
+ return refname;
+ }
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+ !refname_is_safe(refname)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ *flags |= REF_ISBROKEN | REF_BAD_NAME;
+ }
+ }
+
+ errno = ELOOP;
+ return NULL;
+}
+
+/* backend functions */
+int refs_init_db(struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->init_db(refs, err);
+}
+
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags)
+{
+ return resolve_ref_recursively(get_ref_store(NULL), refname,
+ resolve_flags, sha1, flags);
+}
+
+int resolve_gitlink_ref(const char *submodule, const char *refname,
+ unsigned char *sha1)
+{
+ size_t len = strlen(submodule);
+ struct ref_store *refs;
+ int flags;
+
+ while (len && submodule[len - 1] == '/')
+ len--;
+
+ if (!len)
+ return -1;
+
+ if (submodule[len]) {
+ /* We need to strip off one or more trailing slashes */
+ char *stripped = xmemdupz(submodule, len);
+
+ refs = get_ref_store(stripped);
+ free(stripped);
+ } else {
+ refs = get_ref_store(submodule);
+ }
+
+ if (!refs)
+ return -1;
+
+ if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+ is_null_sha1(sha1))
+ return -1;
+ return 0;
+}
+
+/* A pointer to the ref_store for the main repository: */
+static struct ref_store *main_ref_store;
+
+/* A linked list of ref_stores for submodules: */
+static struct ref_store *submodule_ref_stores;
+
+void base_ref_store_init(struct ref_store *refs,
+ const struct ref_storage_be *be,
+ const char *submodule)
+{
+ refs->be = be;
+ if (!submodule) {
+ if (main_ref_store)
+ die("BUG: main_ref_store initialized twice");
+
+ refs->submodule = "";
+ refs->next = NULL;
+ main_ref_store = refs;
+ } else {
+ if (lookup_ref_store(submodule))
+ die("BUG: ref_store for submodule '%s' initialized twice",
+ submodule);
+
+ refs->submodule = xstrdup(submodule);
+ refs->next = submodule_ref_stores;
+ submodule_ref_stores = refs;
+ }
+}
+
+struct ref_store *ref_store_init(const char *submodule)
+{
+ const char *be_name = "files";
+ struct ref_storage_be *be = find_ref_storage_backend(be_name);
+
+ if (!be)
+ die("BUG: reference backend %s is unknown", be_name);
+
+ if (!submodule || !*submodule)
+ return be->init(NULL);
+ else
+ return be->init(submodule);
+}
+
+struct ref_store *lookup_ref_store(const char *submodule)
+{
+ struct ref_store *refs;
+
+ if (!submodule || !*submodule)
+ return main_ref_store;
+
+ for (refs = submodule_ref_stores; refs; refs = refs->next) {
+ if (!strcmp(submodule, refs->submodule))
+ return refs;
+ }
+
+ return NULL;
+}
+
+struct ref_store *get_ref_store(const char *submodule)
+{
+ struct ref_store *refs;
+
+ if (!submodule || !*submodule) {
+ refs = lookup_ref_store(NULL);
+
+ if (!refs)
+ refs = ref_store_init(NULL);
+ } else {
+ refs = lookup_ref_store(submodule);
+
+ if (!refs) {
+ struct strbuf submodule_sb = STRBUF_INIT;
+
+ strbuf_addstr(&submodule_sb, submodule);
+ if (is_nonbare_repository_dir(&submodule_sb))
+ refs = ref_store_init(submodule);
+ strbuf_release(&submodule_sb);
+ }
+ }
+
+ return refs;
+}
+
+void assert_main_repository(struct ref_store *refs, const char *caller)
+{
+ if (*refs->submodule)
+ die("BUG: %s called for a submodule", caller);
+}
+
+/* backend functions */
+int pack_refs(unsigned int flags)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->pack_refs(refs, flags);
+}
+
+int peel_ref(const char *refname, unsigned char *sha1)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->peel_ref(refs, refname, sha1);
+}
+
+int create_symref(const char *ref_target, const char *refs_heads_master,
+ const char *logmsg)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->create_symref(refs, ref_target, refs_heads_master,
+ logmsg);
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->transaction_commit(refs, transaction, err);
+}
+
+int verify_refname_available(const char *refname,
+ const struct string_list *extra,
+ const struct string_list *skip,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+}
+
+int for_each_reflog(each_ref_fn fn, void *cb_data)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+ struct ref_iterator *iter;
+
+ iter = refs->be->reflog_iterator_begin(refs);
+
+ return do_for_each_ref_iterator(iter, fn, cb_data);
+}
+
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->for_each_reflog_ent_reverse(refs, refname,
+ fn, cb_data);
+}
+
+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
+int reflog_exists(const char *refname)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->reflog_exists(refs, refname);
+}
+
+int safe_create_reflog(const char *refname, int force_create,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->create_reflog(refs, refname, force_create, err);
+}
+
+int delete_reflog(const char *refname)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->delete_reflog(refs, refname);
+}
+
+int reflog_expire(const char *refname, const unsigned char *sha1,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->reflog_expire(refs, refname, sha1, flags,
+ prepare_fn, should_prune_fn,
+ cleanup_fn, policy_cb_data);
+}
+
+int initial_ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->initial_transaction_commit(refs, transaction, err);
+}
+
+int delete_refs(struct string_list *refnames, unsigned int flags)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->delete_refs(refs, refnames, flags);
+}
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->rename_ref(refs, oldref, newref, logmsg);
+}
#define RESOLVE_REF_NO_RECURSE 0x02
#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
-extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags);
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
-extern char *resolve_refdup(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags);
+char *resolve_refdup(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
-extern int read_ref_full(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags);
-extern int read_ref(const char *refname, unsigned char *sha1);
+int read_ref_full(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
+int read_ref(const char *refname, unsigned char *sha1);
-extern int ref_exists(const char *refname);
+int ref_exists(const char *refname);
-extern int is_branch(const char *refname);
+int is_branch(const char *refname);
+
+extern int refs_init_db(struct strbuf *err);
/*
* If refname is a non-symbolic reference that refers to a tag object,
* Symbolic references are considered unpeelable, even if they
* ultimately resolve to a peelable tag.
*/
-extern int peel_ref(const char *refname, unsigned char *sha1);
+int peel_ref(const char *refname, unsigned char *sha1);
/**
- * Resolve refname in the nested "gitlink" repository that is located
- * at path. If the resolution is successful, return 0 and set sha1 to
- * the name of the object; otherwise, return a non-zero value.
+ * Resolve refname in the nested "gitlink" repository in the specified
+ * submodule (which must be non-NULL). If the resolution is
+ * successful, return 0 and set sha1 to the name of the object;
+ * otherwise, return a non-zero value.
*/
-extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
+int resolve_gitlink_ref(const char *submodule, const char *refname,
+ unsigned char *sha1);
/*
* Return true iff abbrev_name is a possible abbreviation for
* full_name according to the rules defined by ref_rev_parse_rules in
* refs.c.
*/
-extern int refname_match(const char *abbrev_name, const char *full_name);
+int refname_match(const char *abbrev_name, const char *full_name);
-extern int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);
-extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
-extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
++int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);
+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
+int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
/*
* A ref_transaction represents a collection of ref updates
* If this succeeds, the ref updates will have taken place and
* the transaction cannot be rolled back.
*
+ * - Instead of `ref_transaction_commit`, use
+ * `initial_ref_transaction_commit()` if the ref database is known
+ * to be empty (e.g. during clone). This is likely to be much
+ * faster.
+ *
* - At any time call `ref_transaction_free()` to discard the
* transaction and free associated resources. In particular,
* this rolls back the transaction if it has not been
*
* The message is appended to err without first clearing err.
* err will not be '\n' terminated.
+ *
+ * Caveats
+ * -------
+ *
+ * Note that no locks are taken, and no refs are read, until
+ * `ref_transaction_commit` is called. So `ref_transaction_verify`
+ * won't report a verification failure until the commit is attempted.
*/
struct ref_transaction;
/*
- * Bit values set in the flags argument passed to each_ref_fn():
+ * Bit values set in the flags argument passed to each_ref_fn() and
+ * stored in ref_iterator::flags. Other bits are for internal use
+ * only:
*/
/* Reference is a symbolic reference. */
* modifies the reference also returns a nonzero value to immediately
* stop the iteration.
*/
-extern int head_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
-extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken);
-extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
-extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
-extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
-
-extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+int head_ref(each_ref_fn fn, void *cb_data);
+int for_each_ref(each_ref_fn fn, void *cb_data);
+int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
+ unsigned int broken);
+int for_each_tag_ref(each_ref_fn fn, void *cb_data);
+int for_each_branch_ref(each_ref_fn fn, void *cb_data);
+int for_each_remote_ref(each_ref_fn fn, void *cb_data);
+int for_each_replace_ref(each_ref_fn fn, void *cb_data);
+int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
+int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+ const char *prefix, void *cb_data);
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+int for_each_ref_submodule(const char *submodule,
+ each_ref_fn fn, void *cb_data);
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
each_ref_fn fn, void *cb_data);
-extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+int for_each_tag_ref_submodule(const char *submodule,
+ each_ref_fn fn, void *cb_data);
+int for_each_branch_ref_submodule(const char *submodule,
+ each_ref_fn fn, void *cb_data);
+int for_each_remote_ref_submodule(const char *submodule,
+ each_ref_fn fn, void *cb_data);
-extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
-extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
/* can be used to learn about broken ref and symref */
-extern int for_each_rawref(each_ref_fn fn, void *cb_data);
+int for_each_rawref(each_ref_fn fn, void *cb_data);
static inline const char *has_glob_specials(const char *pattern)
{
return strpbrk(pattern, "?*[");
}
-extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
-extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames);
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
+void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
+ const struct string_list *refnames);
/*
* Flags for controlling behaviour of pack_refs()
int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
/** Reads log for the value of ref during at_time. **/
-extern int read_ref_at(const char *refname, unsigned int flags,
- unsigned long at_time, int cnt,
- unsigned char *sha1, char **msg,
- unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+int read_ref_at(const char *refname, unsigned int flags,
+ unsigned long at_time, int cnt,
+ unsigned char *sha1, char **msg,
+ unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
/** Check if a particular reflog exists */
-extern int reflog_exists(const char *refname);
+int reflog_exists(const char *refname);
/*
* Delete the specified reference. If old_sha1 is non-NULL, then
* exists, regardless of its old value. It is an error for old_sha1 to
* be NULL_SHA1. flags is passed through to ref_transaction_delete().
*/
-extern int delete_ref(const char *refname, const unsigned char *old_sha1,
- unsigned int flags);
+int delete_ref(const char *refname, const unsigned char *old_sha1,
+ unsigned int flags);
/*
* Delete the specified references. If there are any problems, emit
* errors but attempt to keep going (i.e., the deletes are not done in
- * an all-or-nothing transaction).
+ * an all-or-nothing transaction). flags is passed through to
+ * ref_transaction_delete().
*/
-extern int delete_refs(struct string_list *refnames);
+int delete_refs(struct string_list *refnames, unsigned int flags);
/** Delete a reflog */
-extern int delete_reflog(const char *refname);
+int delete_reflog(const char *refname);
/* iterate over reflog entries */
-typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
+typedef int each_reflog_ent_fn(
+ unsigned char *old_sha1, unsigned char *new_sha1,
+ const char *committer, unsigned long timestamp,
+ int tz, const char *msg, void *cb_data);
+
int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
* Calls the specified function for each reflog file until it returns nonzero,
* and returns the value
*/
-extern int for_each_reflog(each_ref_fn, void *);
+int for_each_reflog(each_ref_fn fn, void *cb_data);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
* allow a single "*" wildcard character in the refspec. No leading or
* repeated slashes are accepted.
*/
-extern int check_refname_format(const char *refname, int flags);
+int check_refname_format(const char *refname, int flags);
-extern const char *prettify_refname(const char *refname);
+const char *prettify_refname(const char *refname);
-extern char *shorten_unambiguous_ref(const char *refname, int strict);
+char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
-extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
+int rename_ref(const char *oldref, const char *newref, const char *logmsg);
+
+int create_symref(const char *refname, const char *target, const char *logmsg);
-extern int create_symref(const char *refname, const char *target, const char *logmsg);
+/*
+ * Update HEAD of the specified gitdir.
+ * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but
+ * this can update the main working tree's HEAD regardless of where
+ * $GIT_DIR points to.
+ * Return 0 if successful, non-zero otherwise.
+ * */
+int set_worktree_head_symref(const char *gitdir, const char *target);
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
* msg -- a message describing the change (for the reflog).
*
* err -- a strbuf for receiving a description of any error that
- * might have occured.
+ * might have occurred.
*
* The functions make internal copies of refname and msg, so the
* caller retains ownership of these parameters.
int update_ref(const char *msg, const char *refname,
const unsigned char *new_sha1, const unsigned char *old_sha1,
unsigned int flags, enum action_on_err onerr);
+int update_ref_oid(const char *msg, const char *refname,
+ const struct object_id *new_oid, const struct object_id *old_oid,
+ unsigned int flags, enum action_on_err onerr);
-extern int parse_hide_refs_config(const char *var, const char *value, const char *);
+int parse_hide_refs_config(const char *var, const char *value, const char *);
/*
* Check whether a ref is hidden. If no namespace is set, both the first and
* the ref is outside that namespace, the first parameter is NULL. The second
* parameter always points to the full ref name.
*/
-extern int ref_is_hidden(const char *, const char *);
+int ref_is_hidden(const char *, const char *);
enum ref_type {
REF_TYPE_PER_WORKTREE,
* enum expire_reflog_flags. The three function pointers are described
* above. On success, return zero.
*/
-extern int reflog_expire(const char *refname, const unsigned char *sha1,
- unsigned int flags,
- reflog_expiry_prepare_fn prepare_fn,
- reflog_expiry_should_prune_fn should_prune_fn,
- reflog_expiry_cleanup_fn cleanup_fn,
- void *policy_cb_data);
+int reflog_expire(const char *refname, const unsigned char *sha1,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data);
+
+int ref_storage_backend_exists(const char *name);
#endif /* REFS_H */
struct options {
int verbosity;
unsigned long depth;
+ char *deepen_since;
+ struct string_list deepen_not;
unsigned progress : 1,
check_self_contained_and_connected : 1,
cloning : 1,
dry_run : 1,
thin : 1,
/* One of the SEND_PACK_PUSH_CERT_* constants. */
- push_cert : 2;
+ push_cert : 2,
+ deepen_relative : 1;
};
static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP;
options.depth = v;
return 0;
}
+ else if (!strcmp(name, "deepen-since")) {
+ options.deepen_since = xstrdup(value);
+ return 0;
+ }
+ else if (!strcmp(name, "deepen-not")) {
+ string_list_append(&options.deepen_not, value);
+ return 0;
+ }
+ else if (!strcmp(name, "deepen-relative")) {
+ if (!strcmp(value, "true"))
+ options.deepen_relative = 1;
+ else if (!strcmp(value, "false"))
+ options.deepen_relative = 0;
+ else
+ return -1;
+ return 0;
+ }
else if (!strcmp(name, "followtags")) {
if (!strcmp(value, "true"))
options.followtags = 1;
else
return -1;
return 0;
+
+#if LIBCURL_VERSION_NUM >= 0x070a08
+ } else if (!strcmp(name, "family")) {
+ if (!strcmp(value, "ipv4"))
+ git_curl_ipresolve = CURL_IPRESOLVE_V4;
+ else if (!strcmp(value, "ipv6"))
+ git_curl_ipresolve = CURL_IPRESOLVE_V6;
+ else if (!strcmp(value, "all"))
+ git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
+ else
+ return -1;
+ return 0;
+#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
} else {
return 1 /* unsupported */;
}
err = run_one_slot(slot, results);
if (err != HTTP_OK && err != HTTP_REAUTH) {
- error("RPC failed; result=%d, HTTP code = %ld",
- results->curl_result, results->http_code);
+ struct strbuf msg = STRBUF_INIT;
+ if (results->http_code && results->http_code != 200)
+ strbuf_addf(&msg, "HTTP %ld", results->http_code);
+ if (results->curl_result != CURLE_OK) {
+ if (msg.len)
+ strbuf_addch(&msg, ' ');
+ strbuf_addf(&msg, "curl %d", results->curl_result);
+ if (curl_errorstr[0]) {
+ strbuf_addch(&msg, ' ');
+ strbuf_addstr(&msg, curl_errorstr);
+ }
+ }
+ error("RPC failed; %s", msg.buf);
+ strbuf_release(&msg);
}
return err;
static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
{
struct active_request_slot *slot;
- struct curl_slist *headers = NULL;
+ struct curl_slist *headers = http_copy_default_headers();
struct strbuf buf = STRBUF_INIT;
int err;
static int post_rpc(struct rpc_state *rpc)
{
struct active_request_slot *slot;
- struct curl_slist *headers = NULL;
+ struct curl_slist *headers = http_copy_default_headers();
int use_gzip = rpc->gzip_request;
char *gzip_body = NULL;
size_t gzip_size = 0;
static int fetch_dumb(int nr_heads, struct ref **to_fetch)
{
struct walker *walker;
- char **targets = xmalloc(nr_heads * sizeof(char*));
+ char **targets;
int ret, i;
- if (options.depth)
- die("dumb http transport does not support --depth");
+ ALLOC_ARRAY(targets, nr_heads);
+ if (options.depth || options.deepen_since)
+ die("dumb http transport does not support shallow capabilities");
for (i = 0; i < nr_heads; i++)
targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
{
struct rpc_state rpc;
struct strbuf preamble = STRBUF_INIT;
- char *depth_arg = NULL;
- int argc = 0, i, err;
- const char *argv[17];
-
- argv[argc++] = "fetch-pack";
- argv[argc++] = "--stateless-rpc";
- argv[argc++] = "--stdin";
- argv[argc++] = "--lock-pack";
+ int i, err;
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ argv_array_pushl(&args, "fetch-pack", "--stateless-rpc",
+ "--stdin", "--lock-pack", NULL);
if (options.followtags)
- argv[argc++] = "--include-tag";
+ argv_array_push(&args, "--include-tag");
if (options.thin)
- argv[argc++] = "--thin";
- if (options.verbosity >= 3) {
- argv[argc++] = "-v";
- argv[argc++] = "-v";
- }
+ argv_array_push(&args, "--thin");
+ if (options.verbosity >= 3)
+ argv_array_pushl(&args, "-v", "-v", NULL);
if (options.check_self_contained_and_connected)
- argv[argc++] = "--check-self-contained-and-connected";
+ argv_array_push(&args, "--check-self-contained-and-connected");
if (options.cloning)
- argv[argc++] = "--cloning";
+ argv_array_push(&args, "--cloning");
if (options.update_shallow)
- argv[argc++] = "--update-shallow";
+ argv_array_push(&args, "--update-shallow");
if (!options.progress)
- argv[argc++] = "--no-progress";
- if (options.depth) {
- struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "--depth=%lu", options.depth);
- depth_arg = strbuf_detach(&buf, NULL);
- argv[argc++] = depth_arg;
- }
- argv[argc++] = url.buf;
- argv[argc++] = NULL;
+ argv_array_push(&args, "--no-progress");
+ if (options.depth)
+ argv_array_pushf(&args, "--depth=%lu", options.depth);
+ if (options.deepen_since)
+ argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since);
+ for (i = 0; i < options.deepen_not.nr; i++)
+ argv_array_pushf(&args, "--shallow-exclude=%s",
+ options.deepen_not.items[i].string);
+ if (options.deepen_relative && options.depth)
+ argv_array_push(&args, "--deepen-relative");
+ argv_array_push(&args, url.buf);
for (i = 0; i < nr_heads; i++) {
struct ref *ref = to_fetch[i];
memset(&rpc, 0, sizeof(rpc));
rpc.service_name = "git-upload-pack",
- rpc.argv = argv;
+ rpc.argv = args.argv;
rpc.stdin_preamble = &preamble;
rpc.gzip_request = 1;
write_or_die(1, rpc.result.buf, rpc.result.len);
strbuf_release(&rpc.result);
strbuf_release(&preamble);
- free(depth_arg);
+ argv_array_clear(&args);
return err;
}
static int push_dav(int nr_spec, char **specs)
{
- const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
- int argc = 0, i;
+ struct child_process child = CHILD_PROCESS_INIT;
+ size_t i;
- argv[argc++] = "http-push";
- argv[argc++] = "--helper-status";
+ child.git_cmd = 1;
+ argv_array_push(&child.args, "http-push");
+ argv_array_push(&child.args, "--helper-status");
if (options.dry_run)
- argv[argc++] = "--dry-run";
+ argv_array_push(&child.args, "--dry-run");
if (options.verbosity > 1)
- argv[argc++] = "--verbose";
- argv[argc++] = url.buf;
+ argv_array_push(&child.args, "--verbose");
+ argv_array_push(&child.args, url.buf);
for (i = 0; i < nr_spec; i++)
- argv[argc++] = specs[i];
- argv[argc++] = NULL;
+ argv_array_push(&child.args, specs[i]);
- if (run_command_v_opt(argv, RUN_GIT_CMD))
- die("git-%s failed", argv[0]);
- free(argv);
+ if (run_command(&child))
+ die("git-http-push failed");
return 0;
}
free(specs);
}
-int main(int argc, const char **argv)
+int cmd_main(int argc, const char **argv)
{
struct strbuf buf = STRBUF_INIT;
int nongit;
- git_setup_gettext();
-
- git_extract_argv0_path(argv[0]);
setup_git_directory_gently(&nongit);
if (argc < 2) {
error("remote-curl: usage: git remote-curl <remote> [<url>]");
options.verbosity = 1;
options.progress = !!isatty(2);
options.thin = 1;
+ string_list_init(&options.deepen_not, 1);
remote = remote_get(argv[1]);
#include "diff.h"
#include "revision.h"
#include "commit-slab.h"
+ #include "revision.h"
+ #include "list-objects.h"
static int is_shallow = -1;
static struct stat_validity shallow_stat;
return result;
}
+ static void show_commit(struct commit *commit, void *data)
+ {
+ commit_list_insert(commit, data);
+ }
+
+ /*
+ * Given rev-list arguments, run rev-list. All reachable commits
+ * except border ones are marked with not_shallow_flag. Border commits
+ * are marked with shallow_flag. The list of border/shallow commits
+ * are also returned.
+ */
+ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
+ int shallow_flag,
+ int not_shallow_flag)
+ {
+ struct commit_list *result = NULL, *p;
+ struct commit_list *not_shallow_list = NULL;
+ struct rev_info revs;
+ int both_flags = shallow_flag | not_shallow_flag;
+
+ /*
+ * SHALLOW (excluded) and NOT_SHALLOW (included) should not be
+ * set at this point. But better be safe than sorry.
+ */
+ clear_object_flags(both_flags);
+
+ is_repository_shallow(); /* make sure shallows are read */
+
+ init_revisions(&revs, NULL);
+ save_commit_buffer = 0;
+ setup_revisions(ac, av, &revs, NULL);
+
+ if (prepare_revision_walk(&revs))
+ die("revision walk setup failed");
+ traverse_commit_list(&revs, show_commit, NULL, ¬_shallow_list);
+
+ /* Mark all reachable commits as NOT_SHALLOW */
+ for (p = not_shallow_list; p; p = p->next)
+ p->item->object.flags |= not_shallow_flag;
+
+ /*
+ * mark border commits SHALLOW + NOT_SHALLOW.
+ * We cannot clear NOT_SHALLOW right now. Imagine border
+ * commit A is processed first, then commit B, whose parent is
+ * A, later. If NOT_SHALLOW on A is cleared at step 1, B
+ * itself is considered border at step 2, which is incorrect.
+ */
+ for (p = not_shallow_list; p; p = p->next) {
+ struct commit *c = p->item;
+ struct commit_list *parent;
+
+ if (parse_commit(c))
+ die("unable to parse commit %s",
+ oid_to_hex(&c->object.oid));
+
+ for (parent = c->parents; parent; parent = parent->next)
+ if (!(parent->item->object.flags & not_shallow_flag)) {
+ c->object.flags |= shallow_flag;
+ commit_list_insert(c, &result);
+ break;
+ }
+ }
+ free_commit_list(not_shallow_list);
+
+ /*
+ * Now we can clean up NOT_SHALLOW on border commits. Having
+ * both flags set can confuse the caller.
+ */
+ for (p = result; p; p = p->next) {
+ struct object *o = &p->item->object;
+ if ((o->flags & both_flags) == both_flags)
+ o->flags &= ~not_shallow_flag;
+ }
+ return result;
+ }
+
static void check_shallow_file_for_update(void)
{
if (is_shallow == -1)
info->shallow = sa;
if (!sa)
return;
- info->ours = xmalloc(sizeof(*info->ours) * sa->nr);
- info->theirs = xmalloc(sizeof(*info->theirs) * sa->nr);
+ ALLOC_ARRAY(info->ours, sa->nr);
+ ALLOC_ARRAY(info->theirs, sa->nr);
for (i = 0; i < sa->nr; i++) {
if (has_sha1_file(sa->sha1[i])) {
struct commit_graft *graft;
unsigned int i, nr;
struct commit_list *head = NULL;
int bitmap_nr = (info->nr_bits + 31) / 32;
- int bitmap_size = bitmap_nr * sizeof(uint32_t);
+ size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr);
uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
uint32_t *bitmap = paint_alloc(info);
struct commit *c = lookup_commit_reference_gently(sha1, 1);
struct paint_info pi;
trace_printf_key(&trace_shallow, "shallow: assign_shallow_commits_to_refs\n");
- shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
+ ALLOC_ARRAY(shallow, info->nr_ours + info->nr_theirs);
for (i = 0; i < info->nr_ours; i++)
shallow[nr_shallow++] = info->ours[i];
for (i = 0; i < info->nr_theirs; i++)
test_expect_success 'pull in shallow repo with missing merge base' '
(
cd shallow &&
- test_must_fail git pull --depth 4 .. A
+ git fetch --depth 4 .. A
+ test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
)
'
test_expect_success 'clone shallow object count' '
(
cd shallow &&
+ git prune &&
git count-objects -v
) > count.shallow &&
- grep "^count: 55" count.shallow
+ grep "^count: 54" count.shallow
'
test_expect_success 'fetch --no-shallow on full repo' '
git fsck
)
'
+
+test_expect_success 'fetch-pack can fetch a raw sha1' '
+ git init hidden &&
+ (
+ cd hidden &&
+ test_commit 1 &&
+ test_commit 2 &&
+ git update-ref refs/hidden/one HEAD^ &&
+ git config transfer.hiderefs refs/hidden &&
+ git config uploadpack.allowtipsha1inwant true
+ ) &&
+ git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
+'
+
check_prot_path () {
cat >expected <<-EOF &&
Diag: url=$1
}
check_prot_host_port_path () {
- local diagport
case "$2" in
*ssh*)
pp=ssh
check_prot_path c:repo file c:repo
'
+ test_expect_success 'clone shallow since ...' '
+ test_create_repo shallow-since &&
+ (
+ cd shallow-since &&
+ GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+ GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+ GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+ git clone --shallow-since "300000000 +0700" "file://$(pwd)/." ../shallow11 &&
+ git -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&
+ echo three >expected &&
+ test_cmp expected actual
+ )
+ '
+
+ test_expect_success 'fetch shallow since ...' '
+ git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
+ git -C shallow11 log --pretty=tformat:%s origin/master >actual &&
+ cat >expected <<-\EOF &&
+ three
+ two
+ EOF
+ test_cmp expected actual
+ '
+
+ test_expect_success 'shallow clone exclude tag two' '
+ test_create_repo shallow-exclude &&
+ (
+ cd shallow-exclude &&
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ git clone --shallow-exclude two "file://$(pwd)/." ../shallow12 &&
+ git -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&
+ echo three >expected &&
+ test_cmp expected actual
+ )
+ '
+
+ test_expect_success 'fetch exclude tag one' '
+ git -C shallow12 fetch --shallow-exclude one origin &&
+ git -C shallow12 log --pretty=tformat:%s origin/master >actual &&
+ test_write_lines three two >expected &&
+ test_cmp expected actual
+ '
+
+ test_expect_success 'fetching deepen' '
+ test_create_repo shallow-deepen &&
+ (
+ cd shallow-deepen &&
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ git clone --depth 1 "file://$(pwd)/." deepen &&
+ test_commit four &&
+ git -C deepen log --pretty=tformat:%s master >actual &&
+ echo three >expected &&
+ test_cmp expected actual &&
+ git -C deepen fetch --deepen=1 &&
+ git -C deepen log --pretty=tformat:%s origin/master >actual &&
+ cat >expected <<-\EOF &&
+ four
+ three
+ two
+ EOF
+ test_cmp expected actual
+ )
+ '
+
test_done
TRANS_OPT_THIN,
TRANS_OPT_KEEP,
TRANS_OPT_FOLLOWTAGS,
+ TRANS_OPT_DEEPEN_RELATIVE
};
+ static int strbuf_set_helper_option(struct helper_data *data,
+ struct strbuf *buf)
+ {
+ int ret;
+
+ sendline(data, buf);
+ if (recvline(data, buf))
+ exit(128);
+
+ if (!strcmp(buf->buf, "ok"))
+ ret = 0;
+ else if (starts_with(buf->buf, "error"))
+ ret = -1;
+ else if (!strcmp(buf->buf, "unsupported"))
+ ret = 1;
+ else {
+ warning("%s unexpectedly said: '%s'", data->name, buf->buf);
+ ret = 1;
+ }
+ return ret;
+ }
+
+ static int string_list_set_helper_option(struct helper_data *data,
+ const char *name,
+ struct string_list *list)
+ {
+ struct strbuf buf = STRBUF_INIT;
+ int i, ret = 0;
+
+ for (i = 0; i < list->nr; i++) {
+ strbuf_addf(&buf, "option %s ", name);
+ quote_c_style(list->items[i].string, &buf, NULL, 0);
+ strbuf_addch(&buf, '\n');
+
+ if ((ret = strbuf_set_helper_option(data, &buf)))
+ break;
+ strbuf_reset(&buf);
+ }
+ strbuf_release(&buf);
+ return ret;
+ }
+
static int set_helper_option(struct transport *transport,
const char *name, const char *value)
{
if (!data->option)
return 1;
+ if (!strcmp(name, "deepen-not"))
+ return string_list_set_helper_option(data, name,
+ (struct string_list *)value);
+
for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
if (!strcmp(name, unsupported_options[i]))
return 1;
quote_c_style(value, &buf, NULL, 0);
strbuf_addch(&buf, '\n');
- sendline(data, &buf);
- if (recvline(data, &buf))
- exit(128);
-
- if (!strcmp(buf.buf, "ok"))
- ret = 0;
- else if (starts_with(buf.buf, "error")) {
- ret = -1;
- } else if (!strcmp(buf.buf, "unsupported"))
- ret = 1;
- else {
- warning("%s unexpectedly said: '%s'", data->name, buf.buf);
- ret = 1;
- }
+ ret = strbuf_set_helper_option(data, &buf);
strbuf_release(&buf);
return ret;
}
if (n >= sizeof(buf))
die("impossibly large verbosity value");
set_helper_option(t, "verbosity", buf);
+
+ switch (t->family) {
+ case TRANSPORT_FAMILY_ALL:
+ /*
+ * this is already the default,
+ * do not break old remote helpers by setting "all" here
+ */
+ break;
+ case TRANSPORT_FAMILY_IPV4:
+ set_helper_option(t, "family", "ipv4");
+ break;
+ case TRANSPORT_FAMILY_IPV6:
+ set_helper_option(t, "family", "ipv6");
+ break;
+ }
}
static int release_helper(struct transport *transport)
(*tail)->status |= REF_STATUS_UPTODATE;
if (read_ref((*tail)->name,
(*tail)->old_oid.hash) < 0)
- die(N_("Could not read ref %s"),
+ die(_("Could not read ref %s"),
(*tail)->name);
}
}
}
/* Stream state: More data may be coming in this direction. */
-#define SSTATE_TRANSFERING 0
+#define SSTATE_TRANSFERRING 0
/*
* Stream state: No more data coming in this direction, flushing rest of
* data.
/* Stream state: Transfer in this direction finished. */
#define SSTATE_FINISHED 2
-#define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERING)
+#define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERRING)
#define STATE_NEEDS_WRITING(state) ((state) <= SSTATE_FLUSHING)
#define STATE_NEEDS_CLOSING(state) ((state) == SSTATE_FLUSHING)
}
/*
- * Tries to read read data from source into buffer. If buffer is full,
+ * Tries to read data from source into buffer. If buffer is full,
* no data is read. Returns 0 on success, -1 on error.
*/
static int udt_do_read(struct unidirectional_transfer *t)
bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse);
if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
errno != EINTR) {
- error("read(%s) failed: %s", t->src_name, strerror(errno));
+ error_errno("read(%s) failed", t->src_name);
return -1;
} else if (bytes == 0) {
transfer_debug("%s EOF (with %i bytes in buffer)",
transfer_debug("%s is writable", t->dest_name);
bytes = xwrite(t->dest, t->buf, t->bufuse);
if (bytes < 0 && errno != EWOULDBLOCK) {
- error("write(%s) failed: %s", t->dest_name, strerror(errno));
+ error_errno("write(%s) failed", t->dest_name);
return -1;
} else if (bytes > 0) {
t->bufuse -= bytes;
{
int tret;
if (waitpid(pid, &tret, 0) < 0) {
- error("%s process failed to wait: %s", name, strerror(errno));
+ error_errno("%s process failed to wait", name);
return 1;
}
if (!WIFEXITED(tret) || WEXITSTATUS(tret)) {
state.ptg.dest = 1;
state.ptg.src_is_sock = (input == output);
state.ptg.dest_is_sock = 0;
- state.ptg.state = SSTATE_TRANSFERING;
+ state.ptg.state = SSTATE_TRANSFERRING;
state.ptg.bufuse = 0;
state.ptg.src_name = "remote input";
state.ptg.dest_name = "stdout";
state.gtp.dest = output;
state.gtp.src_is_sock = 0;
state.gtp.dest_is_sock = (input == output);
- state.gtp.state = SSTATE_TRANSFERING;
+ state.gtp.state = SSTATE_TRANSFERRING;
state.gtp.bufuse = 0;
state.gtp.src_name = "stdin";
state.gtp.dest_name = "remote output";
localname + 11, transport->remote->name,
remotename);
else
- printf("Would set upstream of '%s' to '%s' of '%s'\n",
+ printf(_("Would set upstream of '%s' to '%s' of '%s'\n"),
localname + 11, remotename + 11,
transport->remote->name);
}
char *end;
opts->depth = strtol(value, &end, 0);
if (*end)
- die("transport: invalid depth option '%s'", value);
+ die(_("transport: invalid depth option '%s'"), value);
}
return 0;
+ } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {
+ opts->deepen_since = value;
+ return 0;
+ } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
+ opts->deepen_not = (const struct string_list *)value;
+ return 0;
+ } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
+ opts->deepen_relative = !!value;
+ return 0;
}
return 1;
}
if (data->conn)
return 0;
+ switch (transport->family) {
+ case TRANSPORT_FAMILY_ALL: break;
+ case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
+ case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
+ }
+
data->conn = git_connect(data->fd, transport->url,
for_push ? data->options.receivepack :
data->options.uploadpack,
args.quiet = (transport->verbose < 0);
args.no_progress = !transport->progress;
args.depth = data->options.depth;
+ args.deepen_since = data->options.deepen_since;
+ args.deepen_not = data->options.deepen_not;
+ args.deepen_relative = data->options.deepen_relative;
args.check_self_contained_and_connected =
data->options.check_self_contained_and_connected;
args.cloning = transport->cloning;
}
}
-static const char *status_abbrev(unsigned char sha1[20])
-{
- return find_unique_abbrev(sha1, DEFAULT_ABBREV);
-}
-
static void print_ok_ref_status(struct ref *ref, int porcelain)
{
if (ref->deletion)
char type;
const char *msg;
- strbuf_addstr(&quickref, status_abbrev(ref->old_oid.hash));
+ strbuf_add_unique_abbrev(&quickref, ref->old_oid.hash,
+ DEFAULT_ABBREV);
if (ref->forced_update) {
strbuf_addstr(&quickref, "...");
type = '+';
type = ' ';
msg = NULL;
}
- strbuf_addstr(&quickref, status_abbrev(ref->new_oid.hash));
+ strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash,
+ DEFAULT_ABBREV);
print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
strbuf_release(&quickref);
static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
{
- if (!count)
- fprintf(porcelain ? stdout : stderr, "To %s\n", dest);
+ if (!count) {
+ char *url = transport_anonymize_url(dest);
+ fprintf(porcelain ? stdout : stderr, "To %s\n", url);
+ free(url);
+ }
switch(ref->status) {
case REF_STATUS_NONE:
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
+ args.push_options = transport->push_options;
args.url = transport->url;
if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
struct git_transport_data *data;
if (!transport->smart_options)
- die("Bug detected: Taking over transport requires non-NULL "
+ die("BUG: taking over transport requires non-NULL "
"smart_options field.");
data = xcalloc(1, sizeof(*data));
|| starts_with(url, "file://")
|| starts_with(url, "git://")
|| starts_with(url, "ssh://")
- || starts_with(url, "git+ssh://")
- || starts_with(url, "ssh+git://")) {
+ || starts_with(url, "git+ssh://") /* deprecated - do not use */
+ || starts_with(url, "ssh+git://") /* deprecated - do not use */
+ ) {
/*
* These are builtin smart transports; "allowed" transports
* will be checked individually in git_connect.
{
int i;
- fprintf(stderr, "The following submodule paths contain changes that can\n"
- "not be found on any remote:\n");
+ fprintf(stderr, _("The following submodule paths contain changes that can\n"
+ "not be found on any remote:\n"));
for (i = 0; i < needs_pushing->nr; i++)
- printf(" %s\n", needs_pushing->items[i].string);
- fprintf(stderr, "\nPlease try\n\n"
- " git push --recurse-submodules=on-demand\n\n"
- "or cd to the path and use\n\n"
- " git push\n\n"
- "to push them to a remote.\n\n");
+ fprintf(stderr, " %s\n", needs_pushing->items[i].string);
+ fprintf(stderr, _("\nPlease try\n\n"
+ " git push --recurse-submodules=on-demand\n\n"
+ "or cd to the path and use\n\n"
+ " git push\n\n"
+ "to push them to a remote.\n\n"));
string_list_clear(needs_pushing, 0);
- die("Aborting.");
+ die(_("Aborting."));
}
static int run_pre_push_hook(struct transport *transport,
* This condition shouldn't be met in a non-deepening fetch
* (see builtin/fetch.c:quickfetch()).
*/
- heads = xmalloc(nr_refs * sizeof(*heads));
+ ALLOC_ARRAY(heads, nr_refs);
for (rm = refs; rm; rm = rm->next)
heads[nr_heads++] = rm;
}
*/
char *transport_anonymize_url(const char *url)
{
- char *anon_url, *scheme_prefix, *anon_part;
+ char *scheme_prefix, *anon_part;
size_t anon_len, prefix_len = 0;
anon_part = strchr(url, '@');
goto literal_copy;
prefix_len = scheme_prefix - url + 3;
}
- anon_url = xcalloc(1, 1 + prefix_len + anon_len);
- memcpy(anon_url, url, prefix_len);
- memcpy(anon_url + prefix_len, anon_part, anon_len);
- return anon_url;
+ return xstrfmt("%.*s%.*s", (int)prefix_len, url,
+ (int)anon_len, anon_part);
literal_copy:
return xstrdup(url);
}
#include "run-command.h"
#include "remote.h"
+ struct string_list;
+
struct git_transport_options {
unsigned thin : 1;
unsigned keep : 1;
unsigned check_self_contained_and_connected : 1;
unsigned self_contained_and_connected : 1;
unsigned update_shallow : 1;
+ unsigned deepen_relative : 1;
int depth;
+ const char *deepen_since;
+ const struct string_list *deepen_not;
const char *uploadpack;
const char *receivepack;
struct push_cas_option *cas;
};
+enum transport_family {
+ TRANSPORT_FAMILY_ALL = 0,
+ TRANSPORT_FAMILY_IPV4,
+ TRANSPORT_FAMILY_IPV6
+};
+
struct transport {
struct remote *remote;
const char *url;
*/
unsigned cloning : 1;
+ /*
+ * These strings will be passed to the {pre, post}-receive hook,
+ * on the remote side, if both sides support the push options capability.
+ */
+ const struct string_list *push_options;
+
/**
* Returns 0 if successful, positive if the option is not
* recognized or is inapplicable, and negative if the option
* actually turns out to be smart.
*/
struct git_transport_options *smart_options;
+
+ enum transport_family family;
};
#define TRANSPORT_PUSH_ALL 1
#define TRANSPORT_PUSH_CERT_ALWAYS 2048
#define TRANSPORT_PUSH_CERT_IF_ASKED 4096
#define TRANSPORT_PUSH_ATOMIC 8192
+#define TRANSPORT_PUSH_OPTIONS 16384
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
/* Limit the depth of the fetch if not null */
#define TRANS_OPT_DEPTH "depth"
+ /* Limit the depth of the fetch based on time if not null */
+ #define TRANS_OPT_DEEPEN_SINCE "deepen-since"
+
+ /* Limit the depth of the fetch based on revs if not null */
+ #define TRANS_OPT_DEEPEN_NOT "deepen-not"
+
+ /* Limit the deepen of the fetch if not null */
+ #define TRANS_OPT_DEEPEN_RELATIVE "deepen-relative"
+
/* Aggressively fetch annotated tags if possible */
#define TRANS_OPT_FOLLOWTAGS "followtags"
#include "sigchain.h"
#include "version.h"
#include "string-list.h"
+#include "parse-options.h"
+ #include "argv-array.h"
-static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
+static const char * const upload_pack_usage[] = {
+ N_("git upload-pack [<options>] <dir>"),
+ NULL
+};
/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
static unsigned long oldest_have;
+ static int deepen_relative;
static int multi_ack;
static int no_done;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int use_sideband;
static int advertise_refs;
static int stateless_rpc;
+static const char *pack_objects_hook;
static void reset_timeout(void)
{
alarm(timeout);
}
-static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
+static void send_client_data(int fd, const char *data, ssize_t sz)
{
- if (use_sideband)
- return send_sideband(1, fd, data, sz, use_sideband);
+ if (use_sideband) {
+ send_sideband(1, fd, data, sz, use_sideband);
+ return;
+ }
if (fd == 3)
/* emergency quit */
fd = 2;
if (fd == 2) {
/* XXX: are we happy to lose stuff here? */
xwrite(fd, data, sz);
- return sz;
+ return;
}
write_or_die(fd, data, sz);
- return sz;
}
static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
"corruption on the remote side.";
int buffered = -1;
ssize_t sz;
- const char *argv[13];
- int i, arg = 0;
+ int i;
FILE *pipe_fd;
+ if (!pack_objects_hook)
+ pack_objects.git_cmd = 1;
+ else {
+ argv_array_push(&pack_objects.args, pack_objects_hook);
+ argv_array_push(&pack_objects.args, "git");
+ pack_objects.use_shell = 1;
+ }
+
if (shallow_nr) {
- argv[arg++] = "--shallow-file";
- argv[arg++] = "";
+ argv_array_push(&pack_objects.args, "--shallow-file");
+ argv_array_push(&pack_objects.args, "");
}
- argv[arg++] = "pack-objects";
- argv[arg++] = "--revs";
+ argv_array_push(&pack_objects.args, "pack-objects");
+ argv_array_push(&pack_objects.args, "--revs");
if (use_thin_pack)
- argv[arg++] = "--thin";
+ argv_array_push(&pack_objects.args, "--thin");
- argv[arg++] = "--stdout";
+ argv_array_push(&pack_objects.args, "--stdout");
if (shallow_nr)
- argv[arg++] = "--shallow";
+ argv_array_push(&pack_objects.args, "--shallow");
if (!no_progress)
- argv[arg++] = "--progress";
+ argv_array_push(&pack_objects.args, "--progress");
if (use_ofs_delta)
- argv[arg++] = "--delta-base-offset";
+ argv_array_push(&pack_objects.args, "--delta-base-offset");
if (use_include_tag)
- argv[arg++] = "--include-tag";
- argv[arg++] = NULL;
+ argv_array_push(&pack_objects.args, "--include-tag");
pack_objects.in = -1;
pack_objects.out = -1;
pack_objects.err = -1;
- pack_objects.git_cmd = 1;
- pack_objects.argv = argv;
if (start_command(&pack_objects))
die("git upload-pack: unable to fork git-pack-objects");
if (ret < 0) {
if (errno != EINTR) {
- error("poll failed, resuming: %s",
- strerror(errno));
+ error_errno("poll failed, resuming");
sleep(1);
}
continue;
}
else
buffered = -1;
- sz = send_client_data(1, data, sz);
- if (sz < 0)
- goto fail;
+ send_client_data(1, data, sz);
}
/*
/* flush the data */
if (0 <= buffered) {
data[0] = buffered;
- sz = send_client_data(1, data, 1);
- if (sz < 0)
- goto fail;
+ send_client_data(1, data, 1);
fprintf(stderr, "flushed.\n");
}
if (use_sideband)
die("git upload-pack: %s", abort_msg);
}
- static int got_sha1(char *hex, unsigned char *sha1)
+ static int got_sha1(const char *hex, unsigned char *sha1)
{
struct object *o;
int we_knew_they_have = 0;
for (;;) {
char *line = packet_read_line(0, NULL);
+ const char *arg;
+
reset_timeout();
if (!line) {
got_other = 0;
continue;
}
- if (starts_with(line, "have ")) {
- switch (got_sha1(line+5, sha1)) {
+ if (skip_prefix(line, "have ", &arg)) {
+ switch (got_sha1(arg, sha1)) {
case -1: /* they have what we do not */
got_other = 1;
if (multi_ack && ok_to_give_up()) {
return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
}
- static void check_non_tip(void)
+ /*
+ * on successful case, it's up to the caller to close cmd->out
+ */
+ static int do_reachable_revlist(struct child_process *cmd,
+ struct object_array *src,
+ struct object_array *reachable)
{
static const char *argv[] = {
"rev-list", "--stdin", NULL,
};
- static struct child_process cmd = CHILD_PROCESS_INIT;
struct object *o;
char namebuf[42]; /* ^ + SHA-1 + LF */
int i;
- /*
- * In the normal in-process case without
- * uploadpack.allowReachableSHA1InWant,
- * non-tip requests can never happen.
- */
- if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
- goto error;
-
- cmd.argv = argv;
- cmd.git_cmd = 1;
- cmd.no_stderr = 1;
- cmd.in = -1;
- cmd.out = -1;
-
- if (start_command(&cmd))
- goto error;
+ cmd->argv = argv;
+ cmd->git_cmd = 1;
+ cmd->no_stderr = 1;
+ cmd->in = -1;
+ cmd->out = -1;
/*
- * If rev-list --stdin encounters an unknown commit, it
- * terminates, which will cause SIGPIPE in the write loop
+ * If the next rev-list --stdin encounters an unknown commit,
+ * it terminates, which will cause SIGPIPE in the write loop
* below.
*/
sigchain_push(SIGPIPE, SIG_IGN);
+ if (start_command(cmd))
+ goto error;
+
namebuf[0] = '^';
namebuf[41] = '\n';
for (i = get_max_object_index(); 0 < i; ) {
o = get_indexed_object(--i);
if (!o)
continue;
+ if (reachable && o->type == OBJ_COMMIT)
+ o->flags &= ~TMP_MARK;
if (!is_our_ref(o))
continue;
memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd.in, namebuf, 42) < 0)
+ if (write_in_full(cmd->in, namebuf, 42) < 0)
goto error;
}
namebuf[40] = '\n';
- for (i = 0; i < want_obj.nr; i++) {
- o = want_obj.objects[i].item;
- if (is_our_ref(o))
+ for (i = 0; i < src->nr; i++) {
+ o = src->objects[i].item;
+ if (is_our_ref(o)) {
+ if (reachable)
+ add_object_array(o, NULL, reachable);
continue;
+ }
+ if (reachable && o->type == OBJ_COMMIT)
+ o->flags |= TMP_MARK;
memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd.in, namebuf, 41) < 0)
+ if (write_in_full(cmd->in, namebuf, 41) < 0)
goto error;
}
- close(cmd.in);
+ close(cmd->in);
+ cmd->in = -1;
+ sigchain_pop(SIGPIPE);
+ return 0;
+
+ error:
sigchain_pop(SIGPIPE);
+ if (cmd->in >= 0)
+ close(cmd->in);
+ if (cmd->out >= 0)
+ close(cmd->out);
+ return -1;
+ }
+
+ static int get_reachable_list(struct object_array *src,
+ struct object_array *reachable)
+ {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ int i;
+ struct object *o;
+ char namebuf[42]; /* ^ + SHA-1 + LF */
+
+ if (do_reachable_revlist(&cmd, src, reachable) < 0)
+ return -1;
+
+ while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {
+ struct object_id sha1;
+
+ if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1))
+ break;
+
+ o = lookup_object(sha1.hash);
+ if (o && o->type == OBJ_COMMIT) {
+ o->flags &= ~TMP_MARK;
+ }
+ }
+ for (i = get_max_object_index(); 0 < i; i--) {
+ o = get_indexed_object(i - 1);
+ if (o && o->type == OBJ_COMMIT &&
+ (o->flags & TMP_MARK)) {
+ add_object_array(o, NULL, reachable);
+ o->flags &= ~TMP_MARK;
+ }
+ }
+ close(cmd.out);
+
+ if (finish_command(&cmd))
+ return -1;
+
+ return 0;
+ }
+
+ static int has_unreachable(struct object_array *src)
+ {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ char buf[1];
+ int i;
+
+ if (do_reachable_revlist(&cmd, src, NULL) < 0)
+ return 1;
+
/*
* The commits out of the rev-list are not ancestors of
* our ref.
*/
- i = read_in_full(cmd.out, namebuf, 1);
+ i = read_in_full(cmd.out, buf, 1);
if (i)
goto error;
close(cmd.out);
+ cmd.out = -1;
/*
* rev-list may have died by encountering a bad commit
goto error;
/* All the non-tip ones are ancestors of what we advertised */
- return;
+ return 0;
+
+ error:
+ sigchain_pop(SIGPIPE);
+ if (cmd.out >= 0)
+ close(cmd.out);
+ return 1;
+ }
+
+ static void check_non_tip(void)
+ {
+ int i;
+
+ /*
+ * In the normal in-process case without
+ * uploadpack.allowReachableSHA1InWant,
+ * non-tip requests can never happen.
+ */
+ if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
+ goto error;
+ if (!has_unreachable(&want_obj))
+ /* All the non-tip ones are ancestors of what we advertised */
+ return;
error:
/* Pick one of them (we know there at least is one) */
for (i = 0; i < want_obj.nr; i++) {
- o = want_obj.objects[i].item;
+ struct object *o = want_obj.objects[i].item;
if (!is_our_ref(o))
die("git upload-pack: not our ref %s",
oid_to_hex(&o->oid));
}
}
+ static void send_shallow(struct commit_list *result)
+ {
+ while (result) {
+ struct object *object = &result->item->object;
+ if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
+ packet_write(1, "shallow %s",
+ oid_to_hex(&object->oid));
+ register_shallow(object->oid.hash);
+ shallow_nr++;
+ }
+ result = result->next;
+ }
+ }
+
+ static void send_unshallow(const struct object_array *shallows)
+ {
+ int i;
+
+ for (i = 0; i < shallows->nr; i++) {
+ struct object *object = shallows->objects[i].item;
+ if (object->flags & NOT_SHALLOW) {
+ struct commit_list *parents;
+ packet_write(1, "unshallow %s",
+ oid_to_hex(&object->oid));
+ object->flags &= ~CLIENT_SHALLOW;
+ /*
+ * We want to _register_ "object" as shallow, but we
+ * also need to traverse object's parents to deepen a
+ * shallow clone. Unregister it for now so we can
+ * parse and add the parents to the want list, then
+ * re-register it.
+ */
+ unregister_shallow(object->oid.hash);
+ object->parsed = 0;
+ parse_commit_or_die((struct commit *)object);
+ parents = ((struct commit *)object)->parents;
+ while (parents) {
+ add_object_array(&parents->item->object,
+ NULL, &want_obj);
+ parents = parents->next;
+ }
+ add_object_array(object, NULL, &extra_edge_obj);
+ }
+ /* make sure commit traversal conforms to client */
+ register_shallow(object->oid.hash);
+ }
+ }
+
+ static void deepen(int depth, int deepen_relative,
+ struct object_array *shallows)
+ {
+ if (depth == INFINITE_DEPTH && !is_repository_shallow()) {
+ int i;
+
+ for (i = 0; i < shallows->nr; i++) {
+ struct object *object = shallows->objects[i].item;
+ object->flags |= NOT_SHALLOW;
+ }
+ } else if (deepen_relative) {
+ struct object_array reachable_shallows = OBJECT_ARRAY_INIT;
+ struct commit_list *result;
+
+ get_reachable_list(shallows, &reachable_shallows);
+ result = get_shallow_commits(&reachable_shallows,
+ depth + 1,
+ SHALLOW, NOT_SHALLOW);
+ send_shallow(result);
+ free_commit_list(result);
+ object_array_clear(&reachable_shallows);
+ } else {
+ struct commit_list *result;
+
+ result = get_shallow_commits(&want_obj, depth,
+ SHALLOW, NOT_SHALLOW);
+ send_shallow(result);
+ free_commit_list(result);
+ }
+
+ send_unshallow(shallows);
+ packet_flush(1);
+ }
+
+ static void deepen_by_rev_list(int ac, const char **av,
+ struct object_array *shallows)
+ {
+ struct commit_list *result;
+
+ result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
+ send_shallow(result);
+ free_commit_list(result);
+ send_unshallow(shallows);
+ packet_flush(1);
+ }
+
static void receive_needs(void)
{
struct object_array shallows = OBJECT_ARRAY_INIT;
+ struct string_list deepen_not = STRING_LIST_INIT_DUP;
int depth = 0;
int has_non_tip = 0;
+ unsigned long deepen_since = 0;
+ int deepen_rev_list = 0;
shallow_nr = 0;
for (;;) {
const char *features;
unsigned char sha1_buf[20];
char *line = packet_read_line(0, NULL);
+ const char *arg;
+
reset_timeout();
if (!line)
break;
- if (starts_with(line, "shallow ")) {
+ if (skip_prefix(line, "shallow ", &arg)) {
unsigned char sha1[20];
struct object *object;
- if (get_sha1_hex(line + 8, sha1))
+ if (get_sha1_hex(arg, sha1))
die("invalid shallow line: %s", line);
object = parse_object(sha1);
if (!object)
}
continue;
}
- if (starts_with(line, "deepen ")) {
- char *end;
- depth = strtol(line + 7, &end, 0);
- if (end == line + 7 || depth <= 0)
+ if (skip_prefix(line, "deepen ", &arg)) {
+ char *end = NULL;
+ depth = strtol(arg, &end, 0);
+ if (!end || *end || depth <= 0)
die("Invalid deepen: %s", line);
continue;
}
- if (!starts_with(line, "want ") ||
- get_sha1_hex(line+5, sha1_buf))
+ if (skip_prefix(line, "deepen-since ", &arg)) {
+ char *end = NULL;
+ deepen_since = strtoul(arg, &end, 0);
+ if (!end || *end || !deepen_since ||
+ /* revisions.c's max_age -1 is special */
+ deepen_since == -1)
+ die("Invalid deepen-since: %s", line);
+ deepen_rev_list = 1;
+ continue;
+ }
+ if (skip_prefix(line, "deepen-not ", &arg)) {
+ char *ref = NULL;
+ unsigned char sha1[20];
+ if (expand_ref(arg, strlen(arg), sha1, &ref) != 1)
+ die("git upload-pack: ambiguous deepen-not: %s", line);
+ string_list_append(&deepen_not, ref);
+ free(ref);
+ deepen_rev_list = 1;
+ continue;
+ }
+ if (!skip_prefix(line, "want ", &arg) ||
+ get_sha1_hex(arg, sha1_buf))
die("git upload-pack: protocol error, "
"expected to get sha, not '%s'", line);
- features = line + 45;
+ features = arg + 40;
+ if (parse_feature_request(features, "deepen-relative"))
+ deepen_relative = 1;
if (parse_feature_request(features, "multi_ack_detailed"))
multi_ack = 2;
else if (parse_feature_request(features, "multi_ack"))
if (!use_sideband && daemon_mode)
no_progress = 1;
- if (depth == 0 && shallows.nr == 0)
+ if (depth == 0 && !deepen_rev_list && shallows.nr == 0)
return;
- if (depth > 0) {
- struct commit_list *result = NULL, *backup = NULL;
+ if (depth > 0 && deepen_rev_list)
+ die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
+ if (depth > 0)
+ deepen(depth, deepen_relative, &shallows);
+ else if (deepen_rev_list) {
+ struct argv_array av = ARGV_ARRAY_INIT;
int i;
- if (depth == INFINITE_DEPTH && !is_repository_shallow())
- for (i = 0; i < shallows.nr; i++) {
- struct object *object = shallows.objects[i].item;
- object->flags |= NOT_SHALLOW;
- }
- else
- backup = result =
- get_shallow_commits(&want_obj, depth,
- SHALLOW, NOT_SHALLOW);
- while (result) {
- struct object *object = &result->item->object;
- if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
- packet_write(1, "shallow %s",
- oid_to_hex(&object->oid));
- register_shallow(object->oid.hash);
- shallow_nr++;
+
+ argv_array_push(&av, "rev-list");
+ if (deepen_since)
+ argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+ if (deepen_not.nr) {
+ argv_array_push(&av, "--not");
+ for (i = 0; i < deepen_not.nr; i++) {
+ struct string_list_item *s = deepen_not.items + i;
+ argv_array_push(&av, s->string);
}
- result = result->next;
+ argv_array_push(&av, "--not");
}
- free_commit_list(backup);
- for (i = 0; i < shallows.nr; i++) {
- struct object *object = shallows.objects[i].item;
- if (object->flags & NOT_SHALLOW) {
- struct commit_list *parents;
- packet_write(1, "unshallow %s",
- oid_to_hex(&object->oid));
- object->flags &= ~CLIENT_SHALLOW;
- /* make sure the real parents are parsed */
- unregister_shallow(object->oid.hash);
- object->parsed = 0;
- parse_commit_or_die((struct commit *)object);
- parents = ((struct commit *)object)->parents;
- while (parents) {
- add_object_array(&parents->item->object,
- NULL, &want_obj);
- parents = parents->next;
- }
- add_object_array(object, NULL, &extra_edge_obj);
- }
- /* make sure commit traversal conforms to client */
- register_shallow(object->oid.hash);
+ for (i = 0; i < want_obj.nr; i++) {
+ struct object *o = want_obj.objects[i].item;
+ argv_array_push(&av, oid_to_hex(&o->oid));
}
- packet_flush(1);
- } else
+ deepen_by_rev_list(av.argc, av.argv, &shallows);
+ argv_array_clear(&av);
+ }
+ else
if (shallows.nr > 0) {
int i;
for (i = 0; i < shallows.nr; i++)
int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
- " side-band-64k ofs-delta shallow no-progress"
- " include-tag multi_ack_detailed";
+ " side-band-64k ofs-delta shallow deepen-since deepen-not"
+ " deepen-relative no-progress include-tag multi_ack_detailed";
const char *refname_nons = strip_namespace(refname);
struct object_id peeled;
keepalive = git_config_int(var, value);
if (!keepalive)
keepalive = -1;
+ } else if (current_config_scope() != CONFIG_SCOPE_REPO) {
+ if (!strcmp("uploadpack.packobjectshook", var))
+ return git_config_string(&pack_objects_hook, var, value);
}
return parse_hide_refs_config(var, value, "uploadpack");
}
-int main(int argc, char **argv)
+int cmd_main(int argc, const char **argv)
{
- char *dir;
- int i;
+ const char *dir;
int strict = 0;
-
- git_setup_gettext();
+ struct option options[] = {
+ OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
+ N_("quit after a single request/response exchange")),
+ OPT_BOOL(0, "advertise-refs", &advertise_refs,
+ N_("exit immediately after initial ref advertisement")),
+ OPT_BOOL(0, "strict", &strict,
+ N_("do not try <directory>/.git/ if <directory> is no Git directory")),
+ OPT_INTEGER(0, "timeout", &timeout,
+ N_("interrupt transfer after <n> seconds of inactivity")),
+ OPT_END()
+ };
packet_trace_identity("upload-pack");
- git_extract_argv0_path(argv[0]);
check_replace_refs = 0;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--advertise-refs")) {
- advertise_refs = 1;
- continue;
- }
- if (!strcmp(arg, "--stateless-rpc")) {
- stateless_rpc = 1;
- continue;
- }
- if (!strcmp(arg, "--strict")) {
- strict = 1;
- continue;
- }
- if (skip_prefix(arg, "--timeout=", &arg)) {
- timeout = atoi(arg);
- daemon_mode = 1;
- continue;
- }
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- }
+ if (argc != 1)
+ usage_with_options(upload_pack_usage, options);
- if (i != argc-1)
- usage(upload_pack_usage);
+ if (timeout)
+ daemon_mode = 1;
setup_path();
- dir = argv[i];
+ dir = argv[0];
if (!enter_repo(dir, strict))
die("'%s' does not appear to be a git repository", dir);