Cpp11BracedListStyle: false
# A list of macros that should be interpreted as foreach loops instead of as
-# function calls.
-ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut']
+# function calls. Taken from:
+# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' \
+# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
+# | sort | uniq
+ForEachMacros:
+ - 'for_each_abbrev'
+ - 'for_each_builtin'
+ - 'for_each_string_list_item'
+ - 'for_each_ut'
+ - 'for_each_wanted_builtin'
+ - 'list_for_each'
+ - 'list_for_each_dir'
+ - 'list_for_each_prev'
+ - 'list_for_each_prev_safe'
+ - 'list_for_each_safe'
# The maximum number of consecutive empty lines to keep.
MaxEmptyLinesToKeep: 1
* "git help git" was hard to discover (well, at least for some
people).
+ * The pattern "git diff/grep" use to extract funcname and words
+ boundary for Rust has been added.
+
Performance, Internal Implementation, Development Support etc.
to honor the "--origin <name>" option.
(merge 1c4a9f9114 xl/record-partial-clone-origin later to maint).
+ * "git fetch" into a lazy clone forgot to fetch base objects that are
+ necessary to complete delta in a thin packfile, which has been
+ corrected.
+ (merge 810e19322d jt/partial-clone-missing-ref-delta-base later to maint).
+
+ * The filter_data used in the list-objects-filter (which manages a
+ lazily sparse clone repository) did not use the dynamic array API
+ correctly---'nr' is supposed to point at one past the last element
+ of the array in use. This has been corrected.
+ (merge 7140600e2e md/list-objects-filter-memfix later to maint).
+
+ * The description about slashes in gitignore patterns (used to
+ indicate things like "anchored to this level only" and "only
+ matches directories") has been revamped.
+ (merge 1a58bad014 an/ignore-doc-update later to maint).
+
+ * The URL decoding code has been updated to avoid going past the end
+ of the string while parsing %-<hex>-<hex> sequence.
+ (merge d37dc239a4 md/url-parse-harden later to maint).
+
+ * The list of for-each like macros used by clang-format has been
+ updated.
+ (merge fc7e03aace mo/clang-format-for-each-update later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge f547101b26 es/git-debugger-doc later to maint).
(merge 7877ac3d7b js/bisect-helper-check-get-oid-return-value later to maint).
(merge 0108f47eb3 sw/git-p4-unshelve-branched-files later to maint).
+ (merge 9df8f734fd cm/send-email-document-req-modules later to maint).
+ (merge afc3bf6eb1 ab/hash-object-doc later to maint).
This is the same as `gitdir` except that matching is done
case-insensitively (e.g. on case-insensitive file sytems)
+`onbranch`::
+ The data that follows the keyword `onbranch:` is taken to be a
+ pattern with standard globbing wildcards and two additional
+ ones, `**/` and `/**`, that can match multiple path components.
+ If we are in a worktree where the name of the branch that is
+ currently checked out matches the pattern, the include condition
+ is met.
++
+If the pattern ends with `/`, `**` will be automatically added. For
+example, the pattern `foo/` becomes `foo/**`. In other words, it matches
+all branches that begin with `foo/`. This is useful if your branches are
+organized hierarchically and you would like to apply a configuration to
+all the branches in that hierarchy.
+
A few more notes on matching via `gitdir` and `gitdir/i`:
* Symlinks in `$GIT_DIR` are not resolved before matching.
[includeIf "gitdir:/path/to/group/"]
path = foo.inc
+ ; include only if we are in a worktree where foo-branch is
+ ; currently checked out
+ [includeIf "onbranch:foo-branch"]
+ path = foo.inc
+
Values
~~~~~~
alias.*::
Command aliases for the linkgit:git[1] command wrapper - e.g.
- after defining "alias.last = cat-file commit HEAD", the invocation
- "git last" is equivalent to "git cat-file commit HEAD". To avoid
+ after defining `alias.last = cat-file commit HEAD`, the invocation
+ `git last` is equivalent to `git cat-file commit HEAD`. To avoid
confusion and troubles with script usage, aliases that
hide existing Git commands are ignored. Arguments are split by
spaces, the usual shell quoting and escaping is supported.
A quote pair or a backslash can be used to quote them.
+
+Note that the first word of an alias does not necessarily have to be a
+command. It can be a command-line option that will be passed into the
+invocation of `git`. In particular, this is useful when used with `-c`
+to pass in one-time configurations or `-p` to force pagination. For example,
+`loud-rebase = -c commit.verbose=true rebase` can be defined such that
+running `git loud-rebase` would be equivalent to
+`git -c commit.verbose=true rebase`. Also, `ps = -p status` would be a
+helpful alias since `git ps` would paginate the output of `git status`
+where the original command does not.
++
If the alias expansion is prefixed with an exclamation point,
it will be treated as a shell command. For example, defining
-"alias.new = !gitk --all --not ORIG_HEAD", the invocation
-"git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD". Note that shell commands will be
+`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
+`git new` is equivalent to running the shell command
+`gitk --all --not ORIG_HEAD`. Note that shell commands will be
executed from the top-level directory of a repository, which may
not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running 'git rev-parse --show-prefix'
+`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
from the original current directory. See linkgit:git-rev-parse[1].
linkgit:git-tag[1]. Without the "--sort=<value>" option provided, the
value of this variable will be used as the default.
+tag.gpgSign::
+ A boolean to specify whether all tags should be GPG signed.
+ Use of this option when running in an automated script can
+ result in a large number of tags being signed. It is therefore
+ convenient to use an agent to avoid typing your gpg passphrase
+ several times. Note that this option doesn't affects tag signing
+ behavior enabled by "-u <keyid>" or "--local-user=<keyid>" options.
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
Allow several <repository> and <group> arguments to be
specified. No <refspec>s may be specified.
+--[no-]auto-gc::
+ Run `git gc --auto` at the end to perform garbage collection
+ if needed. This is enabled by default.
+
-p::
--prune::
Before fetching, remove any remote-tracking references that no
SYNOPSIS
--------
[verse]
-'git branch' [--color[=<when>] | --no-color] [-r | -a]
- [--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
+'git branch' [--color[=<when>] | --no-color] [--show-current]
+ [-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column] [--sort=<key>]
[(--merged | --no-merged) [<commit>]]
[--contains [<commit]] [--no-contains [<commit>]]
- [--points-at <object>] [--format=<format>] [<pattern>...]
+ [--points-at <object>] [--format=<format>]
+ [(-r | --remotes) | (-a | --all)]
+ [--list] [<pattern>...]
'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
'git branch' --unset-upstream [<branchname>]
-----------
If `--list` is given, or if there are no non-option arguments, existing
-branches are listed; the current branch will be highlighted with an
-asterisk. Option `-r` causes the remote-tracking branches to be listed,
-and option `-a` shows both local and remote branches. If a `<pattern>`
+branches are listed; the current branch will be highlighted in green and
+marked with an asterisk. Any branches checked out in linked worktrees will
+be highlighted in cyan and marked with a plus sign. Option `-r` causes the
+remote-tracking branches to be listed,
+and option `-a` shows both local and remote branches.
+
+If a `<pattern>`
is given, it is used as a shell wildcard to restrict the output to
matching branches. If multiple patterns are given, a branch is shown if
-it matches any of the patterns. Note that when providing a
-`<pattern>`, you must use `--list`; otherwise the command is interpreted
+it matches any of the patterns.
+
+Note that when providing a
+`<pattern>`, you must use `--list`; otherwise the command may be interpreted
as branch creation.
With `--contains`, shows only the branches that contain the named commit
-r::
--remotes::
List or delete (if used with -d) the remote-tracking branches.
+ Combine with `--list` to match the optional pattern(s).
-a::
--all::
List both remote-tracking branches and local branches.
+ Combine with `--list` to match optional pattern(s).
-l::
--list::
When in list mode,
show sha1 and commit subject line for each head, along with
relationship to upstream branch (if any). If given twice, print
- the name of the upstream branch, as well (see also `git remote
- show <remote>`).
+ the path of the linked worktree (if any) and the name of the upstream
+ branch, as well (see also `git remote show <remote>`). Note that the
+ current worktree's HEAD will not have its path printed (it will always
+ be your current directory).
-q::
--quiet::
<2> Delete the "test" branch even if the "master" branch (or whichever branch
is currently checked out) does not have all commits from the test branch.
+Listing branches from a specific remote::
++
+------------
+$ git branch -r -l '<remote>/<pattern>' <1>
+$ git for-each-ref 'refs/remotes/<remote>/<pattern>' <2>
+------------
++
+<1> Using `-a` would conflate <remote> with any local branches you happen to
+ have been prefixed with the same <remote> pattern.
+<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
+
+Patterns will normally need quoting.
NOTES
-----
`:lstrip` and `:rstrip` options in the same way as `refname`
above.
+worktreepath::
+ The absolute path to the worktree in which the ref is checked
+ out, if it is checked out in any linked worktree. Empty string
+ otherwise.
+
In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field.
with the contents of the named file (which can be outside of the
work tree), and optionally writes the resulting object into the
object database. Reports its object ID to its standard output.
-This is used by 'git cvsimport' to update the index
-without modifying files in the work tree. When <type> is not
-specified, it defaults to "blob".
+When <type> is not specified, it defaults to "blob".
OPTIONS
-------
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
-'git merge' --abort
-'git merge' --continue
+'git merge' (--continue | --abort | --quit)
DESCRIPTION
-----------
[<upstream> [<branch>]]
'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
-'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
+'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
DESCRIPTION
-----------
[ --date=<format>]
[ [ --objects | --objects-edge | --objects-edge-aggressive ]
[ --unpacked ]
+ [ --object-names | --no-object-names ]
[ --filter=<filter-spec> [ --filter-print-omitted ] ] ]
[ --missing=<missing-action> ]
[ --pretty | --header ]
helper configured (see linkgit:git-credential[1]), the password will be saved in
the credential store so you won't have to type it the next time.
-Note: the following perl modules are required
- Net::SMTP::SSL, MIME::Base64 and Authen::SASL
+Note: the following core Perl modules that may be installed with your
+distribution of Perl are required:
+MIME::Base64, MIME::QuotedPrint, Net::Domain and Net::SMTP.
+These additional Perl modules are also required:
+Authen::SASL and Mail::Address.
+
SEE ALSO
--------
-s::
--sign::
Make a GPG-signed tag, using the default e-mail address's key.
+ The default behavior of tag GPG-signing is controlled by `tag.gpgSign`
+ configuration variable if it exists, or disabled oder otherwise.
+ See linkgit:git-config[1].
+
+--no-sign::
+ Override `tag.gpgSign` configuration variable that is
+ set to force each and every tag to be signed.
-u <keyid>::
--local-user=<keyid>::
- `ruby` suitable for source code in the Ruby language.
+- `rust` suitable for source code in the Rust language.
+
- `tex` suitable for source code for LaTeX documents.
Put a backslash ("`\`") in front of the first "`!`" for patterns
that begin with a literal "`!`", for example, "`\!important!.txt`".
- - If the pattern ends with a slash, it is removed for the
- purpose of the following description, but it would only find
- a match with a directory. In other words, `foo/` will match a
- directory `foo` and paths underneath it, but will not match a
- regular file or a symbolic link `foo` (this is consistent
- with the way how pathspec works in general in Git).
-
- - If the pattern does not contain a slash '/', Git treats it as
- a shell glob pattern and checks for a match against the
- pathname relative to the location of the `.gitignore` file
- (relative to the toplevel of the work tree if not from a
- `.gitignore` file).
-
- - Otherwise, Git treats the pattern as a shell glob: "`*`" matches
- anything except "`/`", "`?`" matches any one character except "`/`"
- and "`[]`" matches one character in a selected range. See
- fnmatch(3) and the FNM_PATHNAME flag for a more detailed
- description.
-
- - A leading slash matches the beginning of the pathname.
- For example, "/{asterisk}.c" matches "cat-file.c" but not
- "mozilla-sha1/sha1.c".
+ - The slash '/' is used as the directory separator. Separators may
+ occur at the beginning, middle or end of the `.gitignore` search pattern.
+
+ - If there is a separator at the beginning or middle (or both) of the
+ pattern, then the pattern is relative to the directory level of the
+ particular `.gitignore` file itself. Otherwise the pattern may also
+ match at any level below the `.gitignore` level.
+
+ - If there is a separator at the end of the pattern then the pattern
+ will only match directories, otherwise the pattern can match both
+ files and directories.
+
+ - For example, a pattern `doc/frotz/` matches `doc/frotz` directory,
+ but not `a/doc/frotz` directory; however `frotz/` matches `frotz`
+ and `a/frotz` that is a directory (all paths are relative from
+ the `.gitignore` file).
+
+ - An asterisk "`*`" matches anything except a slash.
+ The character "`?`" matches any one character except "`/`".
+ The range notation, e.g. `[a-zA-Z]`, can be used to match
+ one of the characters in a range. See fnmatch(3) and the
+ FNM_PATHNAME flag for a more detailed description.
Two consecutive asterisks ("`**`") in patterns matched against
full pathname may have special meaning:
EXAMPLES
--------
+ - The pattern `hello.*` matches any file or folder
+ whose name begins with `hello`. If one wants to restrict
+ this only to the directory and not in its subdirectories,
+ one can prepend the pattern with a slash, i.e. `/hello.*`;
+ the pattern now matches `hello.txt`, `hello.c` but not
+ `a/hello.java`.
+
+ - The pattern `foo/` will match a directory `foo` and
+ paths underneath it, but will not match a regular file
+ or a symbolic link `foo` (this is consistent with the
+ way how pathspec works in general in Git)
+
+ - The pattern `doc/frotz` and `/doc/frotz` have the same effect
+ in any `.gitignore` file. In other words, a leading slash
+ is not relevant if there is already a middle slash in
+ the pattern.
+
+ - The pattern "foo/*", matches "foo/test.json"
+ (a regular file), "foo/bar" (a directory), but it does not match
+ "foo/bar/hello.c" (a regular file), as the asterisk in the
+ pattern does not match "bar/hello.c" which has a slash in it.
+
--------------------------------------------------------------
$ git status
[...]
Only useful with `--objects`; print the object IDs that are not
in packs.
+--object-names::
+ Only useful with `--objects`; print the names of the object IDs
+ that are found. This is the default behavior.
+
+--no-object-names::
+ Only useful with `--objects`; does not print the names of the object
+ IDs that are found. This inverts `--object-names`. This flag allows
+ the output to be more easily parsed by commands such as
+ linkgit:git-cat-file[1].
+
--filter=<filter-spec>::
Only useful with one of the `--objects*`; omits objects (usually
blobs) from the list of printed objects. The '<filter-spec>'
=== The Normal Format Target
The normal format target is a tradition printf format and similar
-to GIT_TRACE format. This format is enabled with the `GIT_TR`
+to GIT_TRACE format. This format is enabled with the `GIT_TRACE2`
environment variable or the `trace2.normalTarget` system or global
config setting.
helpful for these clones, anyway. The commit-graph will not be read or
written when shallow commits are present.
-Future Work
------------
-
-- After computing and storing generation numbers, we must make graph
- walks aware of generation numbers to gain the performance benefits they
- enable. This will mostly be accomplished by swapping a commit-date-ordered
- priority queue with one ordered by generation number. The following
- operations are important candidates:
-
- - 'log --topo-order'
- - 'tag --merged'
-
-- A server could provide a commit-graph file as part of the network protocol
- to avoid extra calculations by clients. This feature is only of benefit if
- the user is willing to trust the file, because verifying the file is correct
- is as hard as computing it from scratch.
-
Related Links
-------------
[0] https://bugs.chromium.org/p/git/issues/detail?id=8
struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
{
- struct object *obj = lookup_object(r, oid->hash);
+ struct object *obj = lookup_object(r, oid);
if (!obj)
- return create_object(r, oid->hash,
- alloc_blob_node(r));
+ return create_object(r, oid, alloc_blob_node(r));
return object_as_type(r, obj, OBJ_BLOB, 0);
}
*/
if (!state->rebasing) {
am_destroy(state);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
GIT_COLOR_NORMAL, /* LOCAL */
GIT_COLOR_GREEN, /* CURRENT */
GIT_COLOR_BLUE, /* UPSTREAM */
+ GIT_COLOR_CYAN, /* WORKTREE */
};
enum color_branch {
BRANCH_COLOR_RESET = 0,
BRANCH_COLOR_REMOTE = 2,
BRANCH_COLOR_LOCAL = 3,
BRANCH_COLOR_CURRENT = 4,
- BRANCH_COLOR_UPSTREAM = 5
+ BRANCH_COLOR_UPSTREAM = 5,
+ BRANCH_COLOR_WORKTREE = 6
};
static const char *color_branch_slots[] = {
[BRANCH_COLOR_LOCAL] = "local",
[BRANCH_COLOR_CURRENT] = "current",
[BRANCH_COLOR_UPSTREAM] = "upstream",
+ [BRANCH_COLOR_WORKTREE] = "worktree",
};
static struct string_list output = STRING_LIST_INIT_DUP;
struct strbuf local = STRBUF_INIT;
struct strbuf remote = STRBUF_INIT;
- strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)",
- branch_get_color(BRANCH_COLOR_CURRENT),
- branch_get_color(BRANCH_COLOR_LOCAL));
+ strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)",
+ branch_get_color(BRANCH_COLOR_CURRENT),
+ branch_get_color(BRANCH_COLOR_WORKTREE),
+ branch_get_color(BRANCH_COLOR_LOCAL));
strbuf_addf(&remote, " %s",
branch_get_color(BRANCH_COLOR_REMOTE));
strbuf_addf(&local, " %s ", obname.buf);
if (filter->verbose > 1)
+ {
+ strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
+ branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+ }
else
strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
strbuf_release(&buf);
} else if (argc > 0 && argc <= 2) {
if (filter.kind != FILTER_REFS_BRANCHES)
- die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+ die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+ "Did you mean to use: -a|-r --list <pattern>?"));
if (track == BRANCH_TRACK_OVERRIDE)
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
transport_disconnect(transport);
if (option_dissociate) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
dissociate_from_references();
}
struct string_list *pack_indexes = NULL;
struct string_list *commit_hex = NULL;
struct string_list lines;
+ int result = 0;
+ unsigned int flags = COMMIT_GRAPH_PROGRESS;
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
+ if (opts.append)
+ flags |= COMMIT_GRAPH_APPEND;
read_replace_refs = 0;
- if (opts.reachable) {
- write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
- return 0;
- }
+ if (opts.reachable)
+ return write_commit_graph_reachable(opts.obj_dir, flags);
string_list_init(&lines, 0);
if (opts.stdin_packs || opts.stdin_commits) {
UNLEAK(buf);
}
- write_commit_graph(opts.obj_dir,
- pack_indexes,
- commit_hex,
- opts.append,
- 1);
+ if (write_commit_graph(opts.obj_dir,
+ pack_indexes,
+ commit_hex,
+ flags))
+ result = 1;
UNLEAK(lines);
- return 0;
+ return result;
}
int cmd_commit_graph(int argc, const char **argv, const char *prefix)
"new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git reset HEAD\" to recover."));
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
- write_commit_graph_reachable(get_object_directory(), 0, 0);
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+ write_commit_graph_reachable(get_object_directory(), 0))
+ return 1;
repo_rerere(the_repository, 0);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{
- return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
+ return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
}
static int replace_name(struct commit_name *e,
if (!e) {
e = xmalloc(sizeof(struct commit_name));
oidcpy(&e->peeled, peeled);
- hashmap_entry_init(e, sha1hash(peeled->hash));
+ hashmap_entry_init(e, oidhash(peeled));
hashmap_add(&names, e);
e->path = NULL;
}
if (is_null_oid(oid))
return;
- object = lookup_object(the_repository, oid->hash);
+ object = lookup_object(the_repository, oid);
if (object && object->flags & SHOWN)
return;
&spec->oid));
else {
struct object *object = lookup_object(the_repository,
- spec->oid.hash);
+ &spec->oid);
printf("M %06o :%d ", spec->mode,
get_object_mark(object));
}
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
static int progress = -1;
+static int enable_auto_gc = 1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
static int max_children = 1;
static enum transport_family family;
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
N_("report that we have only objects reachable from this object")),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+ N_("run 'gc --auto' after fetching")),
OPT_END()
};
struct refname_hash_entry {
struct hashmap_entry ent; /* must be the first member */
struct object_id oid;
+ int ignore;
char refname[FLEX_ARRAY];
};
return !!hashmap_get_from_hash(map, strhash(refname), refname);
}
+static void clear_item(struct refname_hash_entry *item)
+{
+ item->ignore = 1;
+}
+
static void find_non_local_tags(const struct ref *refs,
struct ref **head,
struct ref ***tail)
!will_fetch(head, ref->old_oid.hash) &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
item = NULL;
continue;
}
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
item = NULL;
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
!will_fetch(head, item->oid.hash))
- oidclr(&item->oid);
+ clear_item(item);
/*
* For all the tags in the remote_refs_list,
*/
for_each_string_list_item(remote_ref_item, &remote_refs_list) {
const char *refname = remote_ref_item->string;
+ struct ref *rm;
item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
if (!item)
BUG("unseen remote ref?");
/* Unless we have already decided to ignore this item... */
- if (!is_null_oid(&item->oid)) {
- struct ref *rm = alloc_ref(item->refname);
- rm->peer_ref = alloc_ref(item->refname);
- oidcpy(&rm->old_oid, &item->oid);
- **tail = rm;
- *tail = &rm->next;
- }
+ if (item->ignore)
+ continue;
+
+ rm = alloc_ref(item->refname);
+ rm->peer_ref = alloc_ref(item->refname);
+ oidcpy(&rm->old_oid, &item->oid);
+ **tail = rm;
+ *tail = &rm->next;
}
hashmap_free(&remote_refs, 1);
string_list_clear(&remote_refs_list, 0);
return errcode;
}
- argv_array_pushl(&argv, "fetch", "--append", NULL);
+ argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
add_options_to_argv(&argv);
for (i = 0; i < list->nr; i++) {
string_list_clear(&list, 0);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
- argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
- if (verbosity < 0)
- argv_array_push(&argv_gc_auto, "--quiet");
- run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
- argv_array_clear(&argv_gc_auto);
+ if (enable_auto_gc) {
+ argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+ if (verbosity < 0)
+ argv_array_push(&argv_gc_auto, "--quiet");
+ run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+ argv_array_clear(&argv_gc_auto);
+ }
return result;
}
static void mark_unreachable_referents(const struct object_id *oid)
{
struct fsck_options options = FSCK_OPTIONS_DEFAULT;
- struct object *obj = lookup_object(the_repository, oid->hash);
+ struct object *obj = lookup_object(the_repository, oid);
if (!obj || !(obj->flags & HAS_OBJ))
return; /* not part of our original set */
struct object *obj;
if (!is_null_oid(oid)) {
- obj = lookup_object(the_repository, oid->hash);
+ obj = lookup_object(the_repository, oid);
if (obj && (obj->flags & HAS_OBJ)) {
if (timestamp && name_objects)
add_decoration(fsck_walk_options.object_names,
static void mark_object_for_connectivity(const struct object_id *oid)
{
- struct object *obj = lookup_unknown_object(oid->hash);
+ struct object *obj = lookup_unknown_object(oid);
obj->flags |= HAS_OBJ;
}
struct object_id oid;
if (!get_oid(arg, &oid)) {
struct object *obj = lookup_object(the_repository,
- oid.hash);
+ &oid);
if (!obj || !(obj->flags & HAS_OBJ)) {
if (is_promisor_object(&oid))
gc_before_repack();
if (!repository_format_precious_objects) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
die(FAILED_RUN, repack.argv[0]);
report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
if (pack_garbage.nr > 0) {
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
clean_pack_garbage();
}
- if (gc_write_commit_graph)
- write_commit_graph_reachable(get_object_directory(), 0,
- !quiet && !daemonized);
+ if (gc_write_commit_graph &&
+ write_commit_graph_reachable(get_object_directory(),
+ !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0))
+ return 1;
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
#include "thread-utils.h"
#include "packfile.h"
#include "object-store.h"
+#include "fetch-object.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
sorted_by_pos[i] = &ref_deltas[i];
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
+ if (repository_format_partial_clone) {
+ /*
+ * Prefetch the delta bases.
+ */
+ struct oid_array to_fetch = OID_ARRAY_INIT;
+ for (i = 0; i < nr_ref_deltas; i++) {
+ struct ref_delta_entry *d = sorted_by_pos[i];
+ if (!oid_object_info_extended(the_repository, &d->oid,
+ NULL,
+ OBJECT_INFO_FOR_PREFETCH))
+ continue;
+ oid_array_append(&to_fetch, &d->oid);
+ }
+ if (to_fetch.nr)
+ fetch_objects(repository_format_partial_clone,
+ to_fetch.oid, to_fetch.nr);
+ oid_array_clear(&to_fetch);
+ }
+
for (i = 0; i < nr_ref_deltas; i++) {
struct ref_delta_entry *d = sorted_by_pos[i];
enum object_type type;
int report_end_of_input = 0;
/*
- * index-pack never needs to fetch missing objects, since it only
- * accesses the repo to do hash collision checks
+ * index-pack never needs to fetch missing objects except when
+ * REF_DELTA bases are missing (which are explicitly handled). It only
+ * accesses the repo to do hash collision checks and to check which
+ * REF_DELTA bases need to be fetched.
*/
fetch_if_missing = 0;
#include "parse-options.h"
#include "string-list.h"
#include "trailer.h"
+#include "config.h"
static const char * const git_interpret_trailers_usage[] = {
N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
OPT_END()
};
+ git_config(git_default_config, NULL);
+
argc = parse_options(argc, argv, prefix, options,
git_interpret_trailers_usage, 0);
first = pos;
last = istate->cache_nr;
while (last > first) {
- int next = (last + first) >> 1;
+ int next = first + ((last - first) >> 1);
const struct cache_entry *ce = istate->cache[next];
if (!strncmp(ce->name, prefix, prefixlen)) {
first = next+1;
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
}
}
*(p+1) = 0;
if (!get_oid(p - (hexsz - 1), &oid)) {
struct object *o =
- lookup_object(the_repository,
- oid.hash);
+ lookup_object(the_repository, &oid);
if (o)
name = get_rev_name(o, &buf);
}
void *cb_data)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
+ struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
if (entry)
entry->tagged = 1;
if (!peel_ref(path, &peeled)) {
- entry = packlist_find(&to_pack, peeled.hash, NULL);
+ entry = packlist_find(&to_pack, &peeled, NULL);
if (entry)
entry->tagged = 1;
}
{
struct object_entry *entry;
- entry = packlist_find(&to_pack, oid->hash, index_pos);
+ entry = packlist_find(&to_pack, oid, index_pos);
if (!entry)
return 0;
if (!base_sha1)
return 0;
+ oidread(&base_oid, base_sha1);
+
/*
* First see if we're already sending the base (or it's explicitly in
* our "excluded" list).
*/
- base = packlist_find(&to_pack, base_sha1, NULL);
+ base = packlist_find(&to_pack, &base_oid, NULL);
if (base) {
if (!in_same_island(&delta->idx.oid, &base->idx.oid))
return 0;
* even if it was buried too deep in history to make it into the
* packing list.
*/
- oidread(&base_oid, base_sha1);
if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
if (use_delta_islands) {
if (!in_same_island(&delta->idx.oid, &base_oid))
* it was included via bitmaps, we would not have parsed it
* previously).
*/
- if (packlist_find(&to_pack, oid->hash, NULL))
+ if (packlist_find(&to_pack, oid, NULL))
return;
tag = lookup_tag(the_repository, oid);
if (starts_with(path, "refs/tags/") && /* is a tag? */
!peel_ref(path, &peeled) && /* peelable? */
- packlist_find(&to_pack, peeled.hash, NULL)) /* object packed? */
+ packlist_find(&to_pack, &peeled, NULL)) /* object packed? */
add_tag_chain(oid);
return 0;
}
for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
depth++;
- ent = packlist_find(&to_pack, obj->oid.hash, NULL);
+ ent = packlist_find(&to_pack, &obj->oid, NULL);
if (ent && depth > oe_tree_depth(&to_pack, ent))
oe_set_tree_depth(&to_pack, ent, depth);
}
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
- o = lookup_unknown_object(oid.hash);
+ o = lookup_unknown_object(&oid);
if (!(o->flags & OBJECT_ADDED))
mark_in_pack_object(o, p, &in_pack);
o->flags |= OBJECT_ADDED;
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
- if (!packlist_find(&to_pack, oid.hash, NULL) &&
+ if (!packlist_find(&to_pack, &oid, NULL) &&
!has_sha1_pack_kept_or_nonlocal(&oid) &&
!loosened_object_can_be_discarded(&oid, p->mtime))
if (force_object_loose(&oid, p->mtime))
return;
if (use_delta_islands)
- load_delta_islands(the_repository);
+ load_delta_islands(the_repository, progress);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
perform_reachability_traversal(revs);
- obj = lookup_object(the_repository, oid->hash);
+ obj = lookup_object(the_repository, oid);
return obj && (obj->flags & SEEN);
}
{
struct strbuf dir = STRBUF_INIT;
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+ int ret = 0;
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
apply_autostash(opts);
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
- strbuf_addstr(&dir, opts->state_dir);
- remove_dir_recursively(&dir, 0);
- strbuf_release(&dir);
+ if (opts->type == REBASE_INTERACTIVE) {
+ struct replay_opts replay = REPLAY_OPTS_INIT;
- return 0;
+ replay.action = REPLAY_INTERACTIVE_REBASE;
+ ret = sequencer_remove_state(&replay);
+ } else {
+ strbuf_addstr(&dir, opts->state_dir);
+ if (remove_dir_recursively(&dir, 0))
+ ret = error(_("could not remove '%s'"),
+ opts->state_dir);
+ strbuf_release(&dir);
+ }
+
+ return ret;
}
static struct commit *peel_committish(const char *name)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository);
- ret = finish_rebase(&options);
+ ret = !!finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
- strbuf_reset(&buf);
- strbuf_addstr(&buf, options.state_dir);
- ret = !!remove_dir_recursively(&buf, 0);
- if (ret)
- die(_("could not remove '%s'"), options.state_dir);
+ if (options.type == REBASE_INTERACTIVE) {
+ struct replay_opts replay = REPLAY_OPTS_INIT;
+
+ replay.action = REPLAY_INTERACTIVE_REBASE;
+ ret = !!sequencer_remove_state(&replay);
+ } else {
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, options.state_dir);
+ ret = !!remove_dir_recursively(&buf, 0);
+ if (ret)
+ error(_("could not remove '%s'"),
+ options.state_dir);
+ }
goto cleanup;
}
case ACTION_EDIT_TODO:
ret = !!run_specific_rebase(&options, action);
cleanup:
+ strbuf_release(&buf);
strbuf_release(&revisions);
free(options.head_name);
free(options.gpg_sign_opt);
proc.git_cmd = 1;
proc.argv = argv_gc_auto;
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
if (!names.nr && !po_args.quiet)
printf_ln(_("Nothing new to pack."));
- close_all_packs(the_repository->objects);
+ close_object_store(the_repository->objects);
/*
* Ok we have prepared all new packfiles.
" --objects | --objects-edge\n"
" --unpacked\n"
" --header | --pretty\n"
+" --[no-]object-names\n"
" --abbrev=<n> | --no-abbrev\n"
" --abbrev-commit\n"
" --left-right\n"
};
static enum missing_action arg_missing_action;
+/* display only the oid of each object encountered */
+static int arg_show_object_names = 1;
+
#define DEFAULT_OIDSET_SIZE (16*1024)
static void finish_commit(struct commit *commit);
display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
- show_object_with_name(stdout, obj, name);
+ if (arg_show_object_names)
+ show_object_with_name(stdout, obj, name);
+ else
+ printf("%s\n", oid_to_hex(&obj->oid));
}
static void show_edge(struct commit *commit)
if (skip_prefix(arg, "--missing=", &arg))
continue; /* already handled above */
+ if (!strcmp(arg, ("--no-object-names"))) {
+ arg_show_object_names = 0;
+ continue;
+ }
+
+ if (!strcmp(arg, ("--object-names"))) {
+ arg_show_object_names = 1;
+ continue;
+ }
+
usage(rev_list_usage);
}
static int show_stash(int argc, const char **argv, const char *prefix)
{
int i;
- int opts = 0;
int ret = 0;
struct stash_info info;
struct rev_info rev;
struct argv_array stash_args = ARGV_ARRAY_INIT;
+ struct argv_array revision_args = ARGV_ARRAY_INIT;
struct option options[] = {
OPT_END()
};
git_config(git_diff_ui_config, NULL);
init_revisions(&rev, prefix);
+ argv_array_push(&revision_args, argv[0]);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-')
argv_array_push(&stash_args, argv[i]);
else
- opts++;
+ argv_array_push(&revision_args, argv[i]);
}
ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
* The config settings are applied only if there are not passed
* any options.
*/
- if (!opts) {
+ if (revision_args.argc == 1) {
git_config(git_stash_config, NULL);
if (show_stat)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
}
}
- argc = setup_revisions(argc, argv, &rev, NULL);
+ argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
if (argc > 1) {
free_stash_info(&info);
usage_with_options(git_stash_show_usage, options);
static unsigned int colopts;
static int force_sign_annotate;
+static int config_sign_tag = -1; /* unspecified */
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
struct ref_format *format)
int status;
struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
+ if (!strcmp(var, "tag.gpgsign")) {
+ config_sign_tag = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
filter.lines = -1;
+ opt.sign = -1;
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
- if (keyid) {
- opt.sign = 1;
- set_signing_key(keyid);
- }
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
-
if (!cmdmode) {
if (argc == 0)
cmdmode = 'l';
if (cmdmode == 'l')
setup_auto_pager("tag", 1);
+ if (opt.sign == -1)
+ opt.sign = cmdmode ? 0 : config_sign_tag > 0;
+
+ if (keyid) {
+ opt.sign = 1;
+ set_signing_key(keyid);
+ }
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
{
struct object *obj;
struct obj_buffer *obj_buffer;
- obj = lookup_object(the_repository, base->hash);
+ obj = lookup_object(the_repository, base);
if (!obj)
return 0;
obj_buffer = lookup_object_buffer(obj);
int git_deflate(git_zstream *, int flush);
unsigned long git_deflate_bound(git_zstream *, unsigned long);
-/* The length in bytes and in hex digits of an object name (SHA-1 value). */
-#define GIT_SHA1_RAWSZ 20
-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
-/* The block size of SHA-1. */
-#define GIT_SHA1_BLKSZ 64
-
-/* The length in bytes and in hex digits of an object name (SHA-256 value). */
-#define GIT_SHA256_RAWSZ 32
-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
-/* The block size of SHA-256. */
-#define GIT_SHA256_BLKSZ 64
-
-/* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
-/* The largest possible block size for any supported hash. */
-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
-
-struct object_id {
- unsigned char hash[GIT_MAX_RAWSZ];
-};
-
-#define the_hash_algo the_repository->hash_algo
-
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
#define DTYPE(de) ((de)->d_type)
#else
return !!first_generation;
}
-void close_commit_graph(struct repository *r)
+void close_commit_graph(struct raw_object_store *o)
{
- free_commit_graph(r->objects->commit_graph);
- r->objects->commit_graph = NULL;
+ free_commit_graph(o->commit_graph);
+ o->commit_graph = NULL;
}
static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
}
+struct packed_commit_list {
+ struct commit **list;
+ int nr;
+ int alloc;
+};
+
+struct packed_oid_list {
+ struct object_id *list;
+ int nr;
+ int alloc;
+};
+
+struct write_commit_graph_context {
+ struct repository *r;
+ const char *obj_dir;
+ char *graph_name;
+ struct packed_oid_list oids;
+ struct packed_commit_list commits;
+ int num_extra_edges;
+ unsigned long approx_nr_objects;
+ struct progress *progress;
+ int progress_done;
+ uint64_t progress_cnt;
+ unsigned append:1,
+ report_progress:1;
+};
+
static void write_graph_chunk_fanout(struct hashfile *f,
- struct commit **commits,
- int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
int i, count = 0;
- struct commit **list = commits;
+ struct commit **list = ctx->commits.list;
/*
* Write the first-level table (the list is sorted,
* having to do eight extra binary search iterations).
*/
for (i = 0; i < 256; i++) {
- while (count < nr_commits) {
+ while (count < ctx->commits.nr) {
if ((*list)->object.oid.hash[0] != i)
break;
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
count++;
list++;
}
}
static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
- struct commit **commits, int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
+ struct commit **list = ctx->commits.list;
int count;
- for (count = 0; count < nr_commits; count++, list++) {
- display_progress(progress, ++*progress_cnt);
+ for (count = 0; count < ctx->commits.nr; count++, list++) {
+ display_progress(ctx->progress, ++ctx->progress_cnt);
hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
}
}
}
static void write_graph_chunk_data(struct hashfile *f, int hash_len,
- struct commit **commits, int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
- struct commit **last = commits + nr_commits;
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
uint32_t num_extra_edges = 0;
while (list < last) {
struct commit_list *parent;
int edge_value;
uint32_t packedDate[2];
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
parse_commit_no_graph(*list);
hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
edge_value = GRAPH_PARENT_NONE;
else {
edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
else {
edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
BUG("missing parent %s for commit %s",
}
static void write_graph_chunk_extra_edges(struct hashfile *f,
- struct commit **commits,
- int nr_commits,
- struct progress *progress,
- uint64_t *progress_cnt)
+ struct write_commit_graph_context *ctx)
{
- struct commit **list = commits;
- struct commit **last = commits + nr_commits;
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
struct commit_list *parent;
while (list < last) {
int num_parents = 0;
- display_progress(progress, ++*progress_cnt);
+ display_progress(ctx->progress, ++ctx->progress_cnt);
for (parent = (*list)->parents; num_parents < 3 && parent;
parent = parent->next)
/* Since num_parents > 2, this initializer is safe. */
for (parent = (*list)->parents->next; parent; parent = parent->next) {
int edge_value = sha1_pos(parent->item->object.oid.hash,
- commits,
- nr_commits,
+ ctx->commits.list,
+ ctx->commits.nr,
commit_to_sha1);
if (edge_value < 0)
return oidcmp(a, b);
}
-struct packed_commit_list {
- struct commit **list;
- int nr;
- int alloc;
-};
-
-struct packed_oid_list {
- struct object_id *list;
- int nr;
- int alloc;
- struct progress *progress;
- int progress_done;
-};
-
static int add_packed_commits(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
void *data)
{
- struct packed_oid_list *list = (struct packed_oid_list*)data;
+ struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
enum object_type type;
off_t offset = nth_packed_object_offset(pack, pos);
struct object_info oi = OBJECT_INFO_INIT;
- if (list->progress)
- display_progress(list->progress, ++list->progress_done);
+ if (ctx->progress)
+ display_progress(ctx->progress, ++ctx->progress_done);
oi.typep = &type;
- if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+ if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
die(_("unable to get type of object %s"), oid_to_hex(oid));
if (type != OBJ_COMMIT)
return 0;
- ALLOC_GROW(list->list, list->nr + 1, list->alloc);
- oidcpy(&(list->list[list->nr]), oid);
- list->nr++;
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+ ctx->oids.nr++;
return 0;
}
-static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
{
struct commit_list *parent;
for (parent = commit->parents; parent; parent = parent->next) {
if (!(parent->item->object.flags & UNINTERESTING)) {
- ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
- oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
- oids->nr++;
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+ ctx->oids.nr++;
parent->item->object.flags |= UNINTERESTING;
}
}
}
-static void close_reachable(struct packed_oid_list *oids, int report_progress)
+static void close_reachable(struct write_commit_graph_context *ctx)
{
int i;
struct commit *commit;
- struct progress *progress = NULL;
- if (report_progress)
- progress = start_delayed_progress(
- _("Loading known commits in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Loading known commits in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
commit->object.flags |= UNINTERESTING;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
/*
- * As this loop runs, oids->nr may grow, but not more
+ * As this loop runs, ctx->oids.nr may grow, but not more
* than the number of missing commits in the reachable
* closure.
*/
- if (report_progress)
- progress = start_delayed_progress(
- _("Expanding reachable commits in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Expanding reachable commits in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit && !parse_commit_no_graph(commit))
- add_missing_parents(oids, commit);
+ add_missing_parents(ctx, commit);
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
- if (report_progress)
- progress = start_delayed_progress(
- _("Clearing commit marks in commit graph"), oids->nr);
- for (i = 0; i < oids->nr; i++) {
- display_progress(progress, i + 1);
- commit = lookup_commit(the_repository, &oids->list[i]);
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Clearing commit marks in commit graph"),
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
commit->object.flags &= ~UNINTERESTING;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
}
-static void compute_generation_numbers(struct packed_commit_list* commits,
- int report_progress)
+static void compute_generation_numbers(struct write_commit_graph_context *ctx)
{
int i;
struct commit_list *list = NULL;
- struct progress *progress = NULL;
- if (report_progress)
- progress = start_progress(
- _("Computing commit graph generation numbers"),
- commits->nr);
- for (i = 0; i < commits->nr; i++) {
- display_progress(progress, i + 1);
- if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
- commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+ if (ctx->report_progress)
+ ctx->progress = start_progress(
+ _("Computing commit graph generation numbers"),
+ ctx->commits.nr);
+ for (i = 0; i < ctx->commits.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+ ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
continue;
- commit_list_insert(commits->list[i], &list);
+ commit_list_insert(ctx->commits.list[i], &list);
while (list) {
struct commit *current = list->item;
struct commit_list *parent;
}
}
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
}
static int add_ref_to_list(const char *refname,
return 0;
}
-void write_commit_graph_reachable(const char *obj_dir, int append,
- int report_progress)
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
{
struct string_list list = STRING_LIST_INIT_DUP;
+ int result;
for_each_ref(add_ref_to_list, &list);
- write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+ result = write_commit_graph(obj_dir, NULL, &list,
+ flags);
string_list_clear(&list, 0);
+ return result;
}
-void write_commit_graph(const char *obj_dir,
- struct string_list *pack_indexes,
- struct string_list *commit_hex,
- int append, int report_progress)
+static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+ struct string_list *pack_indexes)
{
- struct packed_oid_list oids;
- struct packed_commit_list commits;
- struct hashfile *f;
- uint32_t i, count_distinct = 0;
- char *graph_name;
- struct lock_file lk = LOCK_INIT;
- uint32_t chunk_ids[5];
- uint64_t chunk_offsets[5];
- int num_chunks;
- int num_extra_edges;
- struct commit_list *parent;
- struct progress *progress = NULL;
- const unsigned hashsz = the_hash_algo->rawsz;
- uint64_t progress_cnt = 0;
+ uint32_t i;
struct strbuf progress_title = STRBUF_INIT;
- unsigned long approx_nr_objects;
-
- if (!commit_graph_compatible(the_repository))
- return;
-
- oids.nr = 0;
- approx_nr_objects = approximate_object_count();
- oids.alloc = approx_nr_objects / 32;
- oids.progress = NULL;
- oids.progress_done = 0;
+ struct strbuf packname = STRBUF_INIT;
+ int dirlen;
- if (append) {
- prepare_commit_graph_one(the_repository, obj_dir);
- if (the_repository->objects->commit_graph)
- oids.alloc += the_repository->objects->commit_graph->num_commits;
+ strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+ dirlen = packname.len;
+ if (ctx->report_progress) {
+ strbuf_addf(&progress_title,
+ Q_("Finding commits for commit graph in %d pack",
+ "Finding commits for commit graph in %d packs",
+ pack_indexes->nr),
+ pack_indexes->nr);
+ ctx->progress = start_delayed_progress(progress_title.buf, 0);
+ ctx->progress_done = 0;
}
-
- if (oids.alloc < 1024)
- oids.alloc = 1024;
- ALLOC_ARRAY(oids.list, oids.alloc);
-
- if (append && the_repository->objects->commit_graph) {
- struct commit_graph *commit_graph =
- the_repository->objects->commit_graph;
- for (i = 0; i < commit_graph->num_commits; i++) {
- const unsigned char *hash = commit_graph->chunk_oid_lookup +
- commit_graph->hash_len * i;
- hashcpy(oids.list[oids.nr++].hash, hash);
+ for (i = 0; i < pack_indexes->nr; i++) {
+ struct packed_git *p;
+ strbuf_setlen(&packname, dirlen);
+ strbuf_addstr(&packname, pack_indexes->items[i].string);
+ p = add_packed_git(packname.buf, packname.len, 1);
+ if (!p) {
+ error(_("error adding pack %s"), packname.buf);
+ return -1;
+ }
+ if (open_pack_index(p)) {
+ error(_("error opening index for %s"), packname.buf);
+ return -1;
}
+ for_each_object_in_pack(p, add_packed_commits, ctx,
+ FOR_EACH_OBJECT_PACK_ORDER);
+ close_pack(p);
+ free(p);
}
- if (pack_indexes) {
- struct strbuf packname = STRBUF_INIT;
- int dirlen;
- strbuf_addf(&packname, "%s/pack/", obj_dir);
- dirlen = packname.len;
- if (report_progress) {
- strbuf_addf(&progress_title,
- Q_("Finding commits for commit graph in %d pack",
- "Finding commits for commit graph in %d packs",
- pack_indexes->nr),
- pack_indexes->nr);
- oids.progress = start_delayed_progress(progress_title.buf, 0);
- oids.progress_done = 0;
- }
- for (i = 0; i < pack_indexes->nr; i++) {
- struct packed_git *p;
- strbuf_setlen(&packname, dirlen);
- strbuf_addstr(&packname, pack_indexes->items[i].string);
- p = add_packed_git(packname.buf, packname.len, 1);
- if (!p)
- die(_("error adding pack %s"), packname.buf);
- if (open_pack_index(p))
- die(_("error opening index for %s"), packname.buf);
- for_each_object_in_pack(p, add_packed_commits, &oids,
- FOR_EACH_OBJECT_PACK_ORDER);
- close_pack(p);
- free(p);
- }
- stop_progress(&oids.progress);
- strbuf_reset(&progress_title);
- strbuf_release(&packname);
+ stop_progress(&ctx->progress);
+ strbuf_reset(&progress_title);
+ strbuf_release(&packname);
+
+ return 0;
+}
+
+static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+ struct string_list *commit_hex)
+{
+ uint32_t i;
+ struct strbuf progress_title = STRBUF_INIT;
+
+ if (ctx->report_progress) {
+ strbuf_addf(&progress_title,
+ Q_("Finding commits for commit graph from %d ref",
+ "Finding commits for commit graph from %d refs",
+ commit_hex->nr),
+ commit_hex->nr);
+ ctx->progress = start_delayed_progress(
+ progress_title.buf,
+ commit_hex->nr);
}
+ for (i = 0; i < commit_hex->nr; i++) {
+ const char *end;
+ struct object_id oid;
+ struct commit *result;
+
+ display_progress(ctx->progress, i + 1);
+ if (commit_hex->items[i].string &&
+ parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+ continue;
- if (commit_hex) {
- if (report_progress) {
- strbuf_addf(&progress_title,
- Q_("Finding commits for commit graph from %d ref",
- "Finding commits for commit graph from %d refs",
- commit_hex->nr),
- commit_hex->nr);
- progress = start_delayed_progress(progress_title.buf,
- commit_hex->nr);
- }
- for (i = 0; i < commit_hex->nr; i++) {
- const char *end;
- struct object_id oid;
- struct commit *result;
-
- display_progress(progress, i + 1);
- if (commit_hex->items[i].string &&
- parse_oid_hex(commit_hex->items[i].string, &oid, &end))
- continue;
-
- result = lookup_commit_reference_gently(the_repository, &oid, 1);
-
- if (result) {
- ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
- oidcpy(&oids.list[oids.nr], &(result->object.oid));
- oids.nr++;
- }
+ result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+
+ if (result) {
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+ ctx->oids.nr++;
}
- stop_progress(&progress);
- strbuf_reset(&progress_title);
}
+ stop_progress(&ctx->progress);
+ strbuf_release(&progress_title);
+}
- if (!pack_indexes && !commit_hex) {
- if (report_progress)
- oids.progress = start_delayed_progress(
- _("Finding commits for commit graph among packed objects"),
- approx_nr_objects);
- for_each_packed_object(add_packed_commits, &oids,
- FOR_EACH_OBJECT_PACK_ORDER);
- if (oids.progress_done < approx_nr_objects)
- display_progress(oids.progress, approx_nr_objects);
- stop_progress(&oids.progress);
- }
+static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+{
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
+ _("Finding commits for commit graph among packed objects"),
+ ctx->approx_nr_objects);
+ for_each_packed_object(add_packed_commits, ctx,
+ FOR_EACH_OBJECT_PACK_ORDER);
+ if (ctx->progress_done < ctx->approx_nr_objects)
+ display_progress(ctx->progress, ctx->approx_nr_objects);
+ stop_progress(&ctx->progress);
+}
- close_reachable(&oids, report_progress);
+static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+{
+ uint32_t i, count_distinct = 1;
- if (report_progress)
- progress = start_delayed_progress(
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
_("Counting distinct commits in commit graph"),
- oids.nr);
- display_progress(progress, 0); /* TODO: Measure QSORT() progress */
- QSORT(oids.list, oids.nr, commit_compare);
- count_distinct = 1;
- for (i = 1; i < oids.nr; i++) {
- display_progress(progress, i + 1);
- if (!oideq(&oids.list[i - 1], &oids.list[i]))
+ ctx->oids.nr);
+ display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+ QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+
+ for (i = 1; i < ctx->oids.nr; i++) {
+ display_progress(ctx->progress, i + 1);
+ if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
count_distinct++;
}
- stop_progress(&progress);
+ stop_progress(&ctx->progress);
- if (count_distinct >= GRAPH_EDGE_LAST_MASK)
- die(_("the commit graph format cannot write %d commits"), count_distinct);
+ return count_distinct;
+}
- commits.nr = 0;
- commits.alloc = count_distinct;
- ALLOC_ARRAY(commits.list, commits.alloc);
+static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+{
+ uint32_t i;
+ struct commit_list *parent;
- num_extra_edges = 0;
- if (report_progress)
- progress = start_delayed_progress(
+ ctx->num_extra_edges = 0;
+ if (ctx->report_progress)
+ ctx->progress = start_delayed_progress(
_("Finding extra edges in commit graph"),
- oids.nr);
- for (i = 0; i < oids.nr; i++) {
+ ctx->oids.nr);
+ for (i = 0; i < ctx->oids.nr; i++) {
int num_parents = 0;
- display_progress(progress, i + 1);
- if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+ display_progress(ctx->progress, i + 1);
+ if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
continue;
- commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
- parse_commit_no_graph(commits.list[commits.nr]);
+ ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+ parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
- for (parent = commits.list[commits.nr]->parents;
+ for (parent = ctx->commits.list[ctx->commits.nr]->parents;
parent; parent = parent->next)
num_parents++;
if (num_parents > 2)
- num_extra_edges += num_parents - 1;
+ ctx->num_extra_edges += num_parents - 1;
- commits.nr++;
+ ctx->commits.nr++;
}
- num_chunks = num_extra_edges ? 4 : 3;
- stop_progress(&progress);
-
- if (commits.nr >= GRAPH_EDGE_LAST_MASK)
- die(_("too many commits to write graph"));
-
- compute_generation_numbers(&commits, report_progress);
+ stop_progress(&ctx->progress);
+}
- graph_name = get_commit_graph_filename(obj_dir);
- if (safe_create_leading_directories(graph_name)) {
- UNLEAK(graph_name);
- die_errno(_("unable to create leading directories of %s"),
- graph_name);
+static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+{
+ uint32_t i;
+ struct hashfile *f;
+ struct lock_file lk = LOCK_INIT;
+ uint32_t chunk_ids[5];
+ uint64_t chunk_offsets[5];
+ const unsigned hashsz = the_hash_algo->rawsz;
+ struct strbuf progress_title = STRBUF_INIT;
+ int num_chunks = ctx->num_extra_edges ? 4 : 3;
+
+ ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+ if (safe_create_leading_directories(ctx->graph_name)) {
+ UNLEAK(ctx->graph_name);
+ error(_("unable to create leading directories of %s"),
+ ctx->graph_name);
+ return -1;
}
- hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
hashwrite_be32(f, GRAPH_SIGNATURE);
chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
chunk_ids[2] = GRAPH_CHUNKID_DATA;
- if (num_extra_edges)
+ if (ctx->num_extra_edges)
chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
else
chunk_ids[3] = 0;
chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
- chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
- chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
- chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+ chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+ chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+ chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
for (i = 0; i <= num_chunks; i++) {
uint32_t chunk_write[3];
hashwrite(f, chunk_write, 12);
}
- if (report_progress) {
+ if (ctx->report_progress) {
strbuf_addf(&progress_title,
Q_("Writing out commit graph in %d pass",
"Writing out commit graph in %d passes",
num_chunks),
num_chunks);
- progress = start_delayed_progress(
+ ctx->progress = start_delayed_progress(
progress_title.buf,
- num_chunks * commits.nr);
+ num_chunks * ctx->commits.nr);
}
- write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
- write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
- write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
- if (num_extra_edges)
- write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
- stop_progress(&progress);
+ write_graph_chunk_fanout(f, ctx);
+ write_graph_chunk_oids(f, hashsz, ctx);
+ write_graph_chunk_data(f, hashsz, ctx);
+ if (ctx->num_extra_edges)
+ write_graph_chunk_extra_edges(f, ctx);
+ stop_progress(&ctx->progress);
strbuf_release(&progress_title);
- close_commit_graph(the_repository);
+ close_commit_graph(ctx->r->objects);
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
commit_lock_file(&lk);
- free(graph_name);
- free(commits.list);
- free(oids.list);
+ return 0;
+}
+
+int write_commit_graph(const char *obj_dir,
+ struct string_list *pack_indexes,
+ struct string_list *commit_hex,
+ unsigned int flags)
+{
+ struct write_commit_graph_context *ctx;
+ uint32_t i, count_distinct = 0;
+ int res = 0;
+
+ if (!commit_graph_compatible(the_repository))
+ return 0;
+
+ ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+ ctx->r = the_repository;
+ ctx->obj_dir = obj_dir;
+ ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+ ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+
+ ctx->approx_nr_objects = approximate_object_count();
+ ctx->oids.alloc = ctx->approx_nr_objects / 32;
+
+ if (ctx->append) {
+ prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+ if (ctx->r->objects->commit_graph)
+ ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+ }
+
+ if (ctx->oids.alloc < 1024)
+ ctx->oids.alloc = 1024;
+ ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+
+ if (ctx->append && ctx->r->objects->commit_graph) {
+ struct commit_graph *g = ctx->r->objects->commit_graph;
+ for (i = 0; i < g->num_commits; i++) {
+ const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+ hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+ }
+ }
+
+ if (pack_indexes) {
+ if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+ goto cleanup;
+ }
+
+ if (commit_hex)
+ fill_oids_from_commit_hex(ctx, commit_hex);
+
+ if (!pack_indexes && !commit_hex)
+ fill_oids_from_all_packs(ctx);
+
+ close_reachable(ctx);
+
+ count_distinct = count_distinct_commits(ctx);
+
+ if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+ error(_("the commit graph format cannot write %d commits"), count_distinct);
+ res = -1;
+ goto cleanup;
+ }
+
+ ctx->commits.alloc = count_distinct;
+ ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+
+ copy_oids_to_commits(ctx);
+
+ if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+ error(_("too many commits to write graph"));
+ res = -1;
+ goto cleanup;
+ }
+
+ compute_generation_numbers(ctx);
+
+ res = write_commit_graph_file(ctx);
+
+cleanup:
+ free(ctx->graph_name);
+ free(ctx->commits.list);
+ free(ctx->oids.list);
+ free(ctx);
+
+ return res;
}
#define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
graph_commit = lookup_commit(r, &cur_oid);
- odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r));
+ odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));
if (parse_commit_internal(odb_commit, 0, 0)) {
graph_report(_("failed to parse commit %s from object database for commit-graph"),
oid_to_hex(&cur_oid));
*/
int generation_numbers_enabled(struct repository *r);
-void write_commit_graph_reachable(const char *obj_dir, int append,
- int report_progress);
-void write_commit_graph(const char *obj_dir,
- struct string_list *pack_indexes,
- struct string_list *commit_hex,
- int append, int report_progress);
+#define COMMIT_GRAPH_APPEND (1 << 0)
+#define COMMIT_GRAPH_PROGRESS (1 << 1)
+
+/*
+ * The write_commit_graph* methods return zero on success
+ * and a negative value on failure. Note that if the repository
+ * is not compatible with the commit-graph feature, then the
+ * methods will return 0 without writing a commit-graph.
+ */
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags);
+int write_commit_graph(const char *obj_dir,
+ struct string_list *pack_indexes,
+ struct string_list *commit_hex,
+ unsigned int flags);
int verify_commit_graph(struct repository *r, struct commit_graph *g);
-void close_commit_graph(struct repository *);
+void close_commit_graph(struct raw_object_store *);
void free_commit_graph(struct commit_graph *);
#endif
struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
{
- struct object *obj = lookup_object(r, oid->hash);
+ struct object *obj = lookup_object(r, oid);
if (!obj)
- return create_object(r, oid->hash,
- alloc_commit_node(r));
+ return create_object(r, oid, alloc_commit_node(r));
return object_as_type(r, obj, OBJ_COMMIT, 0);
}
item->date = parse_commit_date(bufptr, tail);
if (check_graph)
- load_commit_graph_info(the_repository, item);
+ load_commit_graph_info(r, item);
return 0;
}
case FILE_TYPE_PIPE:
if (!once_only)
{
- NtQueryInformationFile = (PNtQueryInformationFile)
+ NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
GetProcAddress (GetModuleHandle ("ntdll.dll"),
"NtQueryInformationFile");
once_only = TRUE;
#include <wingdi.h>
#include <winreg.h>
#include "win32.h"
+#include "win32/lazyload.h"
static int fd_is_interactive[3] = { 0, 0, 0 };
#define FD_CONSOLE 0x1
#endif
#endif
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
- PCONSOLE_FONT_INFOEX);
-
static void warn_if_raster_font(void)
{
DWORD fontFamily = 0;
- PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+ DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+ HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
/* don't bother if output was ascii only */
if (!non_ascii_used)
return;
/* GetCurrentConsoleFontEx is available since Vista */
- pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
- GetModuleHandle("kernel32.dll"),
- "GetCurrentConsoleFontEx");
- if (pGetCurrentConsoleFontEx) {
+ if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
- if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+ if (GetCurrentConsoleFontEx(console, 0, &cfi))
fontFamily = cfi.FontFamily;
} else {
/* pre-Vista: check default console font in registry */
#include "utf8.h"
#include "dir.h"
#include "color.h"
+#include "refs.h"
struct config_source {
struct config_source *prev;
return ret;
}
+static void add_trailing_starstar_for_dir(struct strbuf *pat)
+{
+ if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+ strbuf_addstr(pat, "**");
+}
+
static int prepare_include_condition_pattern(struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
} else if (!is_absolute_path(pat->buf))
strbuf_insert(pat, 0, "**/", 3);
- if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
- strbuf_addstr(pat, "**");
+ add_trailing_starstar_for_dir(pat);
strbuf_release(&path);
return prefix;
return ret;
}
+static int include_by_branch(const char *cond, size_t cond_len)
+{
+ int flags;
+ int ret;
+ struct strbuf pattern = STRBUF_INIT;
+ const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ const char *shortname;
+
+ if (!refname || !(flags & REF_ISSYMREF) ||
+ !skip_prefix(refname, "refs/heads/", &shortname))
+ return 0;
+
+ strbuf_add(&pattern, cond, cond_len);
+ add_trailing_starstar_for_dir(&pattern);
+ ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
+ strbuf_release(&pattern);
+ return ret;
+}
+
static int include_condition_is_true(const struct config_options *opts,
const char *cond, size_t cond_len)
{
return include_by_gitdir(opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
return include_by_gitdir(opts, cond, cond_len, 1);
+ else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
+ return include_by_branch(cond, cond_len);
/* unknown conditionals are always false */
return 0;
return error_return;
}
-static int parse_unit_factor(const char *end, uintmax_t *val)
+static uintmax_t get_unit_factor(const char *end)
{
if (!*end)
return 1;
- else if (!strcasecmp(end, "k")) {
- *val *= 1024;
- return 1;
- }
- else if (!strcasecmp(end, "m")) {
- *val *= 1024 * 1024;
- return 1;
- }
- else if (!strcasecmp(end, "g")) {
- *val *= 1024 * 1024 * 1024;
- return 1;
- }
+ else if (!strcasecmp(end, "k"))
+ return 1024;
+ else if (!strcasecmp(end, "m"))
+ return 1024 * 1024;
+ else if (!strcasecmp(end, "g"))
+ return 1024 * 1024 * 1024;
return 0;
}
char *end;
intmax_t val;
uintmax_t uval;
- uintmax_t factor = 1;
+ uintmax_t factor;
errno = 0;
val = strtoimax(value, &end, 0);
if (errno == ERANGE)
return 0;
- if (!parse_unit_factor(end, &factor)) {
+ factor = get_unit_factor(end);
+ if (!factor) {
errno = EINVAL;
return 0;
}
- uval = labs(val);
- uval *= factor;
- if (uval > max || labs(val) > uval) {
+ uval = val < 0 ? -val : val;
+ if (unsigned_mult_overflows(factor, uval) ||
+ factor * uval > max) {
errno = ERANGE;
return 0;
}
if (value && *value) {
char *end;
uintmax_t val;
- uintmax_t oldval;
+ uintmax_t factor;
errno = 0;
val = strtoumax(value, &end, 0);
if (errno == ERANGE)
return 0;
- oldval = val;
- if (!parse_unit_factor(end, &val)) {
+ factor = get_unit_factor(end);
+ if (!factor) {
errno = EINVAL;
return 0;
}
- if (val > max || oldval > val) {
+ if (unsigned_mult_overflows(factor, val) ||
+ factor * val > max) {
errno = ERANGE;
return 0;
}
+ val *= factor;
*ret = val;
return 1;
}
if test "$git_cv_ld_rpath" = "yes"; then
CC_LD_DYNPATH=-rpath
else
- CC_LD_DYNPATH=
- AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+ AC_CACHE_CHECK([if linker supports -Wl,+b,], git_cv_ld_wl_b, [
+ SAVE_LDFLAGS="${LDFLAGS}"
+ LDFLAGS="${SAVE_LDFLAGS} -Wl,+b,/"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_wl_b=yes], [git_cv_ld_wl_b=no])
+ LDFLAGS="${SAVE_LDFLAGS}"
+ ])
+ if test "$git_cv_ld_wl_b" = "yes"; then
+ CC_LD_DYNPATH=-Wl,+b,
+ else
+ CC_LD_DYNPATH=
+ AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+ fi
fi
fi
fi
@@
-type T;
-T *dst;
-T *src;
-expression n;
+expression dst, src, n, E;
@@
-- memcpy(dst, src, (n) * sizeof(*dst));
-+ COPY_ARRAY(dst, src, n);
+ memcpy(dst, src, n * sizeof(
+- E[...]
++ *(E)
+ ))
@@
type T;
-T *dst;
-T *src;
-expression n;
+T *ptr;
+T[] arr;
+expression E, n;
@@
-- memcpy(dst, src, (n) * sizeof(*src));
-+ COPY_ARRAY(dst, src, n);
+(
+ memcpy(ptr, E,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+ )
+|
+ memcpy(arr, E,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+ )
+|
+ memcpy(E, ptr,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+ )
+|
+ memcpy(E, arr,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+ )
+)
@@
type T;
-T *dst;
-T *src;
+T *dst_ptr;
+T *src_ptr;
+T[] dst_arr;
+T[] src_arr;
expression n;
@@
-- memcpy(dst, src, (n) * sizeof(T));
-+ COPY_ARRAY(dst, src, n);
+(
+- memcpy(dst_ptr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_ptr, n)
+|
+- memcpy(dst_ptr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_arr, n)
+|
+- memcpy(dst_arr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_ptr, n)
+|
+- memcpy(dst_arr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_arr, n)
+)
@@
type T;
if [ -z "$options" ]; then
# leading and trailing spaces are significant to make
# option removal work correctly.
- options=" $incl $(__git ${cmd/_/ } --git-completion-helper) "
+ options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
+
for i in $excl; do
options="${options/ $i / }"
done
static unsigned int hash_obj(const struct object *obj, unsigned int n)
{
- return sha1hash(obj->oid.hash) % n;
+ return oidhash(&obj->oid) % n;
}
static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
KHASH_INIT(str, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
-static khash_sha1 *island_marks;
+static kh_oid_map_t *island_marks;
static unsigned island_counter;
static unsigned island_counter_core;
* If we don't have a bitmap for the target, we can delta it
* against anything -- it's not an important object
*/
- trg_pos = kh_get_sha1(island_marks, trg_oid->hash);
+ trg_pos = kh_get_oid_map(island_marks, *trg_oid);
if (trg_pos >= kh_end(island_marks))
return 1;
* if the source (our delta base) doesn't have a bitmap,
* we don't want to base any deltas on it!
*/
- src_pos = kh_get_sha1(island_marks, src_oid->hash);
+ src_pos = kh_get_oid_map(island_marks, *src_oid);
if (src_pos >= kh_end(island_marks))
return 0;
if (!island_marks)
return 0;
- a_pos = kh_get_sha1(island_marks, a->hash);
+ a_pos = kh_get_oid_map(island_marks, *a);
if (a_pos < kh_end(island_marks))
a_bitmap = kh_value(island_marks, a_pos);
- b_pos = kh_get_sha1(island_marks, b->hash);
+ b_pos = kh_get_oid_map(island_marks, *b);
if (b_pos < kh_end(island_marks))
b_bitmap = kh_value(island_marks, b_pos);
khiter_t pos;
int hash_ret;
- pos = kh_put_sha1(island_marks, obj->oid.hash, &hash_ret);
+ pos = kh_put_oid_map(island_marks, obj->oid, &hash_ret);
if (hash_ret)
kh_value(island_marks, pos) = island_bitmap_new(NULL);
khiter_t pos;
int hash_ret;
- pos = kh_put_sha1(island_marks, obj->oid.hash, &hash_ret);
+ pos = kh_put_oid_map(island_marks, obj->oid, &hash_ret);
if (hash_ret) {
/*
* We don't have one yet; make a copy-on-write of the
struct name_entry entry;
khiter_t pos;
- pos = kh_get_sha1(island_marks, ent->idx.oid.hash);
+ pos = kh_get_oid_map(island_marks, ent->idx.oid);
if (pos >= kh_end(island_marks))
continue;
if (S_ISGITLINK(entry.mode))
continue;
- obj = lookup_object(r, entry.oid.hash);
+ obj = lookup_object(r, &entry.oid);
if (!obj)
continue;
free(list);
}
-void load_delta_islands(struct repository *r)
+void load_delta_islands(struct repository *r, int progress)
{
- island_marks = kh_init_sha1();
+ island_marks = kh_init_oid_map();
remote_islands = kh_init_str();
git_config(island_config_callback, NULL);
for_each_ref(find_island_for_ref, NULL);
deduplicate_islands(r);
- fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
+ if (progress)
+ fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
}
void propagate_island_marks(struct commit *commit)
{
- khiter_t pos = kh_get_sha1(island_marks, commit->object.oid.hash);
+ khiter_t pos = kh_get_oid_map(island_marks, commit->object.oid);
if (pos < kh_end(island_marks)) {
struct commit_list *p;
for (i = 0; i < to_pack->nr_objects; ++i) {
struct object_entry *entry = &to_pack->objects[i];
- khiter_t pos = kh_get_sha1(island_marks, entry->idx.oid.hash);
+ khiter_t pos = kh_get_oid_map(island_marks, entry->idx.oid);
oe_set_layer(to_pack, entry, 1);
void resolve_tree_islands(struct repository *r,
int progress,
struct packing_data *to_pack);
-void load_delta_islands(struct repository *r);
+void load_delta_islands(struct repository *r, int progress);
void propagate_island_marks(struct commit *commit);
int compute_pack_layers(struct packing_data *to_pack);
first = 0;
last = rename_dst_nr;
while (last > first) {
- int next = (last + first) >> 1;
+ int next = first + ((last - first) >> 1);
struct diff_rename_dst *dst = &(rename_dst[next]);
int cmp = strcmp(two->path, dst->two->path);
if (!cmp)
first = 0;
last = rename_src_nr;
while (last > first) {
- int next = (last + first) >> 1;
+ int next = first + ((last - first) >> 1);
struct diff_rename_src *src = &(rename_src[next]);
int cmp = strcmp(one->path, src->p->one->path);
if (!cmp)
hash_object_file(filespec->data, filespec->size, "blob",
&filespec->oid);
}
- return sha1hash(filespec->oid.hash);
+ return oidhash(&filespec->oid);
}
static int find_identical_files(struct hashmap *srcs,
first = 0;
last = dir->dirs_nr;
while (last > first) {
- int cmp, next = (last + first) >> 1;
+ int cmp, next = first + ((last - first) >> 1);
d = dir->dirs[next];
cmp = strncmp(name, d->name, len);
if (!cmp && strlen(d->name) > len)
struct tree_content *r = new_tree_content(t->entry_count + amt);
r->entry_count = t->entry_count;
r->delta_depth = t->delta_depth;
- memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+ COPY_ARRAY(r->entries, t->entries, t->entry_count);
release_tree_content(t);
return r;
}
* we cannot trust the object flags).
*/
if (!args->no_dependents &&
- ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+ ((o = lookup_object(the_repository, remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
if (skip_prefix(reader.line, "unshallow ", &arg)) {
if (get_oid_hex(arg, &oid))
die(_("invalid unshallow line: %s"), reader.line);
- if (!lookup_object(the_repository, oid.hash))
+ if (!lookup_object(the_repository, &oid))
die(_("object not found: %s"), reader.line);
/* make sure that it is parsed as shallow */
if (!parse_object(the_repository, &oid))
for (ref = *refs; ref; ref = ref->next) {
struct object *o = deref_tag(the_repository,
lookup_object(the_repository,
- ref->old_oid.hash),
+ &ref->old_oid),
NULL, 0);
if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
const struct object_id *remote = &ref->old_oid;
struct object *o;
- o = lookup_object(the_repository, remote->hash);
+ o = lookup_object(the_repository, remote);
if (!o || !(o->flags & COMPLETE)) {
retval = 0;
print_verbose(args, "want %s (%s)", oid_to_hex(remote),
* we cannot trust the object flags).
*/
if (!no_dependents &&
- ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+ ((o = lookup_object(the_repository, remote)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
if (skip_prefix(reader->line, "unshallow ", &arg)) {
if (get_oid_hex(arg, &oid))
die(_("invalid unshallow line: %s"), reader->line);
- if (!lookup_object(the_repository, oid.hash))
+ if (!lookup_object(the_repository, &oid))
die(_("object not found: %s"), reader->line);
/* make sure that it is parsed as shallow */
if (!parse_object(the_repository, &oid))
blob = lookup_blob(the_repository, oid);
if (!blob) {
- struct object *obj = lookup_unknown_object(oid->hash);
+ struct object *obj = lookup_unknown_object(oid);
ret |= report(options, obj,
FSCK_MSG_GITMODULES_BLOB,
"non-blob found at .gitmodules");
void fill_fsmonitor_bitmap(struct index_state *istate)
{
- int i;
+ unsigned int i;
istate->fsmonitor_dirty = ewah_new();
for (i = 0; i < istate->cache_nr; i++)
if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
size_t bol; /* beginning of line */
uint64_t last_update;
char *buf;
- int i;
+ unsigned int i;
if (!core_fsmonitor || istate->fsmonitor_has_run_once)
return;
void add_fsmonitor(struct index_state *istate)
{
- int i;
+ unsigned int i;
if (!istate->fsmonitor_last_update) {
trace_printf_key(&trace_fsmonitor, "add fsmonitor");
void tweak_fsmonitor(struct index_state *istate)
{
- int i;
+ unsigned int i;
int fsmonitor_enabled = git_config_get_fsmonitor();
if (istate->fsmonitor_dirty) {
next;
}
if ($ofs_delta) {
- $n_ofs += $ofs_delta;
+ if ($patch_mode_flavour{IS_REVERSE}) {
+ $o_ofs -= $ofs_delta;
+ } else {
+ $n_ofs += $ofs_delta;
+ }
$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
$n_ofs, $n_cnt);
}
}
checkout_staged_file () {
- tmpfile=$(expr \
- "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
- : '\([^ ]*\) ')
+ tmpfile="$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" &&
+ tmpfile=${tmpfile%%' '*}
if test $? -eq 0 && test -n "$tmpfile"
then
return 1
fi
- if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
- then
- ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
- else
+ # extract file extension from the last path component
+ case "${MERGED##*/}" in
+ *.*)
+ ext=.${MERGED##*.}
+ BASE=${MERGED%"$ext"}
+ ;;
+ *)
BASE=$MERGED
ext=
- fi
+ esac
mergetool_tmpdir_init
REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
- base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
- local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
- remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
+ base_mode= local_mode= remote_mode=
+
+ # here, $IFS is just a LF
+ for line in $f
+ do
+ mode=${line%% *} # 1st word
+ sha1=${line#"$mode "}
+ sha1=${sha1%% *} # 2nd word
+ case "${line#$mode $sha1 }" in # remainder
+ '1 '*)
+ base_mode=$mode
+ ;;
+ '2 '*)
+ local_mode=$mode local_sha1=$sha1
+ ;;
+ '3 '*)
+ remote_mode=$mode remote_sha1=$sha1
+ ;;
+ esac
+ done
if is_submodule "$local_mode" || is_submodule "$remote_mode"
then
echo "Submodule merge conflict for '$MERGED':"
- local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
- remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
describe_file "$local_mode" "local" "$local_sha1"
describe_file "$remote_mode" "remote" "$remote_sha1"
resolve_submodule_merge
-t|--tool*)
case "$#,$1" in
*,*=*)
- merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+ merge_tool=${1#*=}
;;
1,*)
usage ;;
self.needsGit = True
self.verbose = False
- # This is required for the "append" cloneExclude action
+ # This is required for the "append" update_shelve action
def ensure_value(self, attr, value):
if not hasattr(self, attr) or getattr(self, attr) is None:
setattr(self, attr, value)
die( "Error: %s is not found in client spec path" % depot_path )
return ""
+def cloneExcludeCallback(option, opt_str, value, parser):
+ # prepend "/" because the first "/" was consumed as part of the option itself.
+ # ("-//depot/A/..." becomes "/depot/A/..." after option parsing)
+ parser.values.cloneExclude += ["/" + re.sub(r"\.\.\.$", "", value)]
+
class P4Sync(Command, P4UserMap):
def __init__(self):
optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
help="Only sync files that are included in the Perforce Client Spec"),
optparse.make_option("-/", dest="cloneExclude",
- action="append", type="string",
+ action="callback", callback=cloneExcludeCallback, type="string",
help="exclude depot path"),
]
self.description = """Imports from Perforce into a git repository.\n
if self.verbose:
print("checkpoint finished: " + out)
+ def isPathWanted(self, path):
+ for p in self.cloneExclude:
+ if p.endswith("/"):
+ if p4PathStartsWith(path, p):
+ return False
+ # "-//depot/file1" without a trailing "/" should only exclude "file1", but not "file111" or "file1_dir/file2"
+ elif path.lower() == p.lower():
+ return False
+ for p in self.depotPaths:
+ if p4PathStartsWith(path, p):
+ return True
+ return False
+
def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
- self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
- for path in self.cloneExclude]
files = []
fnum = 0
while "depotFile%s" % fnum in commit:
path = commit["depotFile%s" % fnum]
-
- if [p for p in self.cloneExclude
- if p4PathStartsWith(path, p)]:
- found = False
- else:
- found = [p for p in self.depotPaths
- if p4PathStartsWith(path, p)]
+ found = self.isPathWanted(path)
if not found:
fnum = fnum + 1
continue
path = self.clientSpecDirs.map_in_client(path)
if self.detectBranches:
for b in self.knownBranches:
- if path.startswith(b + "/"):
+ if p4PathStartsWith(path, b + "/"):
path = path[len(b)+1:]
elif self.keepRepoPath:
fnum = 0
while "depotFile%s" % fnum in commit:
path = commit["depotFile%s" % fnum]
- found = [p for p in self.depotPaths
- if p4PathStartsWith(path, p)]
+ found = self.isPathWanted(path)
if not found:
fnum = fnum + 1
continue
for branch in self.knownBranches.keys():
# add a trailing slash so that a commit into qt/4.2foo
# doesn't end up in qt/4.2, e.g.
- if relPath.startswith(branch + "/"):
+ if p4PathStartsWith(relPath, branch + "/"):
if branch not in branches:
branches[branch] = []
branches[branch].append(file)
if currentChange < change:
earliestCommit = "^%s" % next
else:
- latestCommit = "%s" % next
+ if next == latestCommit:
+ die("Infinite loop while looking in ref %s for change %s. Check your branch mappings" % (ref, change))
+ latestCommit = "%s^@" % next
return ""
self.cloneDestination = depotPaths[-1]
depotPaths = depotPaths[:-1]
- self.cloneExclude = ["/"+p for p in self.cloneExclude]
for p in depotPaths:
if not p.startswith("//"):
sys.stderr.write('Depot paths must start with "//": %s\n' % p)
return p - hash_algos;
}
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
+
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
+
+struct object_id {
+ unsigned char hash[GIT_MAX_RAWSZ];
+};
+
+#define the_hash_algo the_repository->hash_algo
+
#endif
#ifndef HASHMAP_H
#define HASHMAP_H
+#include "hash.h"
+
/*
* Generic implementation of hash-based key-value mappings.
*
* the results will be different on big-endian and little-endian
* platforms, so they should not be stored or transferred over the net.
*/
-static inline unsigned int sha1hash(const unsigned char *sha1)
+static inline unsigned int oidhash(const struct object_id *oid)
{
/*
- * Equivalent to 'return *(unsigned int *)sha1;', but safe on
+ * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
* platforms that don't support unaligned reads.
*/
unsigned int hash;
- memcpy(&hash, sha1, sizeof(hash));
+ memcpy(&hash, oid->hash, sizeof(hash));
return hash;
}
{
struct object *obj;
- obj = lookup_object(the_repository, oid->hash);
+ obj = lookup_object(the_repository, oid);
if (!obj)
obj = parse_object(the_repository, oid);
* may be required for updating server info later.
*/
if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
- obj = lookup_unknown_object(ref->old_oid.hash);
+ obj = lookup_unknown_object(&ref->old_oid);
fprintf(stderr, " fetch %s for %s\n",
oid_to_hex(&ref->old_oid), refname);
add_fetch_request(obj);
code; \
} }
-#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)
-
-KHASH_INIT(sha1, const unsigned char *, void *, 1, sha1hash, __kh_oid_cmp)
-typedef kh_sha1_t khash_sha1;
-
-KHASH_INIT(sha1_pos, const unsigned char *, int, 1, sha1hash, __kh_oid_cmp)
-typedef kh_sha1_pos_t khash_sha1_pos;
-
-static inline unsigned int oid_hash(struct object_id oid)
+static inline unsigned int oidhash_by_value(struct object_id oid)
{
- return sha1hash(oid.hash);
+ return oidhash(&oid);
}
-static inline int oid_equal(struct object_id a, struct object_id b)
+static inline int oideq_by_value(struct object_id a, struct object_id b)
{
return oideq(&a, &b);
}
-KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
+KHASH_INIT(oid_set, struct object_id, int, 0, oidhash_by_value, oideq_by_value)
-KHASH_INIT(oid_map, struct object_id, void *, 1, oid_hash, oid_equal)
-typedef kh_oid_t khash_oid_map;
+KHASH_INIT(oid_map, struct object_id, void *, 1, oidhash_by_value, oideq_by_value)
-KHASH_INIT(oid_pos, struct object_id, int, 1, oid_hash, oid_equal)
-typedef kh_oid_pos_t khash_oid_pos;
+KHASH_INIT(oid_pos, struct object_id, int, 1, oidhash_by_value, oideq_by_value)
#endif /* __AC_KHASH_H */
#include "compat/obstack.h"
#define NCHAR (UCHAR_MAX + 1)
-#define obstack_chunk_alloc xmalloc
+/* adapter for `xmalloc()`, which takes `size_t`, not `long` */
+static void *obstack_chunk_alloc(long size)
+{
+ if (size < 0)
+ BUG("Cannot allocate a negative amount: %ld", size);
+ return xmalloc(size);
+}
#define obstack_chunk_free free
#define U(c) ((unsigned char) (c))
for (i = 0; i < NCHAR; ++i)
kwset->next[i] = next[U(trans[i])];
else
- memcpy(kwset->next, next, NCHAR * sizeof(struct trie *));
+ COPY_ARRAY(kwset->next, next, NCHAR);
}
/* Fix things up for any translation table. */
*/
if (errbuf)
- strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
+ strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg);
memset(filter_options, 0, sizeof(*filter_options));
return 1;
filename, &dtype, &filter_data->el,
r->index);
if (val < 0)
- val = filter_data->array_frame[filter_data->nr].defval;
+ val = filter_data->array_frame[filter_data->nr - 1].defval;
ALLOC_GROW(filter_data->array_frame, filter_data->nr + 1,
filter_data->alloc);
- filter_data->nr++;
filter_data->array_frame[filter_data->nr].defval = val;
filter_data->array_frame[filter_data->nr].child_prov_omit = 0;
+ filter_data->nr++;
/*
* A directory with this tree OID may appear in multiple
case LOFS_END_TREE:
assert(obj->type == OBJ_TREE);
- assert(filter_data->nr > 0);
+ assert(filter_data->nr > 1);
- frame = &filter_data->array_frame[filter_data->nr];
- filter_data->nr--;
+ frame = &filter_data->array_frame[--filter_data->nr];
/*
* Tell our parent directory if any of our children were
* provisionally omitted.
*/
- filter_data->array_frame[filter_data->nr].child_prov_omit |=
+ filter_data->array_frame[filter_data->nr - 1].child_prov_omit |=
frame->child_prov_omit;
/*
assert(obj->type == OBJ_BLOB);
assert((obj->flags & SEEN) == 0);
- frame = &filter_data->array_frame[filter_data->nr];
+ frame = &filter_data->array_frame[filter_data->nr - 1];
dtype = DT_REG;
val = is_excluded_from_list(pathname, strlen(pathname),
static void filter_sparse_free(void *filter_data)
{
struct filter_sparse_data *d = filter_data;
- /* TODO free contents of 'd' */
+ free(d->array_frame);
free(d);
}
ALLOC_GROW(d->array_frame, d->nr + 1, d->alloc);
d->array_frame[d->nr].defval = 0; /* default to include */
d->array_frame[d->nr].child_prov_omit = 0;
+ d->nr++;
*filter_fn = filter_sparse;
*filter_free_fn = filter_sparse_free;
else {
int begin = k_start;
int end = k_end;
+ assert(begin >= 0);
while (begin < end) {
- int mid = (begin + end) >> 1;
+ int mid = begin + ((end - begin) >> 1);
int cmp = strncmp(istate->cache[mid]->name, prefix->buf, prefix->len);
if (cmp == 0) /* mid has same prefix; look in second part */
begin = mid + 1;
* the specified sha1. n must be a power of 2. Please note that the
* return value is *not* consistent across computer architectures.
*/
-static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
+static unsigned int hash_obj(const struct object_id *oid, unsigned int n)
{
- return sha1hash(sha1) & (n - 1);
+ return oidhash(oid) & (n - 1);
}
/*
*/
static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
{
- unsigned int j = hash_obj(obj->oid.hash, size);
+ unsigned int j = hash_obj(&obj->oid, size);
while (hash[j]) {
j++;
* Look up the record for the given sha1 in the hash map stored in
* obj_hash. Return NULL if it was not found.
*/
-struct object *lookup_object(struct repository *r, const unsigned char *sha1)
+struct object *lookup_object(struct repository *r, const struct object_id *oid)
{
unsigned int i, first;
struct object *obj;
if (!r->parsed_objects->obj_hash)
return NULL;
- first = i = hash_obj(sha1, r->parsed_objects->obj_hash_size);
+ first = i = hash_obj(oid, r->parsed_objects->obj_hash_size);
while ((obj = r->parsed_objects->obj_hash[i]) != NULL) {
- if (hasheq(sha1, obj->oid.hash))
+ if (oideq(oid, &obj->oid))
break;
i++;
if (i == r->parsed_objects->obj_hash_size)
r->parsed_objects->obj_hash_size = new_hash_size;
}
-void *create_object(struct repository *r, const unsigned char *sha1, void *o)
+void *create_object(struct repository *r, const struct object_id *oid, void *o)
{
struct object *obj = o;
obj->parsed = 0;
obj->flags = 0;
- hashcpy(obj->oid.hash, sha1);
+ oidcpy(&obj->oid, oid);
if (r->parsed_objects->obj_hash_size - 1 <= r->parsed_objects->nr_objs * 2)
grow_object_hash(r);
}
}
-struct object *lookup_unknown_object(const unsigned char *sha1)
+struct object *lookup_unknown_object(const struct object_id *oid)
{
- struct object *obj = lookup_object(the_repository, sha1);
+ struct object *obj = lookup_object(the_repository, oid);
if (!obj)
- obj = create_object(the_repository, sha1,
+ obj = create_object(the_repository, oid,
alloc_object_node(the_repository));
return obj;
}
void *buffer;
struct object *obj;
- obj = lookup_object(r, oid->hash);
+ obj = lookup_object(r, oid);
if (obj && obj->parsed)
return obj;
return NULL;
}
parse_blob_buffer(lookup_blob(r, oid), NULL, 0);
- return lookup_object(r, oid->hash);
+ return lookup_object(r, oid);
}
buffer = repo_read_object_file(r, oid, &type, &size);
o->loaded_alternates = 0;
INIT_LIST_HEAD(&o->packed_git_mru);
- close_all_packs(o);
+ close_object_store(o);
o->packed_git = NULL;
}
* half-initialised objects, the caller is expected to initialize them
* by calling parse_object() on them.
*/
-struct object *lookup_object(struct repository *r, const unsigned char *sha1);
+struct object *lookup_object(struct repository *r, const struct object_id *oid);
-void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
+void *create_object(struct repository *r, const struct object_id *oid, void *obj);
void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet);
struct object *parse_object_buffer(struct repository *r, const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
/** Returns the object, with potentially excess memory allocated. **/
-struct object *lookup_unknown_object(const unsigned char *sha1);
+struct object *lookup_unknown_object(const struct object_id *oid);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
{
memset(&set->set, 0, sizeof(set->set));
if (initial_size)
- kh_resize_oid(&set->set, initial_size);
+ kh_resize_oid_set(&set->set, initial_size);
}
int oidset_contains(const struct oidset *set, const struct object_id *oid)
{
- khiter_t pos = kh_get_oid(&set->set, *oid);
+ khiter_t pos = kh_get_oid_set(&set->set, *oid);
return pos != kh_end(&set->set);
}
int oidset_insert(struct oidset *set, const struct object_id *oid)
{
int added;
- kh_put_oid(&set->set, *oid, &added);
+ kh_put_oid_set(&set->set, *oid, &added);
return !added;
}
int oidset_remove(struct oidset *set, const struct object_id *oid)
{
- khiter_t pos = kh_get_oid(&set->set, *oid);
+ khiter_t pos = kh_get_oid_set(&set->set, *oid);
if (pos == kh_end(&set->set))
return 0;
- kh_del_oid(&set->set, pos);
+ kh_del_oid_set(&set->set, pos);
return 1;
}
void oidset_clear(struct oidset *set)
{
- kh_release_oid(&set->set);
+ kh_release_oid_set(&set->set);
oidset_init(set, 0);
}
* A single oidset; should be zero-initialized (or use OIDSET_INIT).
*/
struct oidset {
- kh_oid_t set;
+ kh_oid_set_t set;
};
#define OIDSET_INIT { { 0 } }
void oidset_clear(struct oidset *set);
struct oidset_iter {
- kh_oid_t *set;
+ kh_oid_set_t *set;
khiter_t iter;
};
struct ewah_bitmap *blobs;
struct ewah_bitmap *tags;
- khash_sha1 *bitmaps;
- khash_sha1 *reused;
+ kh_oid_map_t *bitmaps;
+ kh_oid_map_t *reused;
struct packing_data *to_pack;
struct bitmapped_commit *selected;
seen_objects_nr = 0;
}
-static uint32_t find_object_pos(const unsigned char *hash)
+static uint32_t find_object_pos(const struct object_id *oid)
{
- struct object_entry *entry = packlist_find(writer.to_pack, hash, NULL);
+ struct object_entry *entry = packlist_find(writer.to_pack, oid, NULL);
if (!entry) {
die("Failed to write bitmap index. Packfile doesn't have full closure "
- "(object %s is missing)", hash_to_hex(hash));
+ "(object %s is missing)", oid_to_hex(oid));
}
return oe_in_pack_pos(writer.to_pack, entry);
static void show_object(struct object *object, const char *name, void *data)
{
struct bitmap *base = data;
- bitmap_set(base, find_object_pos(object->oid.hash));
+ bitmap_set(base, find_object_pos(&object->oid));
mark_as_seen(object);
}
add_to_include_set(struct bitmap *base, struct commit *commit)
{
khiter_t hash_pos;
- uint32_t bitmap_pos = find_object_pos(commit->object.oid.hash);
+ uint32_t bitmap_pos = find_object_pos(&commit->object.oid);
if (bitmap_get(base, bitmap_pos))
return 0;
- hash_pos = kh_get_sha1(writer.bitmaps, commit->object.oid.hash);
+ hash_pos = kh_get_oid_map(writer.bitmaps, commit->object.oid);
if (hash_pos < kh_end(writer.bitmaps)) {
struct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);
bitmap_or_ewah(base, bc->bitmap);
struct bitmap *base = bitmap_new();
struct rev_info revs;
- writer.bitmaps = kh_init_sha1();
+ writer.bitmaps = kh_init_oid_map();
writer.to_pack = to_pack;
if (writer.show_progress)
if (i >= reuse_after)
stored->flags |= BITMAP_FLAG_REUSE;
- hash_pos = kh_put_sha1(writer.bitmaps, object->oid.hash, &hash_ret);
+ hash_pos = kh_put_oid_map(writer.bitmaps, object->oid, &hash_ret);
if (hash_ret == 0)
die("Duplicate entry when writing index: %s",
oid_to_hex(&object->oid));
if (!(bitmap_git = prepare_bitmap_git(to_pack->repo)))
return;
- writer.reused = kh_init_sha1();
+ writer.reused = kh_init_oid_map();
rebuild_existing_bitmaps(bitmap_git, to_pack, writer.reused,
writer.show_progress);
/*
*/
}
-static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)
+static struct ewah_bitmap *find_reused_bitmap(const struct object_id *oid)
{
khiter_t hash_pos;
if (!writer.reused)
return NULL;
- hash_pos = kh_get_sha1(writer.reused, sha1);
+ hash_pos = kh_get_oid_map(writer.reused, *oid);
if (hash_pos >= kh_end(writer.reused))
return NULL;
if (next == 0) {
chosen = indexed_commits[i];
- reused_bitmap = find_reused_bitmap(chosen->object.oid.hash);
+ reused_bitmap = find_reused_bitmap(&chosen->object.oid);
} else {
chosen = indexed_commits[i + next];
for (j = 0; j <= next; ++j) {
struct commit *cm = indexed_commits[i + j];
- reused_bitmap = find_reused_bitmap(cm->object.oid.hash);
+ reused_bitmap = find_reused_bitmap(&cm->object.oid);
if (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {
chosen = cm;
break;
static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
const struct object_id *oid)
{
- khash_oid_pos *positions = bitmap_git->ext_index.positions;
+ kh_oid_pos_t *positions = bitmap_git->ext_index.positions;
khiter_t pos = kh_get_oid_pos(positions, *oid);
if (pos < kh_end(positions)) {
int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git,
struct packing_data *mapping,
- khash_sha1 *reused_bitmaps,
+ kh_oid_map_t *reused_bitmaps,
int show_progress)
{
uint32_t i, num_objects;
reposition = xcalloc(num_objects, sizeof(uint32_t));
for (i = 0; i < num_objects; ++i) {
- const unsigned char *sha1;
+ struct object_id oid;
struct revindex_entry *entry;
struct object_entry *oe;
entry = &bitmap_git->pack->revindex[i];
- sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
- oe = packlist_find(mapping, sha1, NULL);
+ nth_packed_object_oid(&oid, bitmap_git->pack, entry->nr);
+ oe = packlist_find(mapping, &oid, NULL);
if (oe)
reposition[i] = oe_in_pack_pos(mapping, oe) + 1;
if (!rebuild_bitmap(reposition,
lookup_stored_bitmap(stored),
rebuild)) {
- hash_pos = kh_put_sha1(reused_bitmaps,
- stored->oid.hash,
- &hash_ret);
+ hash_pos = kh_put_oid_map(reused_bitmaps,
+ stored->oid,
+ &hash_ret);
kh_value(reused_bitmaps, hash_pos) =
bitmap_to_ewah(rebuild);
}
struct packed_git **packfile,
uint32_t *entries, off_t *up_to);
int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
- khash_sha1 *reused_bitmaps, int show_progress);
+ kh_oid_map_t *reused_bitmaps, int show_progress);
void free_bitmap_index(struct bitmap_index *);
/*
#include "config.h"
static uint32_t locate_object_entry_hash(struct packing_data *pdata,
- const unsigned char *sha1,
+ const struct object_id *oid,
int *found)
{
uint32_t i, mask = (pdata->index_size - 1);
- i = sha1hash(sha1) & mask;
+ i = oidhash(oid) & mask;
while (pdata->index[i] > 0) {
uint32_t pos = pdata->index[i] - 1;
- if (hasheq(sha1, pdata->objects[pos].idx.oid.hash)) {
+ if (oideq(oid, &pdata->objects[pos].idx.oid)) {
*found = 1;
return i;
}
for (i = 0; i < pdata->nr_objects; i++) {
int found;
uint32_t ix = locate_object_entry_hash(pdata,
- entry->idx.oid.hash,
+ &entry->idx.oid,
&found);
if (found)
}
struct object_entry *packlist_find(struct packing_data *pdata,
- const unsigned char *sha1,
+ const struct object_id *oid,
uint32_t *index_pos)
{
uint32_t i;
if (!pdata->index_size)
return NULL;
- i = locate_object_entry_hash(pdata, sha1, &found);
+ i = locate_object_entry_hash(pdata, oid, &found);
if (index_pos)
*index_pos = i;
uint32_t index_pos);
struct object_entry *packlist_find(struct packing_data *pdata,
- const unsigned char *sha1,
+ const struct object_id *oid,
uint32_t *index_pos);
static inline uint32_t pack_name_hash(const char *name)
#include "tree.h"
#include "object-store.h"
#include "midx.h"
+#include "commit-graph.h"
char *odb_pack_name(struct strbuf *buf,
const unsigned char *sha1,
close_pack_index(p);
}
-void close_all_packs(struct raw_object_store *o)
+void close_object_store(struct raw_object_store *o)
{
struct packed_git *p;
close_midx(o->multi_pack_index);
o->multi_pack_index = NULL;
}
+
+ close_commit_graph(o);
}
/*
if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
poi_stack_alloc = alloc_nr(poi_stack_nr);
ALLOC_ARRAY(poi_stack, poi_stack_alloc);
- memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+ COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
} else {
ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
}
&& delta_stack == small_delta_stack) {
delta_stack_alloc = alloc_nr(delta_stack_nr);
ALLOC_ARRAY(delta_stack, delta_stack_alloc);
- memcpy(delta_stack, small_delta_stack,
- sizeof(*delta_stack)*delta_stack_nr);
+ COPY_ARRAY(delta_stack, small_delta_stack,
+ delta_stack_nr);
} else {
ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
}
unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
void close_pack_windows(struct packed_git *);
void close_pack(struct packed_git *);
-void close_all_packs(struct raw_object_store *o);
+void close_object_store(struct raw_object_store *o);
void unuse_pack(struct pack_window **);
void clear_delta_base_cache(void);
struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1, 0))
return -1;
- hashmap_entry_init(patch, sha1hash(header_only_patch_id.hash));
+ hashmap_entry_init(patch, oidhash(&header_only_patch_id));
return 0;
}
commit_formats_len = ARRAY_SIZE(builtin_formats);
builtin_formats_len = commit_formats_len;
ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
- memcpy(commit_formats, builtin_formats,
- sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+ COPY_ARRAY(commit_formats, builtin_formats,
+ ARRAY_SIZE(builtin_formats));
git_config(git_pretty_formats_config, NULL);
}
const char *path, void *data)
{
struct stat st;
- struct object *obj = lookup_object(the_repository, oid->hash);
+ struct object *obj = lookup_object(the_repository, oid);
if (obj && obj->flags & SEEN)
return 0;
struct packed_git *p, uint32_t pos,
void *data)
{
- struct object *obj = lookup_object(the_repository, oid->hash);
+ struct object *obj = lookup_object(the_repository, oid);
if (obj && obj->flags & SEEN)
return 0;
first = 0;
last = istate->cache_nr;
while (last > first) {
- int next = (last + first) >> 1;
+ int next = first + ((last - first) >> 1);
struct cache_entry *ce = istate->cache[next];
int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
if (!cmp)
#include "commit-slab.h"
#include "commit-graph.h"
#include "commit-reach.h"
+#include "worktree.h"
+#include "hashmap.h"
static struct ref_msg {
const char *gone;
struct object_info info;
} oi, oi_deref;
+struct ref_to_worktree_entry {
+ struct hashmap_entry ent; /* must be the first member! */
+ struct worktree *wt; /* key is wt->head_ref */
+};
+
+static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
+ const void *existing_hashmap_entry_to_test,
+ const void *key,
+ const void *keydata_aka_refname)
+{
+ const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
+ const struct ref_to_worktree_entry *k = key;
+ return strcmp(e->wt->head_ref,
+ keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
+}
+
+static struct ref_to_worktree_map {
+ struct hashmap map;
+ struct worktree **worktrees;
+} ref_to_worktree_map;
+
/*
* An atom is a valid field atom listed below, possibly prefixed with
* a "*" to denote deref_tag().
{ "flag", SOURCE_NONE },
{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+ { "worktreepath", SOURCE_NONE },
{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
{ "end", SOURCE_NONE },
{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
struct wt_status_state state;
memset(&state, 0, sizeof(state));
wt_status_get_state(the_repository, &state, 1);
+
+ /*
+ * The ( character must be hard-coded and not part of a localizable
+ * string, since the description is used as a sort key and compared
+ * with ref names.
+ */
+ strbuf_addch(&desc, '(');
if (state.rebase_in_progress ||
state.rebase_interactive_in_progress) {
if (state.branch)
- strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+ strbuf_addf(&desc, _("no branch, rebasing %s"),
state.branch);
else
- strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
+ strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
state.detached_from);
} else if (state.bisect_in_progress)
- strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+ strbuf_addf(&desc, _("no branch, bisect started on %s"),
state.branch);
else if (state.detached_from) {
if (state.detached_at)
- /*
- * TRANSLATORS: make sure this matches "HEAD
- * detached at " in wt-status.c
- */
- strbuf_addf(&desc, _("(HEAD detached at %s)"),
- state.detached_from);
+ strbuf_addstr(&desc, HEAD_DETACHED_AT);
else
- /*
- * TRANSLATORS: make sure this matches "HEAD
- * detached from " in wt-status.c
- */
- strbuf_addf(&desc, _("(HEAD detached from %s)"),
- state.detached_from);
+ strbuf_addstr(&desc, HEAD_DETACHED_FROM);
+ strbuf_addstr(&desc, state.detached_from);
}
else
- strbuf_addstr(&desc, _("(no branch)"));
+ strbuf_addstr(&desc, _("no branch"));
+ strbuf_addch(&desc, ')');
+
free(state.branch);
free(state.onto);
free(state.detached_from);
return 0;
}
+static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
+{
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (worktrees[i]->head_ref) {
+ struct ref_to_worktree_entry *entry;
+ entry = xmalloc(sizeof(*entry));
+ entry->wt = worktrees[i];
+ hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
+
+ hashmap_add(map, entry);
+ }
+ }
+}
+
+static void lazy_init_worktree_map(void)
+{
+ if (ref_to_worktree_map.worktrees)
+ return;
+
+ ref_to_worktree_map.worktrees = get_worktrees(0);
+ hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
+ populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
+}
+
+static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+{
+ struct hashmap_entry entry;
+ struct ref_to_worktree_entry *lookup_result;
+
+ lazy_init_worktree_map();
+
+ hashmap_entry_init(&entry, strhash(ref->refname));
+ lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
+
+ if (lookup_result)
+ return xstrdup(lookup_result->wt->path);
+ else
+ return xstrdup("");
+}
+
/*
* Parse the object referred by ref, and grab needed value.
*/
if (starts_with(name, "refname"))
refname = get_refname(atom, ref);
+ else if (!strcmp(name, "worktreepath")) {
+ if (ref->kind == FILTER_REFS_BRANCHES)
+ v->s = get_worktree_path(atom, ref);
+ else
+ v->s = xstrdup("");
+ continue;
+ }
else if (starts_with(name, "symref"))
refname = get_symref(atom, ref);
else if (starts_with(name, "upstream")) {
free_array_item(array->items[i]);
FREE_AND_NULL(array->items);
array->nr = array->alloc = 0;
+ if (ref_to_worktree_map.worktrees) {
+ hashmap_free(&(ref_to_worktree_map.map), 1);
+ free_worktrees(ref_to_worktree_map.worktrees);
+ ref_to_worktree_map.worktrees = NULL;
+ }
}
static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
{
- struct object *o = lookup_unknown_object(name->hash);
+ struct object *o = lookup_unknown_object(name);
if (o->type == OBJ_NONE) {
int type = oid_object_info(the_repository, name, NULL);
int sequencer_remove_state(struct replay_opts *opts)
{
struct strbuf buf = STRBUF_INIT;
- int i;
+ int i, ret = 0;
if (is_rebase_i(opts) &&
strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
char *eol = strchr(p, '\n');
if (eol)
*eol = '\0';
- if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
+ if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
warning(_("could not delete '%s'"), p);
+ ret = -1;
+ }
if (!eol)
break;
p = eol + 1;
strbuf_reset(&buf);
strbuf_addstr(&buf, get_dir(opts));
- remove_dir_recursively(&buf, 0);
+ if (remove_dir_recursively(&buf, 0))
+ ret = error(_("could not remove '%s'"), buf.buf);
strbuf_release(&buf);
- return 0;
+ return ret;
}
static const char *action_name(const struct replay_opts *opts)
{
/* Here we know that if s is in the list, it is at an index j
with j1 <= j < j2. */
- size_t j = (j1 + j2) >> 1;
+ size_t j = j1 + ((j2 - j1) >> 1);
int result = strcmp (slp->item[j], s);
if (result > 0)
* Add 2 objects, one with a non-NULL decoration and one with a NULL
* decoration.
*/
- one = lookup_unknown_object(one_oid.hash);
- two = lookup_unknown_object(two_oid.hash);
+ one = lookup_unknown_object(&one_oid);
+ two = lookup_unknown_object(&two_oid);
ret = add_decoration(&n, one, &decoration_a);
if (ret)
BUG("when adding a brand-new object, NULL should be returned");
ret = lookup_decoration(&n, two);
if (ret != &decoration_b)
BUG("lookup should return added declaration");
- three = lookup_unknown_object(three_oid.hash);
+ three = lookup_unknown_object(&three_oid);
ret = lookup_decoration(&n, three);
if (ret)
BUG("lookup for unknown object should return NULL");
)
'
+test_expect_success 'conditional include, onbranch' '
+ echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
+ echo "[test]nine=9" >.git/bar9 &&
+ git checkout -b master &&
+ test_must_fail git config test.nine &&
+ git checkout -b foo-branch &&
+ echo 9 >expect &&
+ git config test.nine >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, wildcard' '
+ echo "[includeIf \"onbranch:?oo-*/**\"]path=bar10" >>.git/config &&
+ echo "[test]ten=10" >.git/bar10 &&
+ git checkout -b not-foo-branch/a &&
+ test_must_fail git config test.ten &&
+
+ echo 10 >expect &&
+ git checkout -b foo-branch/a/b/c &&
+ git config test.ten >actual &&
+ test_cmp expect actual &&
+
+ git checkout -b moo-bar/a &&
+ git config test.ten >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'conditional include, onbranch, implicit /** for /' '
+ echo "[includeIf \"onbranch:foo-dir/\"]path=bar11" >>.git/config &&
+ echo "[test]eleven=11" >.git/bar11 &&
+ git checkout -b not-foo-dir/a &&
+ test_must_fail git config test.eleven &&
+
+ echo 11 >expect &&
+ git checkout -b foo-dir/a/b/c &&
+ git config test.eleven >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'include cycles are detected' '
cat >.gitconfig <<-\EOF &&
[test]value = gitconfig
git worktree add -f bazdir2 baz &&
git branch -M baz bam &&
test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
- test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
+ test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+ rm -r bazdir bazdir2 &&
+ git worktree prune
'
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
git checkout -b baz &&
- git worktree add -f bazdir3 baz &&
+ git worktree add -f bazdir baz &&
(
- cd bazdir3 &&
+ cd bazdir &&
git branch -M baz bam &&
test $(git rev-parse --abbrev-ref HEAD) = bam
) &&
- test $(git rev-parse --abbrev-ref HEAD) = bam
+ test $(git rev-parse --abbrev-ref HEAD) = bam &&
+ rm -r bazdir &&
+ git worktree prune
'
test_expect_success 'git branch -M master should work when master is checked out' '
test_expect_success 'deleting currently checked out branch fails' '
git worktree add -b my7 my7 &&
test_must_fail git -C my7 branch -d my7 &&
- test_must_fail git branch -d my7
+ test_must_fail git branch -d my7 &&
+ rm -r my7 &&
+ git worktree prune
'
test_expect_success 'test --track without .fetch entries' '
branch-two
EOF
git checkout branch-one &&
- git worktree add worktree branch-two &&
+ test_when_finished "
+ git worktree remove worktree_dir
+ " &&
+ git worktree add worktree_dir branch-two &&
{
git branch --show-current &&
- git -C worktree branch --show-current
+ git -C worktree_dir branch --show-current
} >actual &&
test_cmp expect actual
'
test_i18ncmp expect actual
'
+test_expect_success 'worktree colors correct' '
+ cat >expect <<-EOF &&
+ * <GREEN>(HEAD detached from fromtag)<RESET>
+ ambiguous<RESET>
+ branch-one<RESET>
+ + <CYAN>branch-two<RESET>
+ master<RESET>
+ ref-to-branch<RESET> -> branch-one
+ ref-to-remote<RESET> -> origin/branch-one
+ EOF
+ git worktree add worktree_dir branch-two &&
+ git branch --color >actual.raw &&
+ rm -r worktree_dir &&
+ git worktree prune &&
+ test_decode_color <actual.raw >actual &&
+ test_i18ncmp expect actual
+'
+
test_expect_success "set up color tests" '
echo "<RED>master<RESET>" >expect.color &&
echo "master" >expect.bare &&
test_cmp expect.color actual
'
+test_expect_success 'verbose output lists worktree path' '
+ one=$(git rev-parse --short HEAD) &&
+ two=$(git rev-parse --short master) &&
+ cat >expect <<-EOF &&
+ * (HEAD detached from fromtag) $one one
+ ambiguous $one one
+ branch-one $two two
+ + branch-two $one ($(pwd)/worktree_dir) one
+ master $two two
+ ref-to-branch $two two
+ ref-to-remote $two two
+ EOF
+ git worktree add worktree_dir branch-two &&
+ git branch -vv >actual &&
+ rm -r worktree_dir &&
+ git worktree prune &&
+ test_i18ncmp expect actual
+'
+
test_done
test -z "$(git show -s --format=%p HEAD^)"
'
-test_expect_success 'rebase -i --root when root has untracked file confilct' '
+test_expect_success 'rebase -i --root when root has untracked file conflict' '
test_when_finished "reset_rebase" &&
git checkout -b failing-root-pick A &&
echo x >file2 &&
test_cmp_rev HEAD "$(cat wt/b)"
'
+test_expect_success '--abort cleans up refs/rewritten' '
+ git checkout -b abort-cleans-refs-rewritten H &&
+ GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+ git rev-parse --verify refs/rewritten/onto &&
+ git rebase --abort &&
+ test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
+test_expect_success '--quit cleans up refs/rewritten' '
+ git checkout -b quit-cleans-refs-rewritten H &&
+ GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+ git rev-parse --verify refs/rewritten/onto &&
+ git rebase --quit &&
+ test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
test_expect_success 'post-rewrite hook and fixups work for merges' '
- git checkout -b post-rewrite &&
+ git checkout -b post-rewrite H &&
test_commit same1 &&
git reset --hard HEAD^ &&
test_commit same2 &&
test_cmp expected-2 actual
'
+test_expect_success 'checkout -p works with pathological context lines' '
+ test_write_lines a a a a a a >a &&
+ git add a &&
+ test_write_lines a b a b a b a b a b a > a&&
+ test_write_lines s n n y q | git checkout -p &&
+ test_write_lines a b a b a a b a b a >expect &&
+ test_cmp expect a
+'
test_done
git stash drop
'
+test_expect_success 'valid ref of the form "n", n < N' '
+ git stash clear &&
+ echo bar5 >file &&
+ echo bar6 >file2 &&
+ git add file2 &&
+ git stash &&
+ git stash show 0 &&
+ git stash branch tmp 0 &&
+ git checkout master &&
+ git stash &&
+ git stash apply 0 &&
+ git reset --hard &&
+ git stash pop 0 &&
+ git stash &&
+ git stash drop 0 &&
+ test_must_fail git stash drop
+'
+
test_expect_success 'branch: do not drop the stash if the branch exists' '
git stash clear &&
echo foo >file &&
php
python
ruby
+ rust
tex
custom1
custom2
--- /dev/null
+pub(self) fn RIGHT<T>(x: &[T]) where T: Debug {
+ let _ = x;
+ // a comment
+ let a = ChangeMe;
+}
--- /dev/null
+impl<'a, T: AsRef<[u8]>> std::RIGHT for Git<'a> {
+
+ pub fn ChangeMe(&self) -> () {
+ }
+}
--- /dev/null
+#[derive(Debug)]
+pub(super) struct RIGHT<'a> {
+ name: &'a str,
+ age: ChangeMe,
+}
--- /dev/null
+unsafe trait RIGHT<T> {
+ fn len(&self) -> u32;
+ fn ChangeMe(&self, n: u32) -> T;
+ fn iter<F>(&self, f: F) where F: Fn(T);
+}
test_path_is_file info/commit-graph
'
+test_expect_success 'close with correct error on bad input' '
+ cd "$TRASH_DIRECTORY/full" &&
+ echo doesnotexist >in &&
+ { git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
+ test "$ret" = 1 &&
+ test_i18ngrep "error adding pack" stderr
+'
+
test_expect_success 'create commits and repack' '
cd "$TRASH_DIRECTORY/full" &&
for i in $(test_seq 3)
git remote rm origin &&
git remote add one ../one &&
git remote add two ../two &&
- git fetch --multiple one two &&
+ GIT_TRACE=1 git fetch --multiple one two 2>trace &&
git branch -r > output &&
- test_cmp ../expect output)
+ test_cmp ../expect output &&
+ grep "built-in: git gc" trace >gc &&
+ test_line_count = 1 gc
+ )
'
test_expect_success 'git fetch --multiple (bad remote names)' '
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
-# Converts bytes into a form suitable for inclusion in a sed command. For
-# example, "printf 'ab\r\n' | hex_unpack" results in '\x61\x62\x0d\x0a'.
-sed_escape () {
- perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)' |
- sed 's/\(..\)/\\x\1/g'
+# Converts bytes into their hexadecimal representation. For example,
+# "printf 'ab\r\n' | hex_unpack" results in '61620d0a'.
+hex_unpack () {
+ perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)'
+}
+
+# Inserts $1 at the start of the string and every 2 characters thereafter.
+intersperse () {
+ sed 's/\(..\)/'$1'\1/g'
+}
+
+# Create a one-time-sed command to replace the existing packfile with $1.
+replace_packfile () {
+ # The protocol requires that the packfile be sent in sideband 1, hence
+ # the extra \x01 byte at the beginning.
+ printf "1,/packfile/!c %04x\\\\x01%s0000" \
+ "$(($(wc -c <$1) + 5))" \
+ "$(hex_unpack <$1 | intersperse '\\x')" \
+ >"$HTTPD_ROOT_PATH/one-time-sed"
}
test_expect_success 'upon cloning, check that all refs point to objects' '
# Replace the existing packfile with the crafted one. The protocol
# requires that the packfile be sent in sideband 1, hence the extra
# \x01 byte at the beginning.
- printf "1,/packfile/!c %04x\\\\x01%s0000" \
- "$(($(wc -c <incomplete.pack) + 5))" \
- "$(sed_escape <incomplete.pack)" \
- >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ replace_packfile incomplete.pack &&
# Use protocol v2 because the sed command looks for the "packfile"
# section header.
# Replace the existing packfile with the crafted one. The protocol
# requires that the packfile be sent in sideband 1, hence the extra
# \x01 byte at the beginning.
- printf "1,/packfile/!c %04x\\\\x01%s0000" \
- "$(($(wc -c <incomplete.pack) + 5))" \
- "$(sed_escape <incomplete.pack)" \
- >"$HTTPD_ROOT_PATH/one-time-sed" &&
+ replace_packfile incomplete.pack &&
# Use protocol v2 because the sed command looks for the "packfile"
# section header.
! test -e "$HTTPD_ROOT_PATH/one-time-sed"
'
+test_expect_success 'tolerate server sending REF_DELTA against missing promisor objects' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ rm -rf "$SERVER" repo &&
+ test_create_repo "$SERVER" &&
+ test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+ test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+ # Create a commit with 2 blobs to be used as delta bases.
+ for i in $(test_seq 10)
+ do
+ echo "this is a line" >>"$SERVER/foo.txt" &&
+ echo "this is another line" >>"$SERVER/have.txt"
+ done &&
+ git -C "$SERVER" add foo.txt have.txt &&
+ git -C "$SERVER" commit -m bar &&
+ git -C "$SERVER" rev-parse HEAD:foo.txt >deltabase_missing &&
+ git -C "$SERVER" rev-parse HEAD:have.txt >deltabase_have &&
+
+ # Clone. The client has deltabase_have but not deltabase_missing.
+ git -c protocol.version=2 clone --no-checkout \
+ --filter=blob:none $HTTPD_URL/one_time_sed/server repo &&
+ git -C repo hash-object -w -- "$SERVER/have.txt" &&
+
+ # Sanity check to ensure that the client does not have
+ # deltabase_missing.
+ git -C repo rev-list --objects --ignore-missing \
+ -- $(cat deltabase_missing) >objlist &&
+ test_line_count = 0 objlist &&
+
+ # Another commit. This commit will be fetched by the client.
+ echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/foo.txt" &&
+ echo "abcdefghijklmnopqrstuvwxyz" >>"$SERVER/have.txt" &&
+ git -C "$SERVER" add foo.txt have.txt &&
+ git -C "$SERVER" commit -m baz &&
+
+ # Pack a thin pack containing, among other things, HEAD:foo.txt
+ # delta-ed against HEAD^:foo.txt and HEAD:have.txt delta-ed against
+ # HEAD^:have.txt.
+ printf "%s\n--not\n%s\n" \
+ $(git -C "$SERVER" rev-parse HEAD) \
+ $(git -C "$SERVER" rev-parse HEAD^) |
+ git -C "$SERVER" pack-objects --thin --stdout >thin.pack &&
+
+ # Ensure that the pack contains one delta against HEAD^:foo.txt. Since
+ # the delta contains at least 26 novel characters, the size cannot be
+ # contained in 4 bits, so the object header will take up 2 bytes. The
+ # most significant nybble of the first byte is 0b1111 (0b1 to indicate
+ # that the header continues, and 0b111 to indicate REF_DELTA), followed
+ # by any 3 nybbles, then the OID of the delta base.
+ printf "f.,..%s" $(intersperse "," <deltabase_missing) >want &&
+ hex_unpack <thin.pack | intersperse "," >have &&
+ grep $(cat want) have &&
+
+ # Ensure that the pack contains one delta against HEAD^:have.txt,
+ # similar to the above.
+ printf "f.,..%s" $(intersperse "," <deltabase_have) >want &&
+ hex_unpack <thin.pack | intersperse "," >have &&
+ grep $(cat want) have &&
+
+ replace_packfile thin.pack &&
+
+ # Use protocol v2 because the sed command looks for the "packfile"
+ # section header.
+ test_config -C "$SERVER" protocol.version 2 &&
+
+ # Fetch the thin pack and ensure that index-pack is able to handle the
+ # REF_DELTA object with a missing promisor delta base.
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C repo -c protocol.version=2 fetch &&
+
+ # Ensure that the missing delta base was directly fetched, but not the
+ # one that the client has.
+ grep "want $(cat deltabase_missing)" trace &&
+ ! grep "want $(cat deltabase_have)" trace &&
+
+ # Ensure that the one-time-sed script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
+'
+
test_done
'
test_expect_success 'cloning without refspec' '
- GIT_REMOTE_TESTGIT_REFSPEC="" \
+ GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
git clone "testgit::${PWD}/server" local2 2>error &&
test_i18ngrep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
test_expect_success 'pulling without refspecs' '
(cd local2 &&
git reset --hard &&
- GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+ GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
test_i18ngrep "this remote helper should implement refspec capability" error &&
compare_refs local2 HEAD server HEAD
'
(cd local2 &&
echo content >>file &&
git commit -a -m ten &&
- GIT_REMOTE_TESTGIT_REFSPEC="" &&
- export GIT_REMOTE_TESTGIT_REFSPEC &&
+ GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
+ export GIT_REMOTE_TESTGIT_NOREFSPEC &&
test_must_fail git push 2>../error) &&
test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
'
compare_refs server HEAD local FETCH_HEAD
'
+test_expect_success 'fetch tag' '
+ (cd server &&
+ git tag v1.0
+ ) &&
+ (cd local &&
+ git fetch
+ ) &&
+ compare_refs local v1.0 server v1.0
+'
+
test_done
url=$2
dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
-default_refspec="refs/heads/*:${prefix}/heads/*"
+h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
+t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
+if test -n "$GIT_REMOTE_TESTGIT_NOREFSPEC"
+then
+ h_refspec=""
+ t_refspec=""
+fi
GIT_DIR="$url/.git"
export GIT_DIR
capabilities)
echo 'import'
echo 'export'
- test -n "$refspec" && echo "refspec $refspec"
+ test -n "$h_refspec" && echo "refspec $h_refspec"
+ test -n "$t_refspec" && echo "refspec $t_refspec"
if test -n "$gitmarks"
then
echo "*import-marks $gitmarks"
echo
;;
list)
- git for-each-ref --format='? %(refname)' 'refs/heads/'
+ git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
head=$(git symbolic-ref HEAD)
echo "@$head HEAD"
echo
echo "feature done"
git fast-export \
+ ${h_refspec:+"--refspec=$h_refspec"} \
+ ${t_refspec:+"--refspec=$t_refspec"} \
${testgitmarks:+"--import-marks=$testgitmarks"} \
${testgitmarks:+"--export-marks=$testgitmarks"} \
- $refs |
- sed -e "s#refs/heads/#${prefix}/heads/#g"
+ $refs
echo "done"
;;
export)
! grep one output
'
+test_expect_success 'rev-list --objects --no-object-names has no space/names' '
+ git rev-list --objects --no-object-names HEAD >output &&
+ ! grep wanted_file output &&
+ ! grep unwanted_file output &&
+ ! grep " " output
+'
+
+test_expect_success 'rev-list --objects --no-object-names works with cat-file' '
+ git rev-list --objects --no-object-names --all >list-output &&
+ git cat-file --batch-check <list-output >cat-output &&
+ ! grep missing cat-output
+'
+
+test_expect_success '--no-object-names and --object-names are last-one-wins' '
+ git rev-list --objects --no-object-names --object-names --all >output &&
+ grep wanted_file output &&
+ git rev-list --objects --object-names --no-object-names --all >output &&
+ ! grep wanted_file output
+'
+
test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
git commit --allow-empty -m another &&
git tag -a -m "annotated" v1.0 &&
test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
'
+test_expect_success 'validate worktree atom' '
+ cat >expect <<-EOF &&
+ master: $(pwd)
+ master_worktree: $(pwd)/worktree_dir
+ side: not checked out
+ EOF
+ git worktree add -b master_worktree worktree_dir master &&
+ git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
+ rm -r worktree_dir &&
+ git worktree prune &&
+ test_cmp expect actual
+'
+
test_done
test_cmp expect actual
'
+get_tag_header gpgsign-enabled $commit commit $time >expect
+echo "A message" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'git tag configured tag.gpgsign enables GPG sign' \
+ 'test_config tag.gpgsign true &&
+ git tag -m "A message" gpgsign-enabled &&
+ get_tag_msg gpgsign-enabled>actual &&
+ test_cmp expect actual
+'
+
+get_tag_header no-sign $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success GPG \
+ 'git tag --no-sign configured tag.gpgsign skip GPG sign' \
+ 'test_config tag.gpgsign true &&
+ git tag -a --no-sign -m "A message" no-sign &&
+ get_tag_msg no-sign>actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG \
'trying to create a signed tag with non-existing -F file should fail' '
! test -f nonexistingfile &&
test_cmp expected actual
'
-test_expect_success 'with message that has comments' '
- cat basic_message >message_with_comments &&
- sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
- # comment
-
- # other comment
- Cc: Z
- # yet another comment
- Reviewed-by: Johan
- Reviewed-by: Z
- # last comment
-
- EOF
- cat basic_patch >>message_with_comments &&
- cat basic_message >expected &&
- cat >>expected <<-\EOF &&
- # comment
-
- Reviewed-by: Johan
- Cc: Peff
- # last comment
-
- EOF
- cat basic_patch >>expected &&
- git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
- test_cmp expected actual
-'
+# Cover multiple comment characters with the same test input.
+for char in "#" ";"
+do
+ case "$char" in
+ "#")
+ # This is the default, so let's explicitly _not_
+ # set any config to make sure it behaves as we expect.
+ ;;
+ *)
+ config="-c core.commentChar=$char"
+ ;;
+ esac
+
+ test_expect_success "with message that has comments ($char)" '
+ cat basic_message >message_with_comments &&
+ sed -e "s/ Z\$/ /" \
+ -e "s/#/$char/g" >>message_with_comments <<-EOF &&
+ # comment
+
+ # other comment
+ Cc: Z
+ # yet another comment
+ Reviewed-by: Johan
+ Reviewed-by: Z
+ # last comment
+
+ EOF
+ cat basic_patch >>message_with_comments &&
+ cat basic_message >expected &&
+ sed -e "s/#/$char/g" >>expected <<-\EOF &&
+ # comment
+
+ Reviewed-by: Johan
+ Cc: Peff
+ # last comment
+
+ EOF
+ cat basic_patch >>expected &&
+ git $config interpret-trailers \
+ --trim-empty --trailer "Cc: Peff" \
+ message_with_comments >actual &&
+ test_cmp expected actual
+ '
+done
test_expect_success 'with message that has an old style conflict block' '
cat basic_message >message_with_comments &&
git checkout -b test$test_count branch1 &&
git submodule update -N &&
test_must_fail git merge master &&
- ( yes "" | git mergetool both ) &&
- ( yes "" | git mergetool file1 file1 ) &&
- ( yes "" | git mergetool file2 "spaced name" ) &&
- ( yes "" | git mergetool subdir/file3 ) &&
- ( yes "d" | git mergetool file11 ) &&
- ( yes "d" | git mergetool file12 ) &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat file1)" = "master updated" &&
- test "$(cat file2)" = "master new" &&
- test "$(cat subdir/file3)" = "master new sub" &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
+ yes "" | git mergetool both &&
+ yes "" | git mergetool file1 file1 &&
+ yes "" | git mergetool file2 "spaced name" &&
+ yes "" | git mergetool subdir/file3 &&
+ yes "d" | git mergetool file11 &&
+ yes "d" | git mergetool file12 &&
+ yes "l" | git mergetool submod &&
+ echo "master updated" >expect &&
+ test_cmp expect file1 &&
+ echo "master new" >expect &&
+ test_cmp expect file2 &&
+ echo "master new sub" >expect &&
+ test_cmp expect subdir/file3 &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
git commit -m "branch1 resolved with mergetool"
'
git checkout -b test$test_count branch1 &&
git submodule update -N &&
test_must_fail git merge master &&
- ( yes "" | git mergetool --gui both ) &&
- ( yes "" | git mergetool -g file1 file1 ) &&
- ( yes "" | git mergetool --gui file2 "spaced name" ) &&
- ( yes "" | git mergetool --gui subdir/file3 ) &&
- ( yes "d" | git mergetool --gui file11 ) &&
- ( yes "d" | git mergetool --gui file12 ) &&
- ( yes "l" | git mergetool --gui submod ) &&
- test "$(cat file1)" = "gui master updated" &&
- test "$(cat file2)" = "gui master new" &&
- test "$(cat subdir/file3)" = "gui master new sub" &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
+ yes "" | git mergetool --gui both &&
+ yes "" | git mergetool -g file1 file1 &&
+ yes "" | git mergetool --gui file2 "spaced name" &&
+ yes "" | git mergetool --gui subdir/file3 &&
+ yes "d" | git mergetool --gui file11 &&
+ yes "d" | git mergetool --gui file12 &&
+ yes "l" | git mergetool --gui submod &&
+ echo "gui master updated" >expect &&
+ test_cmp expect file1 &&
+ echo "gui master new" >expect &&
+ test_cmp expect file2 &&
+ echo "gui master new sub" >expect &&
+ test_cmp expect subdir/file3 &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
git commit -m "branch1 resolved with mergetool"
'
git checkout -b test$test_count branch1 &&
git submodule update -N &&
test_must_fail git merge master &&
- ( yes "" | git mergetool --gui both ) &&
- ( yes "" | git mergetool -g file1 file1 ) &&
- ( yes "" | git mergetool --gui file2 "spaced name" ) &&
- ( yes "" | git mergetool --gui subdir/file3 ) &&
- ( yes "d" | git mergetool --gui file11 ) &&
- ( yes "d" | git mergetool --gui file12 ) &&
- ( yes "l" | git mergetool --gui submod ) &&
- test "$(cat file1)" = "master updated" &&
- test "$(cat file2)" = "master new" &&
- test "$(cat subdir/file3)" = "master new sub" &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
+ yes "" | git mergetool --gui both &&
+ yes "" | git mergetool -g file1 file1 &&
+ yes "" | git mergetool --gui file2 "spaced name" &&
+ yes "" | git mergetool --gui subdir/file3 &&
+ yes "d" | git mergetool --gui file11 &&
+ yes "d" | git mergetool --gui file12 &&
+ yes "l" | git mergetool --gui submod &&
+ echo "master updated" >expect &&
+ test_cmp expect file1 &&
+ echo "master new" >expect &&
+ test_cmp expect file2 &&
+ echo "master new sub" >expect &&
+ test_cmp expect subdir/file3 &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
git commit -m "branch1 resolved with mergetool"
'
test_config core.autocrlf true &&
git checkout -b test$test_count branch1 &&
test_must_fail git merge master &&
- ( yes "" | git mergetool file1 ) &&
- ( yes "" | git mergetool file2 ) &&
- ( yes "" | git mergetool "spaced name" ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "" | git mergetool subdir/file3 ) &&
- ( yes "d" | git mergetool file11 ) &&
- ( yes "d" | git mergetool file12 ) &&
- ( yes "r" | git mergetool submod ) &&
+ yes "" | git mergetool file1 &&
+ yes "" | git mergetool file2 &&
+ yes "" | git mergetool "spaced name" &&
+ yes "" | git mergetool both &&
+ yes "" | git mergetool subdir/file3 &&
+ yes "d" | git mergetool file11 &&
+ yes "d" | git mergetool file12 &&
+ yes "r" | git mergetool submod &&
test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
git commit -m "branch1 resolved with mergetool - autocrlf"
'
(
cd subdir &&
test_must_fail git merge master &&
- ( yes "" | git mergetool file3 ) &&
- test "$(cat file3)" = "master new sub"
+ yes "" | git mergetool file3 &&
+ echo "master new sub" >expect &&
+ test_cmp expect file3
)
'
(
cd subdir &&
test_must_fail git merge master &&
- ( yes "" | git mergetool file3 ) &&
- ( yes "" | git mergetool ../file1 ) &&
- ( yes "" | git mergetool ../file2 ../spaced\ name ) &&
- ( yes "" | git mergetool ../both ) &&
- ( yes "d" | git mergetool ../file11 ) &&
- ( yes "d" | git mergetool ../file12 ) &&
- ( yes "l" | git mergetool ../submod ) &&
- test "$(cat ../file1)" = "master updated" &&
- test "$(cat ../file2)" = "master new" &&
- test "$(cat ../submod/bar)" = "branch1 submodule" &&
+ yes "" | git mergetool file3 &&
+ yes "" | git mergetool ../file1 &&
+ yes "" | git mergetool ../file2 ../spaced\ name &&
+ yes "" | git mergetool ../both &&
+ yes "d" | git mergetool ../file11 &&
+ yes "d" | git mergetool ../file12 &&
+ yes "l" | git mergetool ../submod &&
+ echo "master updated" >expect &&
+ test_cmp expect ../file1 &&
+ echo "master new" >expect &&
+ test_cmp expect ../file2 &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect ../submod/bar &&
git commit -m "branch1 resolved with mergetool - subdir"
)
'
git submodule update -N &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "d" | git mergetool file11 ) &&
- ( yes "d" | git mergetool file12 ) &&
- ( yes "l" | git mergetool submod ) &&
+ yes "d" | git mergetool file11 &&
+ yes "d" | git mergetool file12 &&
+ yes "l" | git mergetool submod &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging"
'
(
cd subdir &&
test_must_fail git merge master &&
- ( yes "r" | git mergetool ../submod ) &&
- ( yes "d" "d" | git mergetool --no-prompt ) &&
- test "$(cat ../file1)" = "master updated" &&
- test "$(cat ../file2)" = "master new" &&
- test "$(cat file3)" = "master new sub" &&
+ yes "r" | git mergetool ../submod &&
+ yes "d" "d" | git mergetool --no-prompt &&
+ echo "master updated" >expect &&
+ test_cmp expect ../file1 &&
+ echo "master new" >expect &&
+ test_cmp expect ../file2 &&
+ echo "master new sub" >expect &&
+ test_cmp expect file3 &&
( cd .. && git submodule update -N ) &&
- test "$(cat ../submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect ../submod/bar &&
git commit -m "branch2 resolved by mergetool from subdir"
)
'
(
cd subdir &&
test_must_fail git merge master &&
- ( yes "r" | git mergetool ../submod ) &&
- ( yes "d" "d" | git mergetool --no-prompt ) &&
- test "$(cat ../file1)" = "master updated" &&
- test "$(cat ../file2)" = "master new" &&
- test "$(cat file3)" = "master new sub" &&
+ yes "r" | git mergetool ../submod &&
+ yes "d" "d" | git mergetool --no-prompt &&
+ echo "master updated" >expect &&
+ test_cmp expect ../file1 &&
+ echo "master new" >expect &&
+ test_cmp expect ../file2 &&
+ echo "master new sub" >expect &&
+ test_cmp expect file3 &&
( cd .. && git submodule update -N ) &&
- test "$(cat ../submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect ../submod/bar &&
git commit -m "branch2 resolved by mergetool from subdir"
)
'
git checkout -b test$test_count branch1 &&
git submodule update -N &&
test_must_fail git merge master &&
- ( yes "l" | git mergetool --no-prompt submod ) &&
- ( yes "d" "d" | git mergetool --no-prompt ) &&
+ yes "l" | git mergetool --no-prompt submod &&
+ yes "d" "d" | git mergetool --no-prompt &&
git submodule update -N &&
output="$(yes "n" | git mergetool --no-prompt)" &&
test "$output" = "No files need merging"
git submodule update -N &&
test_must_fail git merge master &&
- ( yes "" | git mergetool subdir ) &&
+ yes "" | git mergetool subdir &&
- test "$(cat subdir/file3)" = "master new sub"
+ echo "master new sub" >expect &&
+ test_cmp expect subdir/file3
'
test_expect_success 'mergetool delete/delete conflict' '
git checkout -b test$test_count.a test$test_count &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "r" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "r" | git mergetool submod &&
rmdir submod && mv submod-movedaside submod &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping module" &&
git submodule update -N &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "l" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "l" | git mergetool submod &&
test ! -e submod &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "r" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "r" | git mergetool submod &&
test ! -e submod &&
test -d submod.orig &&
git submodule update -N &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/bar)" = "master submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "l" | git mergetool submod &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
+ git submodule update -N &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping module"
git checkout -b test$test_count.a branch1 &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "r" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "r" | git mergetool submod &&
rmdir submod && mv submod-movedaside submod &&
- test "$(cat submod/bar)" = "branch1 submodule" &&
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping module" &&
git checkout -b test$test_count.b test$test_count &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "l" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "l" | git mergetool submod &&
git submodule update -N &&
- test "$(cat submod)" = "not a submodule" &&
+ echo "not a submodule" >expect &&
+ test_cmp expect submod &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping file" &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "r" | git mergetool submod ) &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "r" | git mergetool submod &&
test -d submod.orig &&
git submodule update -N &&
- test "$(cat submod)" = "not a submodule" &&
+ echo "not a submodule" >expect &&
+ test_cmp expect submod &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping file" &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
- ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 ) &&
- ( yes "" | git mergetool both ) &&
- ( yes "d" | git mergetool file11 file12 ) &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/bar)" = "master submodule" &&
- git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
+ yes "" | git mergetool both &&
+ yes "d" | git mergetool file11 file12 &&
+ yes "l" | git mergetool submod &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
+ git submodule update -N &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git commit -m "Merge resolved by keeping module"
test_must_fail git merge test$test_count.a &&
(
cd subdir &&
- ( yes "l" | git mergetool subdir_module )
+ yes "l" | git mergetool subdir_module
) &&
- test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+ echo "test$test_count.b" >expect &&
+ test_cmp expect subdir/subdir_module/file15 &&
git submodule update -N &&
- test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+ echo "test$test_count.b" >expect &&
+ test_cmp expect subdir/subdir_module/file15 &&
git reset --hard &&
git submodule update -N &&
test_must_fail git merge test$test_count.a &&
- ( yes "r" | git mergetool subdir/subdir_module ) &&
- test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
+ yes "r" | git mergetool subdir/subdir_module &&
+ echo "test$test_count.b" >expect &&
+ test_cmp expect subdir/subdir_module/file15 &&
git submodule update -N &&
- test "$(cat subdir/subdir_module/file15)" = "test$test_count.a" &&
+ echo "test$test_count.a" >expect &&
+ test_cmp expect subdir/subdir_module/file15 &&
git commit -m "branch1 resolved with mergetool"
'
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
- ( yes "l" | git mergetool submod ) &&
- test "$(cat submod/file16)" = "not a submodule" &&
+ yes "l" | git mergetool submod &&
+ echo "not a submodule" >expect &&
+ test_cmp expect submod/file16 &&
rm -rf submod.orig &&
git reset --hard &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
test ! -e submod.orig &&
- ( yes "r" | git mergetool submod ) &&
+ yes "r" | git mergetool submod &&
test -d submod.orig &&
- test "$(cat submod.orig/file16)" = "not a submodule" &&
+ echo "not a submodule" >expect &&
+ test_cmp expect submod.orig/file16 &&
rm -r submod.orig &&
mv submod-movedaside/.git submod &&
( cd submod && git clean -f && git reset --hard ) &&
git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
git reset --hard &&
rm -rf submod-movedaside &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
- ( yes "l" | git mergetool submod ) &&
+ yes "l" | git mergetool submod &&
git submodule update -N &&
- test "$(cat submod/bar)" = "master submodule" &&
+ echo "master submodule" >expect &&
+ test_cmp expect submod/bar &&
git reset --hard &&
git submodule update -N &&
test_must_fail git merge test$test_count &&
test -n "$(git ls-files -u)" &&
test ! -e submod.orig &&
- ( yes "r" | git mergetool submod ) &&
- test "$(cat submod/file16)" = "not a submodule" &&
+ yes "r" | git mergetool submod &&
+ echo "not a submodule" >expect &&
+ test_cmp expect submod/file16 &&
git reset --hard master &&
( cd submod && git clean -f && git reset --hard ) &&
)
'
+# Check that excluded files are omitted during import
+test_expect_success 'git p4 clone complex branches with excluded files' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList branch1:branch2 &&
+ git config --add git-p4.branchList branch1:branch3 &&
+ git config --add git-p4.branchList branch1:branch4 &&
+ git config --add git-p4.branchList branch1:branch5 &&
+ git config --add git-p4.branchList branch1:branch6 &&
+ git p4 clone --dest=. --detect-branches -//depot/branch1/file2 -//depot/branch2/file2 -//depot/branch3/file2 -//depot/branch4/file2 -//depot/branch5/file2 -//depot/branch6/file2 //depot@all &&
+ git log --all --graph --decorate --stat &&
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_file file3 &&
+ git reset --hard p4/depot/branch2 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_missing file3 &&
+ git reset --hard p4/depot/branch3 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_missing file3 &&
+ git reset --hard p4/depot/branch4 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_file file3 &&
+ git reset --hard p4/depot/branch5 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_file file3 &&
+ git reset --hard p4/depot/branch6 &&
+ test_path_is_file file1 &&
+ test_path_is_missing file2 &&
+ test_path_is_missing file3
+ )
+'
+
# From a report in http://stackoverflow.com/questions/11893688
# where --use-client-spec caused branch prefixes not to be removed;
# every file in git appeared into a subdirectory of the branch name.
)
'
+test_expect_success 'restart p4d (case folding enabled)' '
+ stop_and_cleanup_p4d &&
+ start_p4d -C1
+'
+
+#
+# 1: //depot/main/mf1
+# 2: integrate //depot/main/... -> //depot/branch1/...
+# 3: //depot/main/mf2
+# 4: //depot/BRANCH1/B1f3
+# 5: //depot/branch1/b1f4
+#
+test_expect_success !CASE_INSENSITIVE_FS 'basic p4 branches for case folding' '
+ (
+ cd "$cli" &&
+ mkdir -p main &&
+
+ echo mf1 >main/mf1 &&
+ p4 add main/mf1 &&
+ p4 submit -d "main/mf1" &&
+
+ p4 integrate //depot/main/... //depot/branch1/... &&
+ p4 submit -d "integrate main to branch1" &&
+
+ echo mf2 >main/mf2 &&
+ p4 add main/mf2 &&
+ p4 submit -d "main/mf2" &&
+
+ mkdir BRANCH1 &&
+ echo B1f3 >BRANCH1/B1f3 &&
+ p4 add BRANCH1/B1f3 &&
+ p4 submit -d "BRANCH1/B1f3" &&
+
+ echo b1f4 >branch1/b1f4 &&
+ p4 add branch1/b1f4 &&
+ p4 submit -d "branch1/b1f4"
+ )
+'
+
+# Check that files are properly split across branches when ignorecase is set
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone, branchList branch definition, ignorecase' '
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList main:branch1 &&
+ git config --type=bool core.ignoreCase true &&
+ git p4 clone --dest=. --detect-branches //depot@all &&
+
+ git log --all --graph --decorate --stat &&
+
+ git reset --hard p4/master &&
+ test_path_is_file mf1 &&
+ test_path_is_file mf2 &&
+ test_path_is_missing B1f3 &&
+ test_path_is_missing b1f4 &&
+
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file mf1 &&
+ test_path_is_missing mf2 &&
+ test_path_is_file B1f3 &&
+ test_path_is_file b1f4
+ )
+'
+
+# Check that files are properly split across branches when ignorecase is set, use-client-spec case
+test_expect_success !CASE_INSENSITIVE_FS 'git p4 clone with client-spec, branchList branch definition, ignorecase' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git config git-p4.branchList main:branch1 &&
+ git config --type=bool core.ignoreCase true &&
+ git p4 clone --dest=. --use-client-spec --detect-branches //depot@all &&
+
+ git log --all --graph --decorate --stat &&
+
+ git reset --hard p4/master &&
+ test_path_is_file mf1 &&
+ test_path_is_file mf2 &&
+ test_path_is_missing B1f3 &&
+ test_path_is_missing b1f4 &&
+
+ git reset --hard p4/depot/branch1 &&
+ test_path_is_file mf1 &&
+ test_path_is_missing mf2 &&
+ test_path_is_file B1f3 &&
+ test_path_is_file b1f4
+ )
+'
+
test_done
mkdir -p wanted discard &&
echo wanted >wanted/foo &&
echo discard >discard/foo &&
- p4 add wanted/foo discard/foo &&
+ echo discard_file >discard_file &&
+ echo discard_file_not >discard_file_not &&
+ p4 add wanted/foo discard/foo discard_file discard_file_not &&
p4 submit -d "initial revision"
)
'
(
cd "$git" &&
test_path_is_file wanted/foo &&
- test_path_is_file discard/foo
+ test_path_is_file discard/foo &&
+ test_path_is_file discard_file &&
+ test_path_is_file discard_file_not
)
'
(
cd "$git" &&
test_path_is_file wanted/foo &&
- test_path_is_missing discard/foo
+ test_path_is_missing discard/foo &&
+ test_path_is_file discard_file &&
+ test_path_is_file discard_file_not
+ )
+'
+
+test_expect_success 'clone, excluding single file, no trailing /' '
+ test_when_finished cleanup_git &&
+ git p4 clone -//depot/discard_file --dest="$git" //depot/...@all &&
+ (
+ cd "$git" &&
+ test_path_is_file wanted/foo &&
+ test_path_is_file discard/foo &&
+ test_path_is_missing discard_file &&
+ test_path_is_file discard_file_not
)
'
git p4 clone -//depot/discard/... --dest="$git" //depot/...@all &&
(
cd "$cli" &&
- p4 edit wanted/foo discard/foo &&
+ p4 edit wanted/foo discard/foo discard_file_not &&
date >>wanted/foo &&
date >>discard/foo &&
+ date >>discard_file_not &&
p4 submit -d "updating" &&
cd "$git" &&
git p4 sync -//depot/discard/... &&
test_path_is_file wanted/foo &&
- test_path_is_missing discard/foo
+ test_path_is_missing discard/foo &&
+ test_path_is_file discard_file &&
+ test_path_is_file discard_file_not
+ )
+'
+
+test_expect_success 'clone, then sync with exclude, no trailing /' '
+ test_when_finished cleanup_git &&
+ git p4 clone -//depot/discard/... -//depot/discard_file --dest="$git" //depot/...@all &&
+ (
+ cd "$cli" &&
+ p4 edit wanted/foo discard/foo discard_file_not &&
+ date >>wanted/foo &&
+ date >>discard/foo &&
+ date >>discard_file_not &&
+ p4 submit -d "updating" &&
+
+ cd "$git" &&
+ git p4 sync -//depot/discard/... -//depot/discard_file &&
+ test_path_is_file wanted/foo &&
+ test_path_is_missing discard/foo &&
+ test_path_is_missing discard_file &&
+ test_path_is_file discard_file_not
)
'
my @env = keys %ENV;
my $ok = join("|", qw(
TRACE
- TR2_
DEBUG
TEST
.*_TEST
struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
{
- struct object *obj = lookup_object(r, oid->hash);
+ struct object *obj = lookup_object(r, oid);
if (!obj)
- return create_object(r, oid->hash,
- alloc_tag_node(r));
+ return create_object(r, oid, alloc_tag_node(r));
return object_as_type(r, obj, OBJ_TAG, 0);
}
struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
{
- struct object *obj = lookup_object(r, oid->hash);
+ struct object *obj = lookup_object(r, oid);
if (!obj)
- return create_object(r, oid->hash,
- alloc_tree_node(r));
+ return create_object(r, oid, alloc_tree_node(r));
return object_as_type(r, obj, OBJ_TREE, 0);
}
return -1;
while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {
- struct object_id sha1;
+ struct object_id oid;
const char *p;
- if (parse_oid_hex(namebuf, &sha1, &p) || *p != '\n')
+ if (parse_oid_hex(namebuf, &oid, &p) || *p != '\n')
break;
- o = lookup_object(the_repository, sha1.hash);
+ o = lookup_object(the_repository, &oid);
if (o && o->type == OBJ_COMMIT) {
o->flags &= ~TMP_MARK;
}
{
struct commit_list *result;
- close_commit_graph(the_repository);
+ close_commit_graph(the_repository->objects);
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
send_shallow(writer, result);
free_commit_list(result);
static int mark_our_ref(const&nb