Merge branch 'vs/typofixes'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Jul 2018 19:20:31 +0000 (12:20 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Jul 2018 19:20:31 +0000 (12:20 -0700)
Doc fix.

* vs/typofixes:
Documentation: spelling and grammar fixes

196 files changed:
.gitignore
Documentation/RelNotes/2.19.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-apply.txt
Documentation/git-help.txt
Documentation/git-show-index.txt
Documentation/git-submodule.txt
Documentation/technical/commit-graph.txt
Documentation/technical/protocol-v2.txt
GIT-VERSION-GEN
Makefile
RelNotes
advice.c
advice.h
alloc.c
alloc.h [new file with mode: 0644]
apply.c
apply.h
archive-tar.c
archive-zip.c
archive.c
bisect.c
blame.c
blame.h
blob.c
branch.c
builtin.h
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/describe.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/hash-object.c
builtin/help.c
builtin/log.c
builtin/ls-tree.c
builtin/merge-tree.c
builtin/merge.c
builtin/mktag.c
builtin/mktree.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/pull.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/show-branch.c
builtin/show-index.c [new file with mode: 0644]
builtin/show-ref.c
builtin/submodule--helper.c
builtin/tag.c
builtin/unpack-file.c
builtin/unpack-objects.c
builtin/update-ref.c
builtin/verify-commit.c
bulk-checkin.c
bundle.c
cache-tree.c
cache.h
combine-diff.c
commit-graph.c
commit-graph.h
commit-slab-decl.h [new file with mode: 0644]
commit-slab-impl.h [new file with mode: 0644]
commit-slab.h
commit.c
commit.h
config.c
config.h
contrib/completion/git-completion.bash
contrib/credential/netrc/Makefile
contrib/credential/netrc/t-git-credential-netrc.sh
contrib/credential/netrc/test.pl
convert.c
diff-lib.c
diff.c
diffcore-rename.c
dir.c
entry.c
environment.c
ewah/bitmap.c
ewah/ewah_bitmap.c
ewah/ewah_io.c
ewah/ewah_rlw.c
ewah/ewok.h
ewah/ewok_rlw.h
fetch-pack.c
fsck.c
generate-cmdlist.sh
git-add--interactive.perl
git-p4.py
git-rebase--interactive.sh
git-rebase--preserve-merges.sh [new file with mode: 0644]
git-rebase.sh
git-submodule.sh
git.c
grep.c
grep.h
help.c
help.h
list-objects-filter-options.c
list-objects-filter.c
list-objects.c
log-tree.c
mailmap.c
match-trees.c
merge-blobs.c
merge-recursive.c
notes-cache.c
notes-merge.c
notes.c
object-store.h
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
packfile.h
parse-options.c
path.c
path.h
read-cache.c
ref-filter.c
refs.c
refs/packed-backend.c
refspec.c
refspec.h
remote-testsvn.c
remote.c
repository.c
repository.h
rerere.c
revision.c
revision.h
send-pack.c
sequencer.c
sha1-file.c
shallow.c
show-index.c [deleted file]
submodule-config.c
submodule.c
submodule.h
t/lib-submodule-update.sh
t/t0001-init.sh
t/t0020-crlf.sh
t/t0070-fundamental.sh
t/t0410-partial-clone.sh
t/t1004-read-tree-m-u-wf.sh
t/t2203-add-intent.sh
t/t3404-rebase-interactive.sh
t/t3423-rebase-reword.sh [new file with mode: 0755]
t/t3701-add-interactive.sh
t/t4011-diff-symlink.sh
t/t4014-format-patch.sh
t/t4254-am-corrupt.sh
t/t5318-commit-graph.sh
t/t5500-fetch-pack.sh
t/t5526-fetch-submodules.sh
t/t5537-fetch-shallow.sh
t/t5573-pull-verify-signatures.sh
t/t6036-recursive-corner-cases.sh
t/t6042-merge-rename-corner-cases.sh
t/t7030-verify-tag.sh
t/t7400-submodule-basic.sh
t/t7407-submodule-foreach.sh
t/t7508-status.sh
t/t7510-signed-commit.sh
t/t7612-merge-verify-signatures.sh
t/t9104-git-svn-follow-parent.sh
t/t9902-completion.sh
tag.c
tag.h
tree-walk.c
tree.c
unpack-trees.c
upload-pack.c
walker.c
wt-status.c
xdiff-interface.c
index 388cc4beee54faf9f06e9cfae75e7a256d368739..3284a1e9b1e80eae619024258ea96bb7e33bf943 100644 (file)
 /git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
+/git-rebase--preserve-merges
 /git-receive-pack
 /git-reflog
 /git-remote
diff --git a/Documentation/RelNotes/2.19.0.txt b/Documentation/RelNotes/2.19.0.txt
new file mode 100644 (file)
index 0000000..2b45b6f
--- /dev/null
@@ -0,0 +1,101 @@
+Git 2.19 Release Notes
+======================
+
+Updates since v2.18
+-------------------
+
+UI, Workflows & Features
+
+ * "git diff" compares the index and the working tree.  For paths
+   added with intent-to-add bit, the command shows the full contents
+   of them as added, but the paths themselves were not marked as new
+   files.  They are now shown as new by default.
+
+   "git apply" learned the "--intent-to-add" option so that an
+   otherwise working-tree-only application of a patch will add new
+   paths to the index marked with the "intent-to-add" bit.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The bulk of "git submodule foreach" has been rewritten in C.
+
+ * The in-core "commit" object had an all-purpose "void *util" field,
+   which was tricky to use especially in library-ish part of the
+   code.  All of the existing uses of the field has been migrated to a
+   more dedicated "commit-slab" mechanism and the field is eliminated.
+
+ * A less often used command "git show-index" has been modernized.
+   (merge fb3010c31f jk/show-index later to maint).
+
+ * The conversion to pass "the_repository" and then "a_repository"
+   throughout the object access API continues.
+
+ * Continuing with the idea to programatically enumerate various
+   pieces of data required for command line completion, teach the
+   codebase to report the list of configuration variables
+   subcommands care about to help complete them.
+
+ * Separate "rebase -p" codepath out of "rebase -i" implementation to
+   slim down the latter and make it easier to manage.
+
+ * Make refspec parsing codepath more robust.
+
+ * Some flaky tests have been fixed.
+
+ * Continuing with the idea to programmatically enumerate various
+   pieces of data required for command line completion, the codebase
+   has been taught to enumerate options prefixed with "--no-" to
+   negate them.
+
+ * Build and test procedure for netrc credential helper (in contrib/)
+   has been updated.
+
+
+Fixes since v2.18
+-----------------
+
+ * "git remote update" can take both a single remote nickname and a
+   nickname for remote groups, and the completion script (in contrib/)
+   has been taught about it.
+   (merge 9cd4382ad5 ls/complete-remote-update-names later to maint).
+
+ * "git fetch --shallow-since=<cutoff>" that specifies the cut-off
+   point that is newer than the existing history used to end up
+   grabbing the entire history.  Such a request now errors out.
+   (merge e34de73c56 nd/reject-empty-shallow-request later to maint).
+
+ * Fix for 2.17-era regression around `core.safecrlf`.
+   (merge 6cb09125be as/safecrlf-quiet-fix later to maint).
+
+ * The recent addition of "partial clone" experimental feature kicked
+   in when it shouldn't, namely, when there is no partial-clone filter
+   defined even if extensions.partialclone is set.
+   (merge cac1137dc4 jh/partial-clone later to maint).
+
+ * "git send-pack --signed" (hence "git push --signed" over the http
+   transport) did not read user ident from the config mechanism to
+   determine whom to sign the push certificate as, which has been
+   corrected.
+   (merge d067d98887 ms/send-pack-honor-config later to maint).
+
+ * "git fetch-pack --all" used to unnecessarily fail upon seeing an
+   annotated tag that points at an object other than a commit.
+   (merge c12c9df527 jk/fetch-all-peeled-fix later to maint).
+
+ * When user edits the patch in "git add -p" and the user's editor is
+   set to strip trailing whitespaces indiscriminately, an empty line
+   that is unchanged in the patch would become completely empty
+   (instead of a line with a sole SP on it).  The code introduced in
+   Git 2.17 timeframe failed to parse such a patch, but now it learned
+   to notice the situation and cope with it.
+   (merge f4d35a6b49 pw/add-p-recount later to maint).
+
+ * The code to try seeing if a fetch is necessary in a submodule
+   during a fetch with --recurse-submodules got confused when the path
+   to the submodule was changed in the range of commits in the
+   superproject, sometimes showing "(null)".  This has been corrected.
+
+ * Code cleanup.
+   (merge aee9be2ebe sg/update-ref-stdin-cleanup later to maint).
+   (merge 037714252f jc/clean-after-sanity-tests later to maint).
index 98ffbd20875aac168c17c05e3dbbe594fb877e81..536291cb6c1808cc0e556a8384f08ca2f54dd0a9 100644 (file)
@@ -1162,7 +1162,8 @@ color.diff.<slot>::
 color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
        of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
-       branches, remote-tracking branches, tags, stash and HEAD, respectively.
+       branches, remote-tracking branches, tags, stash and HEAD, respectively
+       and `grafted` for grafted commits.
 
 color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@ -3327,12 +3328,13 @@ submodule.<name>.ignore::
 submodule.<name>.active::
        Boolean value indicating if the submodule is of interest to git
        commands.  This config option takes precedence over the
-       submodule.active config option.
+       submodule.active config option. See linkgit:gitsubmodules[7] for
+       details.
 
 submodule.active::
        A repeated field which contains a pathspec used to match against a
        submodule's path to determine if the submodule is of interest to git
-       commands.
+       commands. See linkgit:gitsubmodules[7] for details.
 
 submodule.recurse::
        Specifies if commands recurse into submodules by default. This
index 67228494c00e1df676723072d0884f7705e532f1..b9aa39000fc8aa77d69751d9409a957139c2cff7 100644 (file)
@@ -9,7 +9,7 @@ git-apply - Apply a patch to files and/or to the index
 SYNOPSIS
 --------
 [verse]
-'git apply' [--stat] [--numstat] [--summary] [--check] [--index] [--3way]
+'git apply' [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way]
          [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
@@ -74,6 +74,14 @@ OPTIONS
        cached data, apply the patch, and store the result in the index
        without using the working tree. This implies `--index`.
 
+--intent-to-add::
+       When applying the patch only to the working tree, mark new
+       files to be added to the index later (see `--intent-to-add`
+       option in linkgit:git-add[1]). This option is ignored unless
+       running in a Git repository and `--index` is not specified.
+       Note that `--index` could be implied by other options such
+       as `--cached` or `--3way`.
+
 -3::
 --3way::
        When the patch does not apply cleanly, fall back on 3-way merge if
index a40fc38d8b425fae0a1ca074ecc8d603efc80f14..83d25d825aa5a9b68f3cff5290d1ebacef35ebde 100644 (file)
@@ -45,6 +45,11 @@ OPTIONS
        When used with `--verbose` print description for all recognized
        commands.
 
+-c::
+--config::
+       List all available configuration variables. This is a short
+       summary of the list in linkgit:git-config[1].
+
 -g::
 --guides::
        Prints a list of useful guides on the standard output. This
index a8a9509e0eb0bb416ce0a32f76c964a505da8f61..424e4ba84cf9b0444a41f0a163de0a58a6ac378a 100644 (file)
@@ -14,13 +14,27 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Read the idx file for a Git packfile created with
-'git pack-objects' command from the standard input, and
-dump its contents.
+Read the `.idx` file for a Git packfile (created with
+linkgit:git-pack-objects[1] or linkgit:git-index-pack[1]) from the
+standard input, and dump its contents. The output consists of one object
+per line, with each line containing two or three space-separated
+columns:
 
-The information it outputs is subset of what you can get from
-'git verify-pack -v'; this command only shows the packfile
-offset and SHA-1 of each object.
+  - the first column is the offset in bytes of the object within the
+    corresponding packfile
+
+  - the second column is the object id of the object
+
+  - if the index version is 2 or higher, the third column contains the
+    CRC32 of the object data
+
+The objects are output in the order in which they are found in the index
+file, which should be (in a correctly constructed file) sorted by object
+id.
+
+Note that you can get more information on a packfile by calling
+linkgit:git-verify-pack[1]. However, as this command considers only the
+index file itself, it's both faster and more flexible.
 
 GIT
 ---
index ef9d9d28a9cc581574bd700e467c636996432caa..ba3c4df550acfeb04a0a6b753a778816f031492c 100644 (file)
@@ -183,12 +183,17 @@ information too.
 
 foreach [--recursive] <command>::
        Evaluates an arbitrary shell command in each checked out submodule.
-       The command has access to the variables $name, $path, $sha1 and
-       $toplevel:
+       The command has access to the variables $name, $sm_path, $displaypath,
+       $sha1 and $toplevel:
        $name is the name of the relevant submodule section in `.gitmodules`,
-       $path is the name of the submodule directory relative to the
-       superproject, $sha1 is the commit as recorded in the superproject,
-       and $toplevel is the absolute path to the top-level of the superproject.
+       $sm_path is the path of the submodule as recorded in the immediate
+       superproject, $displaypath contains the relative path from the
+       current working directory to the submodules root directory,
+       $sha1 is the commit as recorded in the immediate
+       superproject, and $toplevel is the absolute path to the top-level
+       of the immediate superproject.
+       Note that to avoid conflicts with '$PATH' on Windows, the '$path'
+       variable is now a deprecated synonym of '$sm_path' variable.
        Any submodules defined in the superproject but not checked out are
        ignored by this command. Unless given `--quiet`, foreach prints the name
        of each submodule before evaluating the command.
index 0550c6d0dc317343dae53e792cc321fd0924bd84..e1a883eb462cd9bddbc192a315464282146ac433 100644 (file)
@@ -77,6 +77,29 @@ in the commit graph. We can treat these commits as having "infinite"
 generation number and walk until reaching commits with known generation
 number.
 
+We use the macro GENERATION_NUMBER_INFINITY = 0xFFFFFFFF to mark commits not
+in the commit-graph file. If a commit-graph file was written by a version
+of Git that did not compute generation numbers, then those commits will
+have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
+
+Since the commit-graph file is closed under reachability, we can guarantee
+the following weaker condition on all commits:
+
+    If A and B are commits with generation numbers N amd M, respectively,
+    and N < M, then A cannot reach B.
+
+Note how the strict inequality differs from the inequality when we have
+fully-computed generation numbers. Using strict inequality may result in
+walking a few extra commits, but the simplicity in dealing with commits
+with generation number *_INFINITY or *_ZERO is valuable.
+
+We use the macro GENERATION_NUMBER_MAX = 0x3FFFFFFF to for commits whose
+generation numbers are computed to be at least this value. We limit at
+this value since it is the largest value that can be stored in the
+commit-graph file using the 30 bits available to generation numbers. This
+presents another case where a commit can have generation number equal to
+that of a parent.
+
 Design Details
 --------------
 
@@ -98,18 +121,14 @@ Future Work
 - The 'commit-graph' subcommand does not have a "verify" mode that is
   necessary for integration with fsck.
 
-- The file format includes room for precomputed generation numbers. These
-  are not currently computed, so all generation numbers will be marked as
-  0 (or "uncomputed"). A later patch will include this calculation.
-
 - 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:
 
-    - paint_down_to_common()
     - 'log --topo-order'
+    - 'tag --merged'
 
 - Currently, parse_commit_gently() requires filling in the root tree
   object for a commit. This passes through lookup_tree() and consequently
index 49bda76d231d08173a48593d0910b3cbc934f01c..f58f24b1efb1cbf4fb959490afe00158a512625b 100644 (file)
@@ -64,9 +64,8 @@ When using the http:// or https:// transport a client makes a "smart"
 info/refs request as described in `http-protocol.txt` and requests that
 v2 be used by supplying "version=2" in the `Git-Protocol` header.
 
-   C: Git-Protocol: version=2
-   C:
    C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0
+   C: Git-Protocol: version=2
 
 A v2 server would reply:
 
index ed9d9e43a980e9f368bd8740b0b77183826eb0ea..920678a14b8ce28c50b5769fcf0bba022798b6ae 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.18.0
+DEF_VER=v2.18.GIT
 
 LF='
 '
index e4b503d259906d60a65204248aeb54962b12c062..0cb6590f24acdaec89fa85815e7af584c48ed620 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -620,6 +620,7 @@ SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--am
 SCRIPT_LIB += git-rebase--interactive
+SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
@@ -689,7 +690,6 @@ PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += show-index.o
 PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
@@ -1077,6 +1077,7 @@ BUILTIN_OBJS += builtin/send-pack.o
 BUILTIN_OBJS += builtin/serve.o
 BUILTIN_OBJS += builtin/shortlog.o
 BUILTIN_OBJS += builtin/show-branch.o
+BUILTIN_OBJS += builtin/show-index.o
 BUILTIN_OBJS += builtin/show-ref.o
 BUILTIN_OBJS += builtin/stripspace.o
 BUILTIN_OBJS += builtin/submodule--helper.o
@@ -2397,6 +2398,7 @@ LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
 LOCALIZED_SH += git-rebase--interactive.sh
+LOCALIZED_SH += git-rebase--preserve-merges.sh
 LOCALIZED_SH += git-sh-setup.sh
 LOCALIZED_PERL = $(SCRIPT_PERL)
 
index f6c58b347fd8338482625ea11ec1802baa6ea068..5d139ba7f1e20412d24b1397b33e2d4cde016d19 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.18.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.19.0.txt
\ No newline at end of file
index 370a56d0546bb3fc7aa4a62203c600e9a8af2d7a..52aa85bdfd9e9054c24445f259125bc4b81be038 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "color.h"
+#include "help.h"
 
 int advice_push_update_rejected = 1;
 int advice_push_non_ff_current = 1;
@@ -16,6 +17,7 @@ int advice_implicit_identity = 1;
 int advice_detached_head = 1;
 int advice_set_upstream_failure = 1;
 int advice_object_name_warning = 1;
+int advice_amworkdir = 1;
 int advice_rm_hints = 1;
 int advice_add_embedded_repo = 1;
 int advice_ignored_hook = 1;
@@ -53,28 +55,29 @@ static struct {
        const char *name;
        int *preference;
 } advice_config[] = {
-       { "pushupdaterejected", &advice_push_update_rejected },
-       { "pushnonffcurrent", &advice_push_non_ff_current },
-       { "pushnonffmatching", &advice_push_non_ff_matching },
-       { "pushalreadyexists", &advice_push_already_exists },
-       { "pushfetchfirst", &advice_push_fetch_first },
-       { "pushneedsforce", &advice_push_needs_force },
-       { "statushints", &advice_status_hints },
-       { "statusuoption", &advice_status_u_option },
-       { "commitbeforemerge", &advice_commit_before_merge },
-       { "resolveconflict", &advice_resolve_conflict },
-       { "implicitidentity", &advice_implicit_identity },
-       { "detachedhead", &advice_detached_head },
-       { "setupstreamfailure", &advice_set_upstream_failure },
-       { "objectnamewarning", &advice_object_name_warning },
-       { "rmhints", &advice_rm_hints },
-       { "addembeddedrepo", &advice_add_embedded_repo },
-       { "ignoredhook", &advice_ignored_hook },
-       { "waitingforeditor", &advice_waiting_for_editor },
-       { "graftfiledeprecated", &advice_graft_file_deprecated },
+       { "pushUpdateRejected", &advice_push_update_rejected },
+       { "pushNonFFCurrent", &advice_push_non_ff_current },
+       { "pushNonFFMatching", &advice_push_non_ff_matching },
+       { "pushAlreadyExists", &advice_push_already_exists },
+       { "pushFetchFirst", &advice_push_fetch_first },
+       { "pushNeedsForce", &advice_push_needs_force },
+       { "statusHints", &advice_status_hints },
+       { "statusUoption", &advice_status_u_option },
+       { "commitBeforeMerge", &advice_commit_before_merge },
+       { "resolveConflict", &advice_resolve_conflict },
+       { "implicitIdentity", &advice_implicit_identity },
+       { "detachedHead", &advice_detached_head },
+       { "setupStreamFailure", &advice_set_upstream_failure },
+       { "objectNameWarning", &advice_object_name_warning },
+       { "amWorkDir", &advice_amworkdir },
+       { "rmHints", &advice_rm_hints },
+       { "addEmbeddedRepo", &advice_add_embedded_repo },
+       { "ignoredHook", &advice_ignored_hook },
+       { "waitingForEditor", &advice_waiting_for_editor },
+       { "graftFileDeprecated", &advice_graft_file_deprecated },
 
        /* make this an alias for backward compatibility */
-       { "pushnonfastforward", &advice_push_update_rejected }
+       { "pushNonFastForward", &advice_push_update_rejected }
 };
 
 void advise(const char *advice, ...)
@@ -122,7 +125,7 @@ int git_default_advice_config(const char *var, const char *value)
                return 0;
 
        for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
-               if (strcmp(k, advice_config[i].name))
+               if (strcasecmp(k, advice_config[i].name))
                        continue;
                *advice_config[i].preference = git_config_bool(var, value);
                return 0;
@@ -131,6 +134,14 @@ int git_default_advice_config(const char *var, const char *value)
        return 0;
 }
 
+void list_config_advices(struct string_list *list, const char *prefix)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(advice_config); i++)
+               list_config_item(list, prefix, advice_config[i].name);
+}
+
 int error_resolve_conflict(const char *me)
 {
        if (!strcmp(me, "cherry-pick"))
index 9f5064e82a862e6daaabe3f23ea9ea896aad87e6..7e9377864f8fca1051ce3fd27f3def62b8d234eb 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -17,6 +17,7 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 extern int advice_set_upstream_failure;
 extern int advice_object_name_warning;
+extern int advice_amworkdir;
 extern int advice_rm_hints;
 extern int advice_add_embedded_repo;
 extern int advice_ignored_hook;
diff --git a/alloc.c b/alloc.c
index cf4f8b61e126c0e9992dc34c6cb52b6f896a565d..c2fc5d68886bf3f74a1f013a3a36c7b5dcad2258 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -4,8 +4,7 @@
  * Copyright (C) 2006 Linus Torvalds
  *
  * The standard malloc/free wastes too much space for objects, partly because
- * it maintains all the allocation infrastructure (which isn't needed, since
- * we never free an object descriptor anyway), but even more because it ends
+ * it maintains all the allocation infrastructure, but even more because it ends
  * up with maximal alignment because it doesn't know what the object alignment
  * for the new allocation is.
  */
@@ -15,6 +14,7 @@
 #include "tree.h"
 #include "commit.h"
 #include "tag.h"
+#include "alloc.h"
 
 #define BLOCKING 1024
 
@@ -30,8 +30,27 @@ struct alloc_state {
        int count; /* total number of nodes allocated */
        int nr;    /* number of nodes left in current allocation */
        void *p;   /* first free node in current allocation */
+
+       /* bookkeeping of allocations */
+       void **slabs;
+       int slab_nr, slab_alloc;
 };
 
+void *allocate_alloc_state(void)
+{
+       return xcalloc(1, sizeof(struct alloc_state));
+}
+
+void clear_alloc_state(struct alloc_state *s)
+{
+       while (s->slab_nr > 0) {
+               s->slab_nr--;
+               free(s->slabs[s->slab_nr]);
+       }
+
+       FREE_AND_NULL(s->slabs);
+}
+
 static inline void *alloc_node(struct alloc_state *s, size_t node_size)
 {
        void *ret;
@@ -39,61 +58,59 @@ static inline void *alloc_node(struct alloc_state *s, size_t node_size)
        if (!s->nr) {
                s->nr = BLOCKING;
                s->p = xmalloc(BLOCKING * node_size);
+
+               ALLOC_GROW(s->slabs, s->slab_nr + 1, s->slab_alloc);
+               s->slabs[s->slab_nr++] = s->p;
        }
        s->nr--;
        s->count++;
        ret = s->p;
        s->p = (char *)s->p + node_size;
        memset(ret, 0, node_size);
+
        return ret;
 }
 
-static struct alloc_state blob_state;
-void *alloc_blob_node(void)
+void *alloc_blob_node(struct repository *r)
 {
-       struct blob *b = alloc_node(&blob_state, sizeof(struct blob));
+       struct blob *b = alloc_node(r->parsed_objects->blob_state, sizeof(struct blob));
        b->object.type = OBJ_BLOB;
        return b;
 }
 
-static struct alloc_state tree_state;
-void *alloc_tree_node(void)
+void *alloc_tree_node(struct repository *r)
 {
-       struct tree *t = alloc_node(&tree_state, sizeof(struct tree));
+       struct tree *t = alloc_node(r->parsed_objects->tree_state, sizeof(struct tree));
        t->object.type = OBJ_TREE;
        return t;
 }
 
-static struct alloc_state tag_state;
-void *alloc_tag_node(void)
+void *alloc_tag_node(struct repository *r)
 {
-       struct tag *t = alloc_node(&tag_state, sizeof(struct tag));
+       struct tag *t = alloc_node(r->parsed_objects->tag_state, sizeof(struct tag));
        t->object.type = OBJ_TAG;
        return t;
 }
 
-static struct alloc_state object_state;
-void *alloc_object_node(void)
+void *alloc_object_node(struct repository *r)
 {
-       struct object *obj = alloc_node(&object_state, sizeof(union any_object));
+       struct object *obj = alloc_node(r->parsed_objects->object_state, sizeof(union any_object));
        obj->type = OBJ_NONE;
        return obj;
 }
 
-static struct alloc_state commit_state;
-
-unsigned int alloc_commit_index(void)
+unsigned int alloc_commit_index(struct repository *r)
 {
-       static unsigned int count;
-       return count++;
+       return r->parsed_objects->commit_count++;
 }
 
-void *alloc_commit_node(void)
+void *alloc_commit_node(struct repository *r)
 {
-       struct commit *c = alloc_node(&commit_state, sizeof(struct commit));
+       struct commit *c = alloc_node(r->parsed_objects->commit_state, sizeof(struct commit));
        c->object.type = OBJ_COMMIT;
-       c->index = alloc_commit_index();
+       c->index = alloc_commit_index(r);
        c->graph_pos = COMMIT_NOT_FROM_GRAPH;
+       c->generation = GENERATION_NUMBER_INFINITY;
        return c;
 }
 
@@ -104,9 +121,10 @@ static void report(const char *name, unsigned int count, size_t size)
 }
 
 #define REPORT(name, type)     \
-    report(#name, name##_state.count, name##_state.count * sizeof(type) >> 10)
+    report(#name, r->parsed_objects->name##_state->count, \
+                 r->parsed_objects->name##_state->count * sizeof(type) >> 10)
 
-void alloc_report(void)
+void alloc_report(struct repository *r)
 {
        REPORT(blob, struct blob);
        REPORT(tree, struct tree);
diff --git a/alloc.h b/alloc.h
new file mode 100644 (file)
index 0000000..3e4e828
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,19 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+struct tree;
+struct commit;
+struct tag;
+
+void *alloc_blob_node(struct repository *r);
+void *alloc_tree_node(struct repository *r);
+void *alloc_commit_node(struct repository *r);
+void *alloc_tag_node(struct repository *r);
+void *alloc_object_node(struct repository *r);
+void alloc_report(struct repository *r);
+unsigned int alloc_commit_index(struct repository *r);
+
+void *allocate_alloc_state(void);
+void clear_alloc_state(struct alloc_state *s);
+
+#endif
diff --git a/apply.c b/apply.c
index d79e61591b3814339fb8b2a39fb7d1e211d622c6..23a0f25ded853c29c3a6e891399dc5c1c2d8a753 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -9,6 +9,7 @@
 
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "blob.h"
 #include "delta.h"
 #include "diff.h"
@@ -141,6 +142,8 @@ int check_apply_state(struct apply_state *state, int force_apply)
                        return error(_("--cached outside a repository"));
                state->check_index = 1;
        }
+       if (state->ita_only && (state->check_index || is_not_gitdir))
+               state->ita_only = 0;
        if (state->check_index)
                state->unsafe_paths = 0;
 
@@ -4242,7 +4245,7 @@ static void patch_stats(struct apply_state *state, struct patch *patch)
 
 static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
 {
-       if (state->update_index) {
+       if (state->update_index && !state->ita_only) {
                if (remove_file_from_cache(patch->old_name) < 0)
                        return error(_("unable to remove %s from index"), patch->old_name);
        }
@@ -4265,15 +4268,15 @@ static int add_index_file(struct apply_state *state,
        int namelen = strlen(path);
        unsigned ce_size = cache_entry_size(namelen);
 
-       if (!state->update_index)
-               return 0;
-
        ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = namelen;
-       if (S_ISGITLINK(mode)) {
+       if (state->ita_only) {
+               ce->ce_flags |= CE_INTENT_TO_ADD;
+               set_object_name_for_intent_to_add_entry(ce);
+       } else if (S_ISGITLINK(mode)) {
                const char *s;
 
                if (!skip_prefix(buf, "Subproject commit ", &s) ||
@@ -4465,8 +4468,9 @@ static int create_file(struct apply_state *state, struct patch *patch)
 
        if (patch->conflicted_threeway)
                return add_conflicted_stages_file(state, patch);
-       else
+       else if (state->update_index)
                return add_index_file(state, path, mode, buf, size);
+       return 0;
 }
 
 /* phase zero is to remove, phase one is to create */
@@ -4686,7 +4690,7 @@ static int apply_patch(struct apply_state *state,
        if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
                state->apply = 0;
 
-       state->update_index = state->check_index && state->apply;
+       state->update_index = (state->check_index || state->ita_only) && state->apply;
        if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
                if (state->index_file)
                        hold_lock_file_for_update(&state->lock_file,
@@ -4941,6 +4945,8 @@ int apply_parse_options(int argc, const char **argv,
                        N_("instead of applying the patch, see if the patch is applicable")),
                OPT_BOOL(0, "index", &state->check_index,
                        N_("make sure the patch is applicable to the current index")),
+               OPT_BOOL('N', "intent-to-add", &state->ita_only,
+                       N_("mark new files with `git add --intent-to-add`")),
                OPT_BOOL(0, "cached", &state->cached,
                        N_("apply a patch without touching the working tree")),
                OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
diff --git a/apply.h b/apply.h
index dc4a0190570d060d23e8464f465232eddcf76deb..b80d8ba73633c042989e52a00f7df8ee3afcd192 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -45,6 +45,7 @@ struct apply_state {
        int check; /* preimage must match working tree, don't actually apply */
        int check_index; /* preimage must match the indexed version */
        int update_index; /* check_index && apply */
+       int ita_only;     /* add intent-to-add entries to the index */
 
        /* These control cosmetic aspect of the output */
        int diffstat; /* just show a diffstat, and don't actually apply */
index b6f58ddf38767d86f866bd9413292e279c6cfa73..7df85652463c6cee150398a00e93e03905861b96 100644 (file)
@@ -5,6 +5,7 @@
 #include "config.h"
 #include "tar.h"
 #include "archive.h"
+#include "object-store.h"
 #include "streaming.h"
 #include "run-command.h"
 
index 74f3fe9103420571c9f22273ae44ebb9d5715c78..abc556e5a7506cd0fe8e1e66a83c3781bfc04104 100644 (file)
@@ -6,6 +6,7 @@
 #include "archive.h"
 #include "streaming.h"
 #include "utf8.h"
+#include "object-store.h"
 #include "userdiff.h"
 #include "xdiff-interface.h"
 
index 4fe7bec60c12b3b1ab27c35b716b23f4535d170c..875dab64b606704d2510b05889a934ed885019ee 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "refs.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tree-walk.h"
 #include "attr.h"
index a579b50884f8e6f8ce8390308d39b2664d050583..6de1abd407ba3f8b7698133d469389913bfd92c3 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -12,6 +12,7 @@
 #include "bisect.h"
 #include "sha1-array.h"
 #include "argv-array.h"
+#include "commit-slab.h"
 
 static struct oid_array good_revs;
 static struct oid_array skipped_revs;
@@ -70,16 +71,19 @@ static void clear_distance(struct commit_list *list)
        }
 }
 
+define_commit_slab(commit_weight, int *);
+static struct commit_weight commit_weight;
+
 #define DEBUG_BISECT 0
 
 static inline int weight(struct commit_list *elem)
 {
-       return *((int*)(elem->item->util));
+       return **commit_weight_at(&commit_weight, elem->item);
 }
 
 static inline void weight_set(struct commit_list *elem, int weight)
 {
-       *((int*)(elem->item->util)) = weight;
+       **commit_weight_at(&commit_weight, elem->item) = weight;
 }
 
 static int count_interesting_parents(struct commit *commit)
@@ -265,7 +269,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list,
                struct commit *commit = p->item;
                unsigned flags = commit->object.flags;
 
-               p->item->util = &weights[n++];
+               *commit_weight_at(&commit_weight, p->item) = &weights[n++];
                switch (count_interesting_parents(commit)) {
                case 0:
                        if (!(flags & TREESAME)) {
@@ -372,6 +376,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
        int *weights;
 
        show_list("bisection 2 entry", 0, 0, *commit_list);
+       init_commit_weight(&commit_weight);
 
        /*
         * Count the number of total and tree-changing items on the
@@ -412,6 +417,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
        }
        free(weights);
        *commit_list = best;
+       clear_commit_weight(&commit_weight);
 }
 
 static int register_ref(const char *refname, const struct object_id *oid,
diff --git a/blame.c b/blame.c
index 14d0e0b5751c2abaad16dac81a71436ca29ddde4..0c4490a35bbda9d5c85c9f223f1ae64777ad6391 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -1,11 +1,31 @@
 #include "cache.h"
 #include "refs.h"
+#include "object-store.h"
 #include "cache-tree.h"
 #include "mergesort.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "tag.h"
 #include "blame.h"
+#include "alloc.h"
+#include "commit-slab.h"
+
+define_commit_slab(blame_suspects, struct blame_origin *);
+static struct blame_suspects blame_suspects;
+
+struct blame_origin *get_blame_suspects(struct commit *commit)
+{
+       struct blame_origin **result;
+
+       result = blame_suspects_peek(&blame_suspects, commit);
+
+       return result ? *result : NULL;
+}
+
+static void set_blame_suspects(struct commit *commit, struct blame_origin *origin)
+{
+       *blame_suspects_at(&blame_suspects, commit) = origin;
+}
 
 void blame_origin_decref(struct blame_origin *o)
 {
@@ -15,12 +35,12 @@ void blame_origin_decref(struct blame_origin *o)
                        blame_origin_decref(o->previous);
                free(o->file.ptr);
                /* Should be present exactly once in commit chain */
-               for (p = o->commit->util; p; l = p, p = p->next) {
+               for (p = get_blame_suspects(o->commit); p; l = p, p = p->next) {
                        if (p == o) {
                                if (l)
                                        l->next = p->next;
                                else
-                                       o->commit->util = p->next;
+                                       set_blame_suspects(o->commit, p->next);
                                free(o);
                                return;
                        }
@@ -41,8 +61,8 @@ static struct blame_origin *make_origin(struct commit *commit, const char *path)
        FLEX_ALLOC_STR(o, path, path);
        o->commit = commit;
        o->refcnt = 1;
-       o->next = commit->util;
-       commit->util = o;
+       o->next = get_blame_suspects(commit);
+       set_blame_suspects(commit, o);
        return o;
 }
 
@@ -54,13 +74,13 @@ static struct blame_origin *get_origin(struct commit *commit, const char *path)
 {
        struct blame_origin *o, *l;
 
-       for (o = commit->util, l = NULL; o; l = o, o = o->next) {
+       for (o = get_blame_suspects(commit), l = NULL; o; l = o, o = o->next) {
                if (!strcmp(o->path, path)) {
                        /* bump to front */
                        if (l) {
                                l->next = o->next;
-                               o->next = commit->util;
-                               commit->util = o;
+                               o->next = get_blame_suspects(commit);
+                               set_blame_suspects(commit, o);
                        }
                        return blame_origin_incref(o);
                }
@@ -110,17 +130,19 @@ static void append_merge_parents(struct commit_list **tail)
        int merge_head;
        struct strbuf line = STRBUF_INIT;
 
-       merge_head = open(git_path_merge_head(), O_RDONLY);
+       merge_head = open(git_path_merge_head(the_repository), O_RDONLY);
        if (merge_head < 0) {
                if (errno == ENOENT)
                        return;
-               die("cannot open '%s' for reading", git_path_merge_head());
+               die("cannot open '%s' for reading",
+                   git_path_merge_head(the_repository));
        }
 
        while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
                struct object_id oid;
                if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
-                       die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
+                       die("unknown line in '%s': %s",
+                           git_path_merge_head(the_repository), line.buf);
                tail = append_parent(tail, &oid);
        }
        close(merge_head);
@@ -161,7 +183,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
 
        read_cache();
        time(&now);
-       commit = alloc_commit_node();
+       commit = alloc_commit_node(the_repository);
        commit->object.parsed = 1;
        commit->date = now;
        parent_tail = &commit->parents;
@@ -478,7 +500,7 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig
                porigin->suspects = blame_merge(porigin->suspects, sorted);
        else {
                struct blame_origin *o;
-               for (o = porigin->commit->util; o; o = o->next) {
+               for (o = get_blame_suspects(porigin->commit); o; o = o->next) {
                        if (o->suspects) {
                                porigin->suspects = sorted;
                                return;
@@ -525,7 +547,7 @@ static struct blame_origin *find_origin(struct commit *parent,
        const char *paths[2];
 
        /* First check any existing origins */
-       for (porigin = parent->util; porigin; porigin = porigin->next)
+       for (porigin = get_blame_suspects(parent); porigin; porigin = porigin->next)
                if (!strcmp(porigin->path, origin->path)) {
                        /*
                         * The same path between origin and its parent
@@ -1550,7 +1572,7 @@ void assign_blame(struct blame_scoreboard *sb, int opt)
 
        while (commit) {
                struct blame_entry *ent;
-               struct blame_origin *suspect = commit->util;
+               struct blame_origin *suspect = get_blame_suspects(commit);
 
                /* find one suspect to break down */
                while (suspect && !suspect->suspects)
@@ -1752,6 +1774,8 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam
        struct commit *final_commit = NULL;
        enum object_type type;
 
+       init_blame_suspects(&blame_suspects);
+
        if (sb->reverse && sb->contents_from)
                die(_("--contents and --reverse do not blend well."));
 
@@ -1815,7 +1839,7 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam
        }
 
        if (is_null_oid(&sb->final->object.oid)) {
-               o = sb->final->util;
+               o = get_blame_suspects(sb->final);
                sb->final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb->final_buf_size = o->file.size;
        }
diff --git a/blame.h b/blame.h
index a6c915c277d99cfa39dd3fe6aee82c2f0f8320df..2092f9529c108d40100ec2a8175098edc8e8e5dd 100644 (file)
--- a/blame.h
+++ b/blame.h
@@ -172,4 +172,6 @@ extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, stru
 
 extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o);
 
+extern struct blame_origin *get_blame_suspects(struct commit *commit);
+
 #endif /* BLAME_H */
diff --git a/blob.c b/blob.c
index fa2ab4f7a74e501366e1d3ceca9285ae60606165..458dafa811edf74e01e10777cf580c696bf0b1a8 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "blob.h"
+#include "repository.h"
+#include "alloc.h"
 
 const char *blob_type = "blob";
 
@@ -7,7 +9,8 @@ struct blob *lookup_blob(const struct object_id *oid)
 {
        struct object *obj = lookup_object(oid->hash);
        if (!obj)
-               return create_object(oid->hash, alloc_blob_node());
+               return create_object(the_repository, oid->hash,
+                                    alloc_blob_node(the_repository));
        return object_as_type(obj, OBJ_BLOB, 0);
 }
 
index f967c98f6324c49d9ab7f94f973230512af13707..6a35dd31f2a9c9171a866c66f65cbb30e2e511a8 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -340,13 +340,13 @@ void create_branch(const char *name, const char *start_name,
 
 void remove_branch_state(void)
 {
-       unlink(git_path_cherry_pick_head());
-       unlink(git_path_revert_head());
-       unlink(git_path_merge_head());
-       unlink(git_path_merge_rr());
-       unlink(git_path_merge_msg());
-       unlink(git_path_merge_mode());
-       unlink(git_path_squash_msg());
+       unlink(git_path_cherry_pick_head(the_repository));
+       unlink(git_path_revert_head(the_repository));
+       unlink(git_path_merge_head(the_repository));
+       unlink(git_path_merge_rr(the_repository));
+       unlink(git_path_merge_msg(the_repository));
+       unlink(git_path_merge_mode(the_repository));
+       unlink(git_path_squash_msg(the_repository));
 }
 
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
index 4e0f64723ed8dde9c97827cc688535b2dda73025..0362f1ce25fb64631e28f00eafa46f35bc94c7eb 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -220,6 +220,7 @@ extern int cmd_serve(int argc, const char **argv, const char *prefix);
 extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
index 2fc2d1e82c5ead598476964329b555e38de9a495..6273ea5195bb7f7f2296155753c5e7982d533d66 100644 (file)
@@ -1827,15 +1827,11 @@ static void am_run(struct am_state *state, int resume)
                }
 
                if (apply_status) {
-                       int advice_amworkdir = 1;
-
                        printf_ln(_("Patch failed at %s %.*s"), msgnum(state),
                                linelen(state->msg), state->msg);
 
-                       git_config_get_bool("advice.amworkdir", &advice_amworkdir);
-
                        if (advice_amworkdir)
-                               printf_ln(_("Use 'git am --show-current-patch' to see the failed patch"));
+                               advise(_("Use 'git am --show-current-patch' to see the failed patch"));
 
                        die_user_resolve(state);
                }
index 3295718841aab1051ee8bdde0b1fc0aa26ae5f00..921d127f29257facb5942e7b03304a69bafff175 100644 (file)
@@ -9,6 +9,7 @@
 #include "config.h"
 #include "color.h"
 #include "builtin.h"
+#include "repository.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
@@ -23,6 +24,7 @@
 #include "line-log.h"
 #include "dir.h"
 #include "progress.h"
+#include "object-store.h"
 #include "blame.h"
 #include "string-list.h"
 
@@ -543,7 +545,7 @@ static void output(struct blame_scoreboard *sb, int option)
                        struct commit *commit = ent->suspect->commit;
                        if (commit->object.flags & MORE_THAN_ONE_PATH)
                                continue;
-                       for (suspect = commit->util; suspect; suspect = suspect->next) {
+                       for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) {
                                if (suspect->guilty && count++) {
                                        commit->object.flags |= MORE_THAN_ONE_PATH;
                                        break;
@@ -576,7 +578,7 @@ static int read_ancestry(const char *graft_file)
                /* The format is just "Commit Parent1 Parent2 ...\n" */
                struct commit_graft *graft = read_graft_line(&buf);
                if (graft)
-                       register_commit_graft(graft, 0);
+                       register_commit_graft(the_repository, graft, 0);
        }
        fclose(fp);
        strbuf_release(&buf);
index 5217ba3bdebc2255e95260fdb097166d3617e120..1876ca9e7969019e1db1c97aedcf1064903b80a2 100644 (file)
@@ -22,6 +22,7 @@
 #include "wt-status.h"
 #include "ref-filter.h"
 #include "worktree.h"
+#include "help.h"
 
 static const char * const builtin_branch_usage[] = {
        N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
@@ -55,25 +56,19 @@ enum color_branch {
        BRANCH_COLOR_UPSTREAM = 5
 };
 
+static const char *color_branch_slots[] = {
+       [BRANCH_COLOR_RESET]    = "reset",
+       [BRANCH_COLOR_PLAIN]    = "plain",
+       [BRANCH_COLOR_REMOTE]   = "remote",
+       [BRANCH_COLOR_LOCAL]    = "local",
+       [BRANCH_COLOR_CURRENT]  = "current",
+       [BRANCH_COLOR_UPSTREAM] = "upstream",
+};
+
 static struct string_list output = STRING_LIST_INIT_DUP;
 static unsigned int colopts;
 
-static int parse_branch_color_slot(const char *slot)
-{
-       if (!strcasecmp(slot, "plain"))
-               return BRANCH_COLOR_PLAIN;
-       if (!strcasecmp(slot, "reset"))
-               return BRANCH_COLOR_RESET;
-       if (!strcasecmp(slot, "remote"))
-               return BRANCH_COLOR_REMOTE;
-       if (!strcasecmp(slot, "local"))
-               return BRANCH_COLOR_LOCAL;
-       if (!strcasecmp(slot, "current"))
-               return BRANCH_COLOR_CURRENT;
-       if (!strcasecmp(slot, "upstream"))
-               return BRANCH_COLOR_UPSTREAM;
-       return -1;
-}
+define_list_config_array(color_branch_slots);
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
@@ -86,7 +81,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (skip_prefix(var, "color.branch.", &slot_name)) {
-               int slot = parse_branch_color_slot(slot_name);
+               int slot = LOOKUP_CONFIG(color_branch_slots, slot_name);
                if (slot < 0)
                        return 0;
                if (!value)
index 665b5819499b4d56a4f49996125cac919e477309..4a44b2404fb36bb01758cc68964799a0438d5933 100644 (file)
@@ -13,6 +13,7 @@
 #include "tree-walk.h"
 #include "sha1-array.h"
 #include "packfile.h"
+#include "object-store.h"
 
 struct batch_options {
        int enabled;
index 2e1d2376d24043a75adb9bbc69724591bcc2d5c3..28627650cd66bb38a432397b2e6398b412cbf073 100644 (file)
@@ -4,6 +4,7 @@
 #include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tree.h"
 #include "tree-walk.h"
@@ -1120,10 +1121,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
-               OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
-                           2),
-               OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
-                           3),
+               OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
+                             N_("checkout our version for unmerged files"),
+                             2, PARSE_OPT_NONEG),
+               OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
+                             N_("checkout their version for unmerged files"),
+                             3, PARSE_OPT_NONEG),
                OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
                           PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
index fad533a0a7382f10ecf48a738c955734ad5c0d96..ab402c204cbcaea6190608753311605d37ca83d2 100644 (file)
@@ -16,6 +16,7 @@
 #include "column.h"
 #include "color.h"
 #include "pathspec.h"
+#include "help.h"
 
 static int force = -1; /* unset */
 static int interactive;
@@ -42,6 +43,15 @@ enum color_clean {
        CLEAN_COLOR_ERROR = 5
 };
 
+static const char *color_interactive_slots[] = {
+       [CLEAN_COLOR_ERROR]  = "error",
+       [CLEAN_COLOR_HEADER] = "header",
+       [CLEAN_COLOR_HELP]   = "help",
+       [CLEAN_COLOR_PLAIN]  = "plain",
+       [CLEAN_COLOR_PROMPT] = "prompt",
+       [CLEAN_COLOR_RESET]  = "reset",
+};
+
 static int clean_use_color = -1;
 static char clean_colors[][COLOR_MAXLEN] = {
        [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED,
@@ -82,22 +92,7 @@ struct menu_stuff {
        void *stuff;
 };
 
-static int parse_clean_color_slot(const char *var)
-{
-       if (!strcasecmp(var, "reset"))
-               return CLEAN_COLOR_RESET;
-       if (!strcasecmp(var, "plain"))
-               return CLEAN_COLOR_PLAIN;
-       if (!strcasecmp(var, "prompt"))
-               return CLEAN_COLOR_PROMPT;
-       if (!strcasecmp(var, "header"))
-               return CLEAN_COLOR_HEADER;
-       if (!strcasecmp(var, "help"))
-               return CLEAN_COLOR_HELP;
-       if (!strcasecmp(var, "error"))
-               return CLEAN_COLOR_ERROR;
-       return -1;
-}
+define_list_config_array(color_interactive_slots);
 
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
@@ -113,7 +108,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
                return 0;
        }
        if (skip_prefix(var, "color.interactive.", &slot_name)) {
-               int slot = parse_clean_color_slot(slot_name);
+               int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name);
                if (slot < 0)
                        return 0;
                if (!value)
index 99e73dae8595d37f704d0fe7f7fd84a190edfcf6..1d939af9d8cb93bd233549c44d891f3f9df803cc 100644 (file)
@@ -15,6 +15,7 @@
 #include "fetch-pack.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
@@ -1077,7 +1078,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
-       refspec_item_init(&refspec, value.buf, REFSPEC_FETCH);
+       refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);
 
        strbuf_reset(&value);
 
index ecf42191da10cd2e87360f001d5493e792b9682e..9fbd3529fb17bf46608a3a7617671adcd7036450 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tree.h"
 #include "builtin.h"
index a842fea666a33bb2191d10cf452a255754bb44e6..158e3f843afbf889e26328f29cb2894f17da4ee1 100644 (file)
@@ -32,6 +32,7 @@
 #include "column.h"
 #include "sequencer.h"
 #include "mailmap.h"
+#include "help.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@ -66,6 +67,18 @@ N_("If you wish to skip this commit, use:\n"
 "Then \"git cherry-pick --continue\" will resume cherry-picking\n"
 "the remaining commits.\n");
 
+static const char *color_status_slots[] = {
+       [WT_STATUS_HEADER]        = "header",
+       [WT_STATUS_UPDATED]       = "updated",
+       [WT_STATUS_CHANGED]       = "changed",
+       [WT_STATUS_UNTRACKED]     = "untracked",
+       [WT_STATUS_NOBRANCH]      = "noBranch",
+       [WT_STATUS_UNMERGED]      = "unmerged",
+       [WT_STATUS_LOCAL_BRANCH]  = "localBranch",
+       [WT_STATUS_REMOTE_BRANCH] = "remoteBranch",
+       [WT_STATUS_ONBRANCH]      = "branch",
+};
+
 static const char *use_message_buffer;
 static struct lock_file index_lock; /* real index */
 static struct lock_file false_lock; /* used only for partial commits */
@@ -155,9 +168,9 @@ static int opt_parse_rename_score(const struct option *opt, const char *arg, int
 
 static void determine_whence(struct wt_status *s)
 {
-       if (file_exists(git_path_merge_head()))
+       if (file_exists(git_path_merge_head(the_repository)))
                whence = FROM_MERGE;
-       else if (file_exists(git_path_cherry_pick_head())) {
+       else if (file_exists(git_path_cherry_pick_head(the_repository))) {
                whence = FROM_CHERRY_PICK;
                if (file_exists(git_path_seq_dir()))
                        sequencer_in_use = 1;
@@ -705,21 +718,21 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (have_option_m)
                        strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
-       } else if (!stat(git_path_merge_msg(), &statbuf)) {
+       } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
                /*
                 * prepend SQUASH_MSG here if it exists and a
                 * "merge --squash" was originally performed
                 */
-               if (!stat(git_path_squash_msg(), &statbuf)) {
-                       if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+               if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                                die_errno(_("could not read SQUASH_MSG"));
                        hook_arg1 = "squash";
                } else
                        hook_arg1 = "merge";
-               if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0)
+               if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
                        die_errno(_("could not read MERGE_MSG"));
-       } else if (!stat(git_path_squash_msg(), &statbuf)) {
-               if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0)
+       } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
+               if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
                        die_errno(_("could not read SQUASH_MSG"));
                hook_arg1 = "squash";
        } else if (template_file) {
@@ -800,8 +813,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                        "       %s\n"
                                        "and try again.\n"),
                                whence == FROM_MERGE ?
-                                       git_path_merge_head() :
-                                       git_path_cherry_pick_head());
+                                       git_path_merge_head(the_repository) :
+                                       git_path_cherry_pick_head(the_repository));
                }
 
                fprintf(s->fp, "\n");
@@ -1183,27 +1196,14 @@ static int dry_run_commit(int argc, const char **argv, const char *prefix,
        return commitable ? 0 : 1;
 }
 
+define_list_config_array_extra(color_status_slots, {"added"});
+
 static int parse_status_slot(const char *slot)
 {
-       if (!strcasecmp(slot, "header"))
-               return WT_STATUS_HEADER;
-       if (!strcasecmp(slot, "branch"))
-               return WT_STATUS_ONBRANCH;
-       if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
+       if (!strcasecmp(slot, "added"))
                return WT_STATUS_UPDATED;
-       if (!strcasecmp(slot, "changed"))
-               return WT_STATUS_CHANGED;
-       if (!strcasecmp(slot, "untracked"))
-               return WT_STATUS_UNTRACKED;
-       if (!strcasecmp(slot, "nobranch"))
-               return WT_STATUS_NOBRANCH;
-       if (!strcasecmp(slot, "unmerged"))
-               return WT_STATUS_UNMERGED;
-       if (!strcasecmp(slot, "localBranch"))
-               return WT_STATUS_LOCAL_BRANCH;
-       if (!strcasecmp(slot, "remoteBranch"))
-               return WT_STATUS_REMOTE_BRANCH;
-       return -1;
+
+       return LOOKUP_CONFIG(color_status_slots, slot);
 }
 
 static int git_status_config(const char *k, const char *v, void *cb)
@@ -1564,7 +1564,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
                pptr = commit_list_append(current_head, pptr);
-               fp = xfopen(git_path_merge_head(), "r");
+               fp = xfopen(git_path_merge_head(the_repository), "r");
                while (strbuf_getline_lf(&m, fp) != EOF) {
                        struct commit *parent;
 
@@ -1575,8 +1575,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                }
                fclose(fp);
                strbuf_release(&m);
-               if (!stat(git_path_merge_mode(), &statbuf)) {
-                       if (strbuf_read_file(&sb, git_path_merge_mode(), 0) < 0)
+               if (!stat(git_path_merge_mode(the_repository), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path_merge_mode(the_repository), 0) < 0)
                                die_errno(_("could not read MERGE_MODE"));
                        if (!strcmp(sb.buf, "no-ff"))
                                allow_fast_forward = 0;
@@ -1639,12 +1639,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                die("%s", err.buf);
        }
 
-       unlink(git_path_cherry_pick_head());
-       unlink(git_path_revert_head());
-       unlink(git_path_merge_head());
-       unlink(git_path_merge_msg());
-       unlink(git_path_merge_mode());
-       unlink(git_path_squash_msg());
+       unlink(git_path_cherry_pick_head(the_repository));
+       unlink(git_path_revert_head(the_repository));
+       unlink(git_path_merge_head(the_repository));
+       unlink(git_path_merge_msg(the_repository));
+       unlink(git_path_merge_mode(the_repository));
+       unlink(git_path_squash_msg(the_repository));
 
        if (commit_index_files())
                die (_("Repository has been updated, but unable to write\n"
index cf1ae77d7c705cdfb34bdb5403bad8db4148eccf..1e87f68d5eeeaa81b5d67a93b80b5c2eb1b0b797 100644 (file)
 #include "hashmap.h"
 #include "argv-array.h"
 #include "run-command.h"
+#include "object-store.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "commit-slab.h"
 
 #define MAX_TAGS       (FLAG_BITS - 1)
 
+define_commit_slab(commit_names, struct commit_name *);
+
 static const char * const describe_usage[] = {
        N_("git describe [<options>] [<commit-ish>...]"),
        N_("git describe [<options>] --dirty"),
@@ -37,6 +41,7 @@ static struct string_list patterns = STRING_LIST_INIT_NODUP;
 static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
 static int always;
 static const char *suffix, *dirty, *broken;
+static struct commit_names commit_names;
 
 /* diff-index command arguments to check if working tree is dirty. */
 static const char *diff_index_args[] = {
@@ -321,11 +326,14 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
        if (!have_util) {
                struct hashmap_iter iter;
                struct commit *c;
-               struct commit_name *n = hashmap_iter_first(&names, &iter);
+               struct commit_name *n;
+
+               init_commit_names(&commit_names);
+               n = hashmap_iter_first(&names, &iter);
                for (; n; n = hashmap_iter_next(&iter)) {
                        c = lookup_commit_reference_gently(&n->peeled, 1);
                        if (c)
-                               c->util = n;
+                               *commit_names_at(&commit_names, c) = n;
                }
                have_util = 1;
        }
@@ -336,8 +344,11 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
        while (list) {
                struct commit *c = pop_commit(&list);
                struct commit_list *parents = c->parents;
+               struct commit_name **slot;
+
                seen_commits++;
-               n = c->util;
+               slot = commit_names_peek(&commit_names, c);
+               n = slot ? *slot : NULL;
                if (n) {
                        if (!tags && !all && n->prio < 2) {
                                unannotated_cnt++;
index bfefff3a84896a79fbed42eec1121286edcc86dd..b709b6e9842c68597b5bb7149db4054ef980fc53 100644 (file)
@@ -352,6 +352,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        rev.diffopt.flags.allow_external = 1;
        rev.diffopt.flags.allow_textconv = 1;
 
+       /*
+        * Default to intent-to-add entries invisible in the
+        * index. This makes them show up as new files in diff-files
+        * and not at all in diff-cached.
+        */
+       rev.diffopt.ita_invisible_in_index = 1;
+
        if (nongit)
                die(_("Not a git repository"));
        argc = setup_revisions(argc, argv, &rev, NULL);
index bc97d4aef2db2d76e3ffddfc4759e52688f3974e..51f6c9cdb460f5d35dc1abc199604ac5fa912dda 100644 (file)
@@ -20,6 +20,7 @@
 #include "argv-array.h"
 #include "strbuf.h"
 #include "lockfile.h"
+#include "object-store.h"
 #include "dir.h"
 
 static char *diff_gui_tool;
index 6c9768742fd4faecb3719e9c87997df3d490502a..9ee6a4d2e8f7e728c6c8deca277f6b1f2fcd9530 100644 (file)
@@ -8,6 +8,7 @@
 #include "config.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
@@ -22,6 +23,7 @@
 #include "quote.h"
 #include "remote.h"
 #include "blob.h"
+#include "commit-slab.h"
 
 static const char *fast_export_usage[] = {
        N_("git fast-export [rev-list-opts]"),
@@ -38,6 +40,7 @@ static int full_tree;
 static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
 static struct refspec refspecs = REFSPEC_INIT_FETCH;
 static int anonymize;
+static struct revision_sources revision_sources;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -589,7 +592,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
                if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
                        export_blob(&diff_queued_diff.queue[i]->two->oid);
 
-       refname = commit->util;
+       refname = *revision_sources_at(&revision_sources, commit);
        if (anonymize) {
                refname = anonymize_refname(refname);
                anonymize_ident_line(&committer, &committer_end);
@@ -861,10 +864,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                 * This ref will not be updated through a commit, lets make
                 * sure it gets properly updated eventually.
                 */
-               if (commit->util || commit->object.flags & SHOWN)
+               if (*revision_sources_at(&revision_sources, commit) ||
+                   commit->object.flags & SHOWN)
                        string_list_append(&extra_refs, full_name)->util = commit;
-               if (!commit->util)
-                       commit->util = full_name;
+               if (!*revision_sources_at(&revision_sources, commit))
+                       *revision_sources_at(&revision_sources, commit) = full_name;
        }
 }
 
@@ -1028,8 +1032,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
 
        init_revisions(&revs, prefix);
+       init_revision_sources(&revision_sources);
        revs.topo_order = 1;
-       revs.show_source = 1;
+       revs.sources = &revision_sources;
        revs.rewrite_parents = 1;
        argc = parse_options(argc, argv, prefix, options, fast_export_usage,
                        PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
index ea5b9669ad1f40da56a8565cada56e5991d24206..83f36d7cdebdede92274e661d3c97d6b999a8518 100644 (file)
@@ -6,6 +6,7 @@
 #include "repository.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "commit.h"
 #include "builtin.h"
 #include "string-list.h"
@@ -777,7 +778,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        const char *what, *kind;
        struct ref *rm;
        char *url;
-       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
+       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
        int want_status;
        int summary_width = transport_summary_width(ref_map);
 
@@ -1029,7 +1030,7 @@ static void check_not_current_branch(struct ref *ref_map)
 
 static int truncate_fetch_head(void)
 {
-       const char *filename = git_path_fetch_head();
+       const char *filename = git_path_fetch_head(the_repository);
        FILE *fp = fopen_for_writing(filename);
 
        if (!fp)
@@ -1449,7 +1450,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (unshallow) {
                if (depth)
                        die(_("--depth and --unshallow cannot be used together"));
-               else if (!is_repository_shallow())
+               else if (!is_repository_shallow(the_repository))
                        die(_("--unshallow on a complete repository does not make sense"));
                else
                        depth = xstrfmt("%d", INFINITE_DEPTH);
index bd680be6874da29cf776c84468a60888beea53fb..1b526adb3a95bbef0eabf8d154f0f6f9da1ef214 100644 (file)
@@ -2,6 +2,7 @@
 #include "cache.h"
 #include "config.h"
 #include "refs.h"
+#include "object-store.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
index a9a3a198c3b47197cb847ba32a33177f1a45f428..9ada4f4dfd8747d3888203749f057915f7c9ab46 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include "builtin.h"
 #include "config.h"
+#include "object-store.h"
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
index 58e0a5507f10365b43eaa2698da2c8bba18b12ed..8d4f6dd30152e70d1379a956793b1086fba7f5b6 100644 (file)
@@ -37,6 +37,7 @@ static const char *html_path;
 
 static int show_all = 0;
 static int show_guides = 0;
+static int show_config;
 static int verbose;
 static unsigned int colopts;
 static enum help_format help_format = HELP_FORMAT_NONE;
@@ -45,6 +46,8 @@ static struct option builtin_help_options[] = {
        OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
        OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
        OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
+       OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
+       OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
        OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
        OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
                        HELP_FORMAT_WEB),
@@ -444,6 +447,19 @@ int cmd_help(int argc, const char **argv, const char *prefix)
                list_commands(colopts, &main_cmds, &other_cmds);
        }
 
+       if (show_config) {
+               int for_human = show_config == 1;
+
+               if (!for_human) {
+                       list_config_help(for_human);
+                       return 0;
+               }
+               setup_pager();
+               list_config_help(for_human);
+               printf("\n%s\n", _("'git help config' for more information"));
+               return 0;
+       }
+
        if (show_guides)
                list_common_guides_help();
 
index 4686f68594829a85e9d73bcf0b6298c1e61b6852..805f89d7e1552e86a8c2b990a0e213d09f91d42a 100644 (file)
@@ -7,6 +7,7 @@
 #include "cache.h"
 #include "config.h"
 #include "refs.h"
+#include "object-store.h"
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
@@ -28,6 +29,7 @@
 #include "mailmap.h"
 #include "gpg-interface.h"
 #include "progress.h"
+#include "commit-slab.h"
 
 #define MAIL_DEFAULT_WRAP 72
 
@@ -148,6 +150,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
        struct decoration_filter decoration_filter = {&decorate_refs_include,
                                                      &decorate_refs_exclude};
+       static struct revision_sources revision_sources;
 
        const struct option builtin_log_options[] = {
                OPT__QUIET(&quiet, N_("suppress diff output")),
@@ -194,8 +197,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
            rev->diffopt.filter || rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
 
-       if (source)
-               rev->show_source = 1;
+       if (source) {
+               init_revision_sources(&revision_sources);
+               rev->sources = &revision_sources;
+       }
 
        if (mailmap) {
                rev->mailmap = xcalloc(1, sizeof(struct string_list));
@@ -1337,6 +1342,8 @@ static struct commit *get_base_commit(const char *base_commit,
        return base;
 }
 
+define_commit_slab(commit_base, int);
+
 static void prepare_bases(struct base_tree_info *bases,
                          struct commit *base,
                          struct commit **list,
@@ -1345,11 +1352,13 @@ static void prepare_bases(struct base_tree_info *bases,
        struct commit *commit;
        struct rev_info revs;
        struct diff_options diffopt;
+       struct commit_base commit_base;
        int i;
 
        if (!base)
                return;
 
+       init_commit_base(&commit_base);
        diff_setup(&diffopt);
        diffopt.flags.recursive = 1;
        diff_setup_done(&diffopt);
@@ -1362,7 +1371,7 @@ static void prepare_bases(struct base_tree_info *bases,
        for (i = 0; i < total; i++) {
                list[i]->object.flags &= ~UNINTERESTING;
                add_pending_object(&revs, &list[i]->object, "rev_list");
-               list[i]->util = (void *)1;
+               *commit_base_at(&commit_base, list[i]) = 1;
        }
        base->object.flags |= UNINTERESTING;
        add_pending_object(&revs, &base->object, "base");
@@ -1376,7 +1385,7 @@ static void prepare_bases(struct base_tree_info *bases,
        while ((commit = get_revision(&revs)) != NULL) {
                struct object_id oid;
                struct object_id *patch_id;
-               if (commit->util)
+               if (*commit_base_at(&commit_base, commit))
                        continue;
                if (commit_patch_id(commit, &diffopt, &oid, 0))
                        die(_("cannot get patch id"));
@@ -1385,6 +1394,7 @@ static void prepare_bases(struct base_tree_info *bases,
                oidcpy(patch_id, &oid);
                bases->nr_patch_id++;
        }
+       clear_commit_base(&commit_base);
 }
 
 static void print_bases(struct base_tree_info *bases, FILE *file)
@@ -1746,6 +1756,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (base_commit || base_auto) {
                struct commit *base = get_base_commit(base_commit, list, nr);
                reset_revision_walk();
+               clear_object_flags(UNINTERESTING);
                prepare_bases(&bases, base, list, nr);
        }
 
index 409da4e8351f6d8719671d3176f0ea34fe9b756c..fe3b952cb3007d12c2e94a08b10486ed301c4769 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "blob.h"
 #include "tree.h"
 #include "commit.h"
index bf01e05808313da20ed73c37d0d150c4ddb45587..8a8d579752050487269bcc63aac4713c0b8ebc1e 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "tree-walk.h"
 #include "xdiff-interface.h"
+#include "object-store.h"
 #include "blob.h"
 #include "exec-cmd.h"
 #include "merge-blobs.h"
index b00d6f4821e7dc4067997bf515702edcfa413501..d1b547d97376d74e02e8f3dae3a1afb64dd2b780 100644 (file)
@@ -247,9 +247,9 @@ static struct option builtin_merge_options[] = {
 /* Cleans up metadata that is uninteresting after a succeeded merge. */
 static void drop_save(void)
 {
-       unlink(git_path_merge_head());
-       unlink(git_path_merge_msg());
-       unlink(git_path_merge_mode());
+       unlink(git_path_merge_head(the_repository));
+       unlink(git_path_merge_msg(the_repository));
+       unlink(git_path_merge_mode(the_repository));
 }
 
 static int save_state(struct object_id *stash)
@@ -382,7 +382,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
                        oid_to_hex(&commit->object.oid));
                pretty_print_commit(&ctx, commit, &out);
        }
-       write_file_buf(git_path_squash_msg(), out.buf, out.len);
+       write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
        strbuf_release(&out);
 }
 
@@ -445,6 +445,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        struct object_id branch_head;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
+       struct merge_remote_desc *desc;
        const char *ptr;
        char *found_ref;
        int len, early;
@@ -517,16 +518,13 @@ static void merge_name(const char *remote, struct strbuf *msg)
                strbuf_release(&truname);
        }
 
-       if (remote_head->util) {
-               struct merge_remote_desc *desc;
-               desc = merge_remote_util(remote_head);
-               if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
-                       strbuf_addf(msg, "%s\t\t%s '%s'\n",
-                                   oid_to_hex(&desc->obj->oid),
-                                   type_name(desc->obj->type),
-                                   remote);
-                       goto cleanup;
-               }
+       desc = merge_remote_util(remote_head);
+       if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+               strbuf_addf(msg, "%s\t\t%s '%s'\n",
+                           oid_to_hex(&desc->obj->oid),
+                           type_name(desc->obj->type),
+                           remote);
+               goto cleanup;
        }
 
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
@@ -743,7 +741,7 @@ static void add_strategies(const char *string, unsigned attr)
 
 static void read_merge_msg(struct strbuf *msg)
 {
-       const char *filename = git_path_merge_msg();
+       const char *filename = git_path_merge_msg(the_repository);
        strbuf_reset(msg);
        if (strbuf_read_file(msg, filename, 0) < 0)
                die_errno(_("Could not read from '%s'"), filename);
@@ -780,18 +778,18 @@ static void prepare_to_commit(struct commit_list *remoteheads)
        if (signoff)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
-       write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
+       write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
-                           git_path_merge_msg(), "merge", NULL))
+                           git_path_merge_msg(the_repository), "merge", NULL))
                abort_commit(remoteheads, NULL);
        if (0 < option_edit) {
-               if (launch_editor(git_path_merge_msg(), NULL, NULL))
+               if (launch_editor(git_path_merge_msg(the_repository), NULL, NULL))
                        abort_commit(remoteheads, NULL);
        }
 
        if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
                                          "commit-msg",
-                                         git_path_merge_msg(), NULL))
+                                         git_path_merge_msg(the_repository), NULL))
                abort_commit(remoteheads, NULL);
 
        read_merge_msg(&msg);
@@ -861,7 +859,7 @@ static int suggest_conflicts(void)
        FILE *fp;
        struct strbuf msgbuf = STRBUF_INIT;
 
-       filename = git_path_merge_msg();
+       filename = git_path_merge_msg(the_repository);
        fp = xfopen(filename, "a");
 
        append_conflicts_hint(&msgbuf);
@@ -934,19 +932,22 @@ static void write_merge_heads(struct commit_list *remoteheads)
        for (j = remoteheads; j; j = j->next) {
                struct object_id *oid;
                struct commit *c = j->item;
-               if (c->util && merge_remote_util(c)->obj) {
-                       oid = &merge_remote_util(c)->obj->oid;
+               struct merge_remote_desc *desc;
+
+               desc = merge_remote_util(c);
+               if (desc && desc->obj) {
+                       oid = &desc->obj->oid;
                } else {
                        oid = &c->object.oid;
                }
                strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
-       write_file_buf(git_path_merge_head(), buf.buf, buf.len);
+       write_file_buf(git_path_merge_head(the_repository), buf.buf, buf.len);
 
        strbuf_reset(&buf);
        if (fast_forward == FF_NO)
                strbuf_addstr(&buf, "no-ff");
-       write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
+       write_file_buf(git_path_merge_mode(the_repository), buf.buf, buf.len);
        strbuf_release(&buf);
 }
 
@@ -954,7 +955,8 @@ static void write_merge_state(struct commit_list *remoteheads)
 {
        write_merge_heads(remoteheads);
        strbuf_addch(&merge_msg, '\n');
-       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
+       write_file_buf(git_path_merge_msg(the_repository), merge_msg.buf,
+                      merge_msg.len);
 }
 
 static int default_edit_option(void)
@@ -1037,7 +1039,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
        if (!merge_names)
                merge_names = &fetch_head_file;
 
-       filename = git_path_fetch_head();
+       filename = git_path_fetch_head(the_repository);
        fd = open(filename, O_RDONLY);
        if (fd < 0)
                die_errno(_("could not open '%s' for reading"), filename);
@@ -1185,14 +1187,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
        if (branch)
                skip_prefix(branch, "refs/heads/", &branch);
+
+       init_diff_ui_defaults();
+       git_config(git_merge_config, NULL);
+
        if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
                head_commit = lookup_commit_or_die(&head_oid, "HEAD");
 
-       init_diff_ui_defaults();
-       git_config(git_merge_config, NULL);
-
        if (branch_mergeoptions)
                parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
@@ -1211,7 +1214,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        usage_msg_opt(_("--abort expects no arguments"),
                              builtin_merge_usage, builtin_merge_options);
 
-               if (!file_exists(git_path_merge_head()))
+               if (!file_exists(git_path_merge_head(the_repository)))
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
                /* Invoke 'git reset --merge' */
@@ -1227,7 +1230,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        usage_msg_opt(_("--continue expects no arguments"),
                              builtin_merge_usage, builtin_merge_options);
 
-               if (!file_exists(git_path_merge_head()))
+               if (!file_exists(git_path_merge_head(the_repository)))
                        die(_("There is no merge in progress (MERGE_HEAD missing)."));
 
                /* Invoke 'git commit' */
@@ -1238,7 +1241,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (read_cache_unmerged())
                die_resolve_conflict("merge");
 
-       if (file_exists(git_path_merge_head())) {
+       if (file_exists(git_path_merge_head(the_repository))) {
                /*
                 * There is no unmerged entry, don't advise 'git
                 * add/rm <file>', just 'git commit'.
@@ -1249,7 +1252,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                else
                        die(_("You have not concluded your merge (MERGE_HEAD exists)."));
        }
-       if (file_exists(git_path_cherry_pick_head())) {
+       if (file_exists(git_path_cherry_pick_head(the_repository))) {
                if (advice_resolve_conflict)
                        die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
                            "Please, commit your changes before you merge."));
index 82a6e860775f872a9145e32ee9d8f315b70ad2ea..6fb7dc8578d68526c64179d7fc43aaa5a72445f6 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "tag.h"
 #include "replace-object.h"
+#include "object-store.h"
 
 /*
  * A signature file has a very simple fixed format: four lines
index bb76b469fd1f57319f3d202c1e2658d77962d698..2dc4ad6ba8f227a6a8a46bac62048959220411ec 100644 (file)
@@ -7,6 +7,7 @@
 #include "quote.h"
 #include "tree.h"
 #include "parse-options.h"
+#include "object-store.h"
 
 static struct treeent {
        unsigned mode;
index 387ddf85d21a443f060dbb212fe95a983d9e4f58..0eb440359dd40d51f2802979218bd20a2d3d732e 100644 (file)
@@ -6,6 +6,7 @@
 #include "refs.h"
 #include "parse-options.h"
 #include "sha1-lookup.h"
+#include "commit-slab.h"
 
 #define CUTOFF_DATE_SLOP 86400 /* one day */
 
@@ -17,11 +18,26 @@ typedef struct rev_name {
        int from_tag;
 } rev_name;
 
+define_commit_slab(commit_rev_name, struct rev_name *);
+
 static timestamp_t cutoff = TIME_MAX;
+static struct commit_rev_name rev_names;
 
 /* How many generations are maximally preferred over _one_ merge traversal? */
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
+static struct rev_name *get_commit_rev_name(struct commit *commit)
+{
+       struct rev_name **slot = commit_rev_name_peek(&rev_names, commit);
+
+       return slot ? *slot : NULL;
+}
+
+static void set_commit_rev_name(struct commit *commit, struct rev_name *name)
+{
+       *commit_rev_name_at(&rev_names, commit) = name;
+}
+
 static int is_better_name(struct rev_name *name,
                          const char *tip_name,
                          timestamp_t taggerdate,
@@ -65,7 +81,7 @@ static void name_rev(struct commit *commit,
                int generation, int distance, int from_tag,
                int deref)
 {
-       struct rev_name *name = (struct rev_name *)commit->util;
+       struct rev_name *name = get_commit_rev_name(commit);
        struct commit_list *parents;
        int parent_number = 1;
        char *to_free = NULL;
@@ -84,7 +100,7 @@ static void name_rev(struct commit *commit,
 
        if (name == NULL) {
                name = xmalloc(sizeof(rev_name));
-               commit->util = name;
+               set_commit_rev_name(commit, name);
                goto copy_data;
        } else if (is_better_name(name, tip_name, taggerdate,
                                  generation, distance, from_tag)) {
@@ -296,7 +312,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf)
        if (o->type != OBJ_COMMIT)
                return get_exact_ref_match(o);
        c = (struct commit *) o;
-       n = c->util;
+       n = get_commit_rev_name(c);
        if (!n)
                return NULL;
 
@@ -413,6 +429,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
+       init_commit_rev_name(&rev_names);
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
        if (all + transform_stdin + !!argc > 1) {
index 6981e2d906f43b06cac5bd11cb0d627571be09d5..a0a184004097a0892b3eaafae6ad31b331d6efd3 100644 (file)
@@ -11,6 +11,7 @@
 #include "config.h"
 #include "builtin.h"
 #include "notes.h"
+#include "object-store.h"
 #include "blob.h"
 #include "pretty.h"
 #include "refs.h"
index 71056d8294d7f4bcfdcbc9bb292a5d17faf49252..ebc8cefb53f4a1fde99eff8d6f19c8df0cec0ede 100644 (file)
@@ -2929,11 +2929,13 @@ static int pack_options_allow_reuse(void)
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
 {
-       if (prepare_bitmap_walk(revs) < 0)
+       struct bitmap_index *bitmap_git;
+       if (!(bitmap_git = prepare_bitmap_walk(revs)))
                return -1;
 
        if (pack_options_allow_reuse() &&
            !reuse_partial_packfile_from_bitmap(
+                       bitmap_git,
                        &reuse_packfile,
                        &reuse_packfile_objects,
                        &reuse_packfile_offset)) {
@@ -2942,7 +2944,8 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
                display_progress(progress_state, nr_result);
        }
 
-       traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
+       traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
+       free_bitmap_index(bitmap_git);
        return 0;
 }
 
@@ -2969,7 +2972,7 @@ static void get_object_list(int ac, const char **av)
        setup_revisions(ac, av, &revs, NULL);
 
        /* make sure shallows are read */
-       is_repository_shallow();
+       is_repository_shallow(the_repository);
 
        while (fgets(line, sizeof(line), stdin) != NULL) {
                int len = strlen(line);
@@ -2987,7 +2990,7 @@ static void get_object_list(int ac, const char **av)
                                struct object_id oid;
                                if (get_oid_hex(line + 10, &oid))
                                        die("not an SHA-1 '%s'", line + 10);
-                               register_shallow(&oid);
+                               register_shallow(the_repository, &oid);
                                use_bitmap_index = 0;
                                continue;
                        }
@@ -3299,7 +3302,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                use_bitmap_index = use_bitmap_index_default;
 
        /* "hard" reasons not to use bitmaps; these just won't work at all */
-       if (!use_internal_rev_list || (!pack_to_stdout && write_bitmap_index) || is_repository_shallow())
+       if (!use_internal_rev_list || (!pack_to_stdout && write_bitmap_index) || is_repository_shallow(the_repository))
                use_bitmap_index = 0;
 
        if (pack_to_stdout || !rev_list_all)
index 518ffbea1397faa35102dc088a3830e40c9b13a6..70ec35aa0582386d233b4d49d4bbdbb9cef985cb 100644 (file)
@@ -6,6 +6,7 @@
 #include "reachable.h"
 #include "parse-options.h"
 #include "progress.h"
+#include "object-store.h"
 
 static const char * const prune_usage[] = {
        N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
@@ -159,7 +160,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        remove_temporary_files(s);
        free(s);
 
-       if (is_repository_shallow())
+       if (is_repository_shallow(the_repository))
                prune_shallow(show_only);
 
        return 0;
index 49cc3beb4c428a4bd0513d922b59465ab0c5c414..7197b22b165576ed599f1d01528c5ca2527032b1 100644 (file)
@@ -356,7 +356,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
  */
 static void get_merge_heads(struct oid_array *merge_heads)
 {
-       const char *filename = git_path_fetch_head();
+       const char *filename = git_path_fetch_head(the_repository);
        FILE *fp;
        struct strbuf sb = STRBUF_INIT;
        struct object_id oid;
@@ -684,7 +684,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
        const char *spec_src;
        const char *merge_branch;
 
-       refspec_item_init(&spec, refspec, REFSPEC_FETCH);
+       refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);
        spec_src = spec.src;
        if (!*spec_src || !strcmp(spec_src, "HEAD"))
                spec_src = "HEAD";
@@ -864,7 +864,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (read_cache_unmerged())
                die_resolve_conflict("pull");
 
-       if (file_exists(git_path_merge_head()))
+       if (file_exists(git_path_merge_head(the_repository)))
                die_conclude_merge();
 
        if (get_oid("HEAD", &orig_head))
index 68d36e0a56c3c72ad76c07af293d20e8254377d1..44c7c9ee827e9b0e05e60b207f216d4bda0bddc4 100644 (file)
@@ -25,6 +25,7 @@
 #include "tmp-objdir.h"
 #include "oidset.h"
 #include "packfile.h"
+#include "object-store.h"
 #include "protocol.h"
 
 static const char * const receive_pack_usage[] = {
@@ -905,7 +906,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
         * not lose these new roots..
         */
        for (i = 0; i < extra.nr; i++)
-               register_shallow(&extra.oid[i]);
+               register_shallow(the_repository, &extra.oid[i]);
 
        si->shallow_ref[cmd->index] = 0;
        oid_array_clear(&extra);
index a48984d37e4f5f9e1f07a840582f82e202bd09f0..009119299529d5c93f02d5f8dc2363f6e29f0f9c 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "config.h"
 #include "lockfile.h"
+#include "object-store.h"
 #include "commit.h"
 #include "refs.h"
 #include "dir.h"
index 1a82d850a22c3bfc3176eeda3f8b061b6feb7dc4..c74ee886900176e900d80625144a8570ea127fb1 100644 (file)
@@ -8,6 +8,7 @@
 #include "run-command.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "argv-array.h"
 
 static const char * const builtin_remote_usage[] = {
index 6da2411e14b9f94f309671e14124ef32d89ef528..deabda210127087188117e394e453373fa48991f 100644 (file)
@@ -487,7 +487,7 @@ static int create_graft(int argc, const char **argv, int force, int gentle)
 
 static int convert_graft_file(int force)
 {
-       const char *graft_file = get_graft_file();
+       const char *graft_file = get_graft_file(the_repository);
        FILE *fp = fopen_or_warn(graft_file, "r");
        struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT;
        struct argv_array args = ARGV_ARRAY_INIT;
index a862c70fab99290b5faf3e700e88194589583329..ffe41c924b4810d79872fb5faf600a47e19508e5 100644 (file)
@@ -39,7 +39,7 @@ static const char *reset_type_names[] = {
 
 static inline int is_merge(void)
 {
-       return !access(git_path_merge_head(), F_OK);
+       return !access(git_path_merge_head(the_repository), F_OK);
 }
 
 static int reset_index(const struct object_id *oid, int reset_type, int quiet)
index fadd3ec14cbf0469c332a85278e5d1b4932ef788..6fcb0ff6d56154b9849108028f529c09d0c3450e 100644 (file)
@@ -6,6 +6,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "object-store.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "builtin.h"
@@ -16,6 +17,7 @@
 #include "reflog-walk.h"
 #include "oidset.h"
 #include "packfile.h"
+#include "object-store.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -514,17 +516,21 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                if (revs.count && !revs.left_right && !revs.cherry_mark) {
                        uint32_t commit_count;
                        int max_count = revs.max_count;
-                       if (!prepare_bitmap_walk(&revs)) {
-                               count_bitmap_commit_list(&commit_count, NULL, NULL, NULL);
+                       struct bitmap_index *bitmap_git;
+                       if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+                               count_bitmap_commit_list(bitmap_git, &commit_count, NULL, NULL, NULL);
                                if (max_count >= 0 && max_count < commit_count)
                                        commit_count = max_count;
                                printf("%d\n", commit_count);
+                               free_bitmap_index(bitmap_git);
                                return 0;
                        }
                } else if (revs.max_count < 0 &&
                           revs.tag_objects && revs.tree_objects && revs.blob_objects) {
-                       if (!prepare_bitmap_walk(&revs)) {
-                               traverse_bitmap_commit_list(&show_object_fast);
+                       struct bitmap_index *bitmap_git;
+                       if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+                               traverse_bitmap_commit_list(bitmap_git, &show_object_fast);
+                               free_bitmap_index(bitmap_git);
                                return 0;
                        }
                }
index 4f49e96bfd0166aeb6f9875a688ff0dd83d631b5..2a6cb298bdab7961a73df16c4984ff9eefef7795 100644 (file)
@@ -883,7 +883,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--is-shallow-repository")) {
-                               printf("%s\n", is_repository_shallow() ? "true"
+                               printf("%s\n",
+                                               is_repository_shallow(the_repository) ? "true"
                                                : "false");
                                continue;
                        }
index 4923b1058c667bd45282c0930e1089594a3f4731..42fd8d1a3528c3ea0b30007407b2ae38f240f4c7 100644 (file)
@@ -121,7 +121,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
                        }
                }
        }
-       return 0;
+       return git_default_config(k, v, cb);
 }
 
 int cmd_send_pack(int argc, const char **argv, const char *prefix)
index 6c2148b71db593af1a4ef8d2d1bdbdfe16661851..f2e985c00abd2e6f7aa1eaa1c9368f4c3ada60ab 100644 (file)
@@ -7,6 +7,7 @@
 #include "argv-array.h"
 #include "parse-options.h"
 #include "dir.h"
+#include "commit-slab.h"
 
 static const char* show_branch_usage[] = {
     N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
@@ -21,6 +22,11 @@ static int showbranch_use_color = -1;
 
 static struct argv_array default_args = ARGV_ARRAY_INIT;
 
+/*
+ * TODO: convert this use of commit->object.flags to commit-slab
+ * instead to store a pointer to ref name directly. Then use the same
+ * UNINTERESTING definition from revision.h here.
+ */
 #define UNINTERESTING  01
 
 #define REV_SHIFT       2
@@ -59,15 +65,27 @@ struct commit_name {
        int generation; /* how many parents away from head_name */
 };
 
+define_commit_slab(commit_name_slab, struct commit_name *);
+static struct commit_name_slab name_slab;
+
+static struct commit_name *commit_to_name(struct commit *commit)
+{
+       return *commit_name_slab_at(&name_slab, commit);
+}
+
+
 /* Name the commit as nth generation ancestor of head_name;
  * we count only the first-parent relationship for naming purposes.
  */
 static void name_commit(struct commit *commit, const char *head_name, int nth)
 {
        struct commit_name *name;
-       if (!commit->util)
-               commit->util = xmalloc(sizeof(struct commit_name));
-       name = commit->util;
+
+       name = *commit_name_slab_at(&name_slab, commit);
+       if (!name) {
+               name = xmalloc(sizeof(*name));
+               *commit_name_slab_at(&name_slab, commit) = name;
+       }
        name->head_name = head_name;
        name->generation = nth;
 }
@@ -79,8 +97,8 @@ static void name_commit(struct commit *commit, const char *head_name, int nth)
  */
 static void name_parent(struct commit *commit, struct commit *parent)
 {
-       struct commit_name *commit_name = commit->util;
-       struct commit_name *parent_name = parent->util;
+       struct commit_name *commit_name = commit_to_name(commit);
+       struct commit_name *parent_name = commit_to_name(parent);
        if (!commit_name)
                return;
        if (!parent_name ||
@@ -94,12 +112,12 @@ static int name_first_parent_chain(struct commit *c)
        int i = 0;
        while (c) {
                struct commit *p;
-               if (!c->util)
+               if (!commit_to_name(c))
                        break;
                if (!c->parents)
                        break;
                p = c->parents->item;
-               if (!p->util) {
+               if (!commit_to_name(p)) {
                        name_parent(c, p);
                        i++;
                }
@@ -122,7 +140,7 @@ static void name_commits(struct commit_list *list,
        /* First give names to the given heads */
        for (cl = list; cl; cl = cl->next) {
                c = cl->item;
-               if (c->util)
+               if (commit_to_name(c))
                        continue;
                for (i = 0; i < num_rev; i++) {
                        if (rev[i] == c) {
@@ -148,9 +166,9 @@ static void name_commits(struct commit_list *list,
                        struct commit_name *n;
                        int nth;
                        c = cl->item;
-                       if (!c->util)
+                       if (!commit_to_name(c))
                                continue;
-                       n = c->util;
+                       n = commit_to_name(c);
                        parents = c->parents;
                        nth = 0;
                        while (parents) {
@@ -158,7 +176,7 @@ static void name_commits(struct commit_list *list,
                                struct strbuf newname = STRBUF_INIT;
                                parents = parents->next;
                                nth++;
-                               if (p->util)
+                               if (commit_to_name(p))
                                        continue;
                                switch (n->generation) {
                                case 0:
@@ -271,7 +289,7 @@ static void show_one_commit(struct commit *commit, int no_name)
 {
        struct strbuf pretty = STRBUF_INIT;
        const char *pretty_str = "(unavailable)";
-       struct commit_name *name = commit->util;
+       struct commit_name *name = commit_to_name(commit);
 
        if (commit->object.parsed) {
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
@@ -660,6 +678,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                OPT_END()
        };
 
+       init_commit_name_slab(&name_slab);
+
        git_config(git_show_branch_config, NULL);
 
        /* If nothing is specified, try the default first */
diff --git a/builtin/show-index.c b/builtin/show-index.c
new file mode 100644 (file)
index 0000000..a6e6788
--- /dev/null
@@ -0,0 +1,86 @@
+#include "builtin.h"
+#include "cache.h"
+#include "pack.h"
+
+static const char show_index_usage[] =
+"git show-index";
+
+int cmd_show_index(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       unsigned nr;
+       unsigned int version;
+       static unsigned int top_index[256];
+
+       if (argc != 1)
+               usage(show_index_usage);
+       if (fread(top_index, 2 * 4, 1, stdin) != 1)
+               die("unable to read header");
+       if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
+               version = ntohl(top_index[1]);
+               if (version < 2 || version > 2)
+                       die("unknown index version");
+               if (fread(top_index, 256 * 4, 1, stdin) != 1)
+                       die("unable to read index");
+       } else {
+               version = 1;
+               if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
+                       die("unable to read index");
+       }
+       nr = 0;
+       for (i = 0; i < 256; i++) {
+               unsigned n = ntohl(top_index[i]);
+               if (n < nr)
+                       die("corrupt index file");
+               nr = n;
+       }
+       if (version == 1) {
+               for (i = 0; i < nr; i++) {
+                       unsigned int offset, entry[6];
+
+                       if (fread(entry, 4 + 20, 1, stdin) != 1)
+                               die("unable to read entry %u/%u", i, nr);
+                       offset = ntohl(entry[0]);
+                       printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
+               }
+       } else {
+               unsigned off64_nr = 0;
+               struct {
+                       unsigned char sha1[20];
+                       uint32_t crc;
+                       uint32_t off;
+               } *entries;
+               ALLOC_ARRAY(entries, nr);
+               for (i = 0; i < nr; i++)
+                       if (fread(entries[i].sha1, 20, 1, stdin) != 1)
+                               die("unable to read sha1 %u/%u", i, nr);
+               for (i = 0; i < nr; i++)
+                       if (fread(&entries[i].crc, 4, 1, stdin) != 1)
+                               die("unable to read crc %u/%u", i, nr);
+               for (i = 0; i < nr; i++)
+                       if (fread(&entries[i].off, 4, 1, stdin) != 1)
+                               die("unable to read 32b offset %u/%u", i, nr);
+               for (i = 0; i < nr; i++) {
+                       uint64_t offset;
+                       uint32_t off = ntohl(entries[i].off);
+                       if (!(off & 0x80000000)) {
+                               offset = off;
+                       } else {
+                               uint32_t off64[2];
+                               if ((off & 0x7fffffff) != off64_nr)
+                                       die("inconsistent 64b offset index");
+                               if (fread(off64, 8, 1, stdin) != 1)
+                                       die("unable to read 64b offset %u", off64_nr);
+                               offset = (((uint64_t)ntohl(off64[0])) << 32) |
+                                                    ntohl(off64[1]);
+                               off64_nr++;
+                       }
+                       printf("%" PRIuMAX " %s (%08"PRIx32")\n",
+                              (uintmax_t) offset,
+                              sha1_to_hex(entries[i].sha1),
+                              ntohl(entries[i].crc));
+               }
+               free(entries);
+       }
+       return 0;
+}
index f2eb1a7724058bb1db237a6199d16e5ff1ef495a..2f13f1316fadc2fc860fac75a161b47f7d11e391 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
 #include "refs.h"
+#include "object-store.h"
 #include "object.h"
 #include "tag.h"
 #include "string-list.h"
index bd250ca2164b31356fb42406351761cd2381d115..216e3daf5c9ea95d174efc89be2f42942d85472e 100644 (file)
@@ -55,7 +55,7 @@ static char *get_default_remote(void)
 
 static int print_default_remote(int argc, const char **argv, const char *prefix)
 {
-       const char *remote;
+       char *remote;
 
        if (argc != 1)
                die(_("submodule--helper print-default-remote takes no arguments"));
@@ -64,6 +64,7 @@ static int print_default_remote(int argc, const char **argv, const char *prefix)
        if (remote)
                printf("%s\n", remote);
 
+       free(remote);
        return 0;
 }
 
@@ -440,6 +441,149 @@ static void for_each_listed_submodule(const struct module_list *list,
                fn(list->entries[i], cb_data);
 }
 
+struct cb_foreach {
+       int argc;
+       const char **argv;
+       const char *prefix;
+       int quiet;
+       int recursive;
+};
+#define CB_FOREACH_INIT { 0 }
+
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
+                                      void *cb_data)
+{
+       struct cb_foreach *info = cb_data;
+       const char *path = list_item->name;
+       const struct object_id *ce_oid = &list_item->oid;
+
+       const struct submodule *sub;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       char *displaypath;
+
+       displaypath = get_submodule_displaypath(path, info->prefix);
+
+       sub = submodule_from_path(the_repository, &null_oid, path);
+
+       if (!sub)
+               die(_("No url found for submodule path '%s' in .gitmodules"),
+                       displaypath);
+
+       if (!is_submodule_populated_gently(path, NULL))
+               goto cleanup;
+
+       prepare_submodule_repo_env(&cp.env_array);
+
+       /*
+        * For the purpose of executing <command> in the submodule,
+        * separate shell is used for the purpose of running the
+        * child process.
+        */
+       cp.use_shell = 1;
+       cp.dir = path;
+
+       /*
+        * NEEDSWORK: the command currently has access to the variables $name,
+        * $sm_path, $displaypath, $sha1 and $toplevel only when the command
+        * contains a single argument. This is done for maintaining a faithful
+        * translation from shell script.
+        */
+       if (info->argc == 1) {
+               char *toplevel = xgetcwd();
+               struct strbuf sb = STRBUF_INIT;
+
+               argv_array_pushf(&cp.env_array, "name=%s", sub->name);
+               argv_array_pushf(&cp.env_array, "sm_path=%s", path);
+               argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
+               argv_array_pushf(&cp.env_array, "sha1=%s",
+                               oid_to_hex(ce_oid));
+               argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+
+               /*
+                * Since the path variable was accessible from the script
+                * before porting, it is also made available after porting.
+                * The environment variable "PATH" has a very special purpose
+                * on windows. And since environment variables are
+                * case-insensitive in windows, it interferes with the
+                * existing PATH variable. Hence, to avoid that, we expose
+                * path via the args argv_array and not via env_array.
+                */
+               sq_quote_buf(&sb, path);
+               argv_array_pushf(&cp.args, "path=%s; %s",
+                                sb.buf, info->argv[0]);
+               strbuf_release(&sb);
+               free(toplevel);
+       } else {
+               argv_array_pushv(&cp.args, info->argv);
+       }
+
+       if (!info->quiet)
+               printf(_("Entering '%s'\n"), displaypath);
+
+       if (info->argv[0] && run_command(&cp))
+               die(_("run_command returned non-zero status for %s\n."),
+                       displaypath);
+
+       if (info->recursive) {
+               struct child_process cpr = CHILD_PROCESS_INIT;
+
+               cpr.git_cmd = 1;
+               cpr.dir = path;
+               prepare_submodule_repo_env(&cpr.env_array);
+
+               argv_array_pushl(&cpr.args, "--super-prefix", NULL);
+               argv_array_pushf(&cpr.args, "%s/", displaypath);
+               argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+                               NULL);
+
+               if (info->quiet)
+                       argv_array_push(&cpr.args, "--quiet");
+
+               argv_array_pushv(&cpr.args, info->argv);
+
+               if (run_command(&cpr))
+                       die(_("run_command returned non-zero status while"
+                               "recursing in the nested submodules of %s\n."),
+                               displaypath);
+       }
+
+cleanup:
+       free(displaypath);
+}
+
+static int module_foreach(int argc, const char **argv, const char *prefix)
+{
+       struct cb_foreach info = CB_FOREACH_INIT;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+
+       struct option module_foreach_options[] = {
+               OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
+               OPT_BOOL(0, "recursive", &info.recursive,
+                        N_("Recurse into nested submodules")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_foreach_options,
+                            git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
+
+       if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       info.argc = argc;
+       info.argv = argv;
+       info.prefix = prefix;
+
+       for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
+
+       return 0;
+}
+
 struct init_cb {
        const char *prefix;
        unsigned int flags;
@@ -980,6 +1124,8 @@ static void deinit_submodule(const char *path, const char *prefix,
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
 
+               submodule_unset_core_worktree(sub);
+
                strbuf_release(&sb_rm);
        }
 
@@ -1860,6 +2006,29 @@ static int check_name(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int connect_gitdir_workingtree(int argc, const char **argv, const char *prefix)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *name, *path;
+       char *sm_gitdir;
+
+       if (argc != 3)
+               BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+
+       name = argv[1];
+       path = argv[2];
+
+       strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+       sm_gitdir = absolute_pathdup(sb.buf);
+
+       connect_work_tree_and_git_dir(path, sm_gitdir, 0);
+
+       strbuf_release(&sb);
+       free(sm_gitdir);
+
+       return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -1873,9 +2042,11 @@ static struct cmd_struct commands[] = {
        {"name", module_name, 0},
        {"clone", module_clone, 0},
        {"update-clone", update_clone, 0},
+       {"connect-gitdir-workingtree", connect_gitdir_workingtree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
+       {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
        {"print-default-remote", print_default_remote, 0},
index 5d0dd112408419a0090cbe179d69c0ea709f22a7..9919b03b2d601a74639553e8e61baef04ffd7cd9 100644 (file)
@@ -10,6 +10,7 @@
 #include "config.h"
 #include "builtin.h"
 #include "refs.h"
+#include "object-store.h"
 #include "tag.h"
 #include "run-command.h"
 #include "parse-options.h"
index 300eb59657e29cace38798029a9170834cac7c9e..58652229f273bf93e55b0ff5eef1421096cfe719 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "config.h"
+#include "object-store.h"
 
 static char *create_temp_file(struct object_id *oid)
 {
index 6e81ca8ca28a67ce3d7bc0d62556fd41eda2ba52..cf585fcc5eee5e41aac066a08d6db080f33011b8 100644 (file)
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "object.h"
 #include "delta.h"
 #include "pack.h"
index 4b4714b3fd82ca9510b9b241867e497cdae92e76..4fa3c0a86fd7cbeb5214223e06aa10a2ecfa2d85 100644 (file)
@@ -311,11 +311,12 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
 
 static const char *parse_cmd_option(struct strbuf *input, const char *next)
 {
-       if (!strncmp(next, "no-deref", 8) && next[8] == line_termination)
+       const char *rest;
+       if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
                update_flags |= REF_NO_DEREF;
        else
                die("option unknown: %s", next);
-       return next + 8;
+       return rest;
 }
 
 static void update_refs_stdin(struct ref_transaction *transaction)
@@ -332,16 +333,16 @@ static void update_refs_stdin(struct ref_transaction *transaction)
                        die("empty command in input");
                else if (isspace(*next))
                        die("whitespace before command: %s", next);
-               else if (starts_with(next, "update "))
-                       next = parse_cmd_update(transaction, &input, next + 7);
-               else if (starts_with(next, "create "))
-                       next = parse_cmd_create(transaction, &input, next + 7);
-               else if (starts_with(next, "delete "))
-                       next = parse_cmd_delete(transaction, &input, next + 7);
-               else if (starts_with(next, "verify "))
-                       next = parse_cmd_verify(transaction, &input, next + 7);
-               else if (starts_with(next, "option "))
-                       next = parse_cmd_option(&input, next + 7);
+               else if (skip_prefix(next, "update ", &next))
+                       next = parse_cmd_update(transaction, &input, next);
+               else if (skip_prefix(next, "create ", &next))
+                       next = parse_cmd_create(transaction, &input, next);
+               else if (skip_prefix(next, "delete ", &next))
+                       next = parse_cmd_delete(transaction, &input, next);
+               else if (skip_prefix(next, "verify ", &next))
+                       next = parse_cmd_verify(transaction, &input, next);
+               else if (skip_prefix(next, "option ", &next))
+                       next = parse_cmd_option(&input, next);
                else
                        die("unknown command: %s", next);
 
index dcdaada111071c84b56022d9050ae06ffafbc25f..f6922da16d6bfaadc20462f206d4ce1f53a4994f 100644 (file)
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "config.h"
 #include "builtin.h"
+#include "object-store.h"
 #include "commit.h"
 #include "run-command.h"
 #include <signal.h>
index b7e131c47a62603069185d67e5d2758175bc543f..9f3b644811cd79b2c93ebf1dcebd711640c18419 100644 (file)
@@ -8,6 +8,7 @@
 #include "pack.h"
 #include "strbuf.h"
 #include "packfile.h"
+#include "object-store.h"
 
 static struct bulk_checkin_state {
        unsigned plugged:1;
index 160bbfdc64ec2876bdc685d253bf537d1c739c1f..ba18e25d028001fd17b4807536ec2db3cd7a8b3d 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "lockfile.h"
 #include "bundle.h"
+#include "object-store.h"
 #include "object.h"
 #include "commit.h"
 #include "diff.h"
index 25663825b553b4e4590e3b4aaad54b4f5950aef8..6b467119960b03540e38209aa1a10f1dbe36c4dd 100644 (file)
@@ -3,6 +3,7 @@
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
+#include "object-store.h"
 
 #ifndef DEBUG
 #define DEBUG 0
diff --git a/cache.h b/cache.h
index 89a107a7f79175c600eb2a9689982912541ae4e7..8b447652a7e94ad31fd28134e0f548baa37265db 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -484,7 +484,7 @@ extern const char *get_git_dir(void);
 extern const char *get_git_common_dir(void);
 extern char *get_object_directory(void);
 extern char *get_index_file(void);
-extern char *get_graft_file(void);
+extern char *get_graft_file(struct repository *r);
 extern void set_git_dir(const char *path);
 extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
 extern int get_common_dir(struct strbuf *sb, const char *gitdir);
@@ -1192,32 +1192,6 @@ extern char *xdg_config_home(const char *filename);
  */
 extern char *xdg_cache_home(const char *filename);
 
-extern void *read_object_file_extended(const struct object_id *oid,
-                                      enum object_type *type,
-                                      unsigned long *size, int lookup_replace);
-static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
-{
-       return read_object_file_extended(oid, type, size, 1);
-}
-
-/* Read and unpack an object file into memory, write memory to an object file */
-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
-
-extern int hash_object_file(const void *buf, unsigned long len,
-                           const char *type, struct object_id *oid);
-
-extern int write_object_file(const void *buf, unsigned long len,
-                            const char *type, struct object_id *oid);
-
-extern int hash_object_file_literally(const void *buf, unsigned long len,
-                                     const char *type, struct object_id *oid,
-                                     unsigned flags);
-
-extern int pretend_object_file(void *, unsigned long, enum object_type,
-                              struct object_id *oid);
-
-extern int force_object_loose(const struct object_id *oid, time_t mtime);
-
 extern int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
@@ -1227,43 +1201,6 @@ extern int check_object_signature(const struct object_id *oid, void *buf, unsign
 
 extern int finalize_object_file(const char *tmpfile, const char *filename);
 
-/*
- * Open the loose object at path, check its hash, and return the contents,
- * type, and size. If the object is a blob, then "contents" may return NULL,
- * to allow streaming of large blobs.
- *
- * Returns 0 on success, negative on error (details may be written to stderr).
- */
-int read_loose_object(const char *path,
-                     const struct object_id *expected_oid,
-                     enum object_type *type,
-                     unsigned long *size,
-                     void **contents);
-
-/*
- * Convenience for sha1_object_info_extended() with a NULL struct
- * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass
- * nonzero flags to also set other flags.
- */
-extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
-static inline int has_sha1_file(const unsigned char *sha1)
-{
-       return has_sha1_file_with_flags(sha1, 0);
-}
-
-/* Same as the above, except for struct object_id. */
-extern int has_object_file(const struct object_id *oid);
-extern int has_object_file_with_flags(const struct object_id *oid, int flags);
-
-/*
- * Return true iff an alternate object database has a loose object
- * with the specified name.  This function does not respect replace
- * references.
- */
-extern int has_loose_object_nonlocal(const struct object_id *oid);
-
-extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
-
 /* Helper to check and "touch" a file */
 extern int check_and_freshen_file(const char *fn, int freshen);
 
@@ -1631,60 +1568,6 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path,
 #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 
-struct object_info {
-       /* Request */
-       enum object_type *typep;
-       unsigned long *sizep;
-       off_t *disk_sizep;
-       unsigned char *delta_base_sha1;
-       struct strbuf *type_name;
-       void **contentp;
-
-       /* Response */
-       enum {
-               OI_CACHED,
-               OI_LOOSE,
-               OI_PACKED,
-               OI_DBCACHED
-       } whence;
-       union {
-               /*
-                * struct {
-                *      ... Nothing to expose in this case
-                * } cached;
-                * struct {
-                *      ... Nothing to expose in this case
-                * } loose;
-                */
-               struct {
-                       struct packed_git *pack;
-                       off_t offset;
-                       unsigned int is_delta;
-               } packed;
-       } u;
-};
-
-/*
- * Initializer for a "struct object_info" that wants no items. You may
- * also memset() the memory to all-zeroes.
- */
-#define OBJECT_INFO_INIT {NULL}
-
-/* Invoke lookup_replace_object() on the given hash */
-#define OBJECT_INFO_LOOKUP_REPLACE 1
-/* Allow reading from a loose object file of unknown/bogus type */
-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
-/* Do not check cached storage */
-#define OBJECT_INFO_SKIP_CACHED 4
-/* Do not retry packed storage after checking packed and loose storage */
-#define OBJECT_INFO_QUICK 8
-/* Do not check loose object */
-#define OBJECT_INFO_IGNORE_LOOSE 16
-
-int oid_object_info_extended(struct repository *r,
-                            const struct object_id *,
-                            struct object_info *, unsigned flags);
-
 /*
  * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
  * blobs. This has a difference only if extensions.partialClone is set.
@@ -1770,15 +1653,6 @@ extern const char *excludes_file;
 int decode_85(char *dst, const char *line, int linelen);
 void encode_85(char *buf, const unsigned char *data, int bytes);
 
-/* alloc.c */
-extern void *alloc_blob_node(void);
-extern void *alloc_tree_node(void);
-extern void *alloc_commit_node(void);
-extern void *alloc_tag_node(void);
-extern void *alloc_object_node(void);
-extern void alloc_report(void);
-extern unsigned int alloc_commit_index(void);
-
 /* pkt-line.c */
 void packet_trace_identity(const char *prog);
 
index 2ef495963fc1cf2e092778d35f1965912d7eaac0..de7695e72824f6f041e0979e29302ded6e50d611 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "commit.h"
 #include "blob.h"
 #include "diff.h"
index 4c6127088ff96282fbcdc98ef5767281e52846e9..b63a1fc85eaded844fcb1a2634067f9509b5937c 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "config.h"
+#include "dir.h"
 #include "git-compat-util.h"
 #include "lockfile.h"
 #include "pack.h"
@@ -248,6 +249,13 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
        return &commit_list_insert(c, pptr)->next;
 }
 
+static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, uint32_t pos)
+{
+       const unsigned char *commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * pos;
+       item->graph_pos = pos;
+       item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+}
+
 static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos)
 {
        uint32_t edge_value;
@@ -265,6 +273,8 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        date_low = get_be32(commit_data + g->hash_len + 12);
        item->date = (timestamp_t)((date_high << 32) | date_low);
 
+       item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
        pptr = &item->parents;
 
        edge_value = get_be32(commit_data + g->hash_len);
@@ -293,31 +303,40 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        return 1;
 }
 
+static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t *pos)
+{
+       if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
+               *pos = item->graph_pos;
+               return 1;
+       } else {
+               return bsearch_graph(g, &(item->object.oid), pos);
+       }
+}
+
 int parse_commit_in_graph(struct commit *item)
 {
+       uint32_t pos;
+
        if (!core_commit_graph)
                return 0;
        if (item->object.parsed)
                return 1;
-
        prepare_commit_graph();
-       if (commit_graph) {
-               uint32_t pos;
-               int found;
-               if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
-                       pos = item->graph_pos;
-                       found = 1;
-               } else {
-                       found = bsearch_graph(commit_graph, &(item->object.oid), &pos);
-               }
-
-               if (found)
-                       return fill_commit_in_graph(item, commit_graph, pos);
-       }
-
+       if (commit_graph && find_commit_in_graph(item, commit_graph, &pos))
+               return fill_commit_in_graph(item, commit_graph, pos);
        return 0;
 }
 
+void load_commit_graph_info(struct commit *item)
+{
+       uint32_t pos;
+       if (!core_commit_graph)
+               return;
+       prepare_commit_graph();
+       if (commit_graph && find_commit_in_graph(item, commit_graph, &pos))
+               fill_commit_graph_info(item, commit_graph, pos);
+}
+
 static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c)
 {
        struct object_id oid;
@@ -440,6 +459,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                else
                        packedDate[0] = 0;
 
+               packedDate[0] |= htonl((*list)->generation << 2);
+
                packedDate[1] = htonl((*list)->date);
                hashwrite(f, packedDate, 8);
 
@@ -572,6 +593,45 @@ static void close_reachable(struct packed_oid_list *oids)
        }
 }
 
+static void compute_generation_numbers(struct packed_commit_list* commits)
+{
+       int i;
+       struct commit_list *list = NULL;
+
+       for (i = 0; i < commits->nr; i++) {
+               if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
+                   commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+                       continue;
+
+               commit_list_insert(commits->list[i], &list);
+               while (list) {
+                       struct commit *current = list->item;
+                       struct commit_list *parent;
+                       int all_parents_computed = 1;
+                       uint32_t max_generation = 0;
+
+                       for (parent = current->parents; parent; parent = parent->next) {
+                               if (parent->item->generation == GENERATION_NUMBER_INFINITY ||
+                                   parent->item->generation == GENERATION_NUMBER_ZERO) {
+                                       all_parents_computed = 0;
+                                       commit_list_insert(parent->item, &list);
+                                       break;
+                               } else if (parent->item->generation > max_generation) {
+                                       max_generation = parent->item->generation;
+                               }
+                       }
+
+                       if (all_parents_computed) {
+                               current->generation = max_generation + 1;
+                               pop_commit(&list);
+
+                               if (current->generation > GENERATION_NUMBER_MAX)
+                                       current->generation = GENERATION_NUMBER_MAX;
+                       }
+               }
+       }
+}
+
 void write_commit_graph(const char *obj_dir,
                        const char **pack_indexes,
                        int nr_packs,
@@ -584,7 +644,6 @@ void write_commit_graph(const char *obj_dir,
        struct hashfile *f;
        uint32_t i, count_distinct = 0;
        char *graph_name;
-       int fd;
        struct lock_file lk = LOCK_INIT;
        uint32_t chunk_ids[5];
        uint64_t chunk_offsets[5];
@@ -695,24 +754,14 @@ void write_commit_graph(const char *obj_dir,
        if (commits.nr >= GRAPH_PARENT_MISSING)
                die(_("too many commits to write graph"));
 
-       graph_name = get_commit_graph_filename(obj_dir);
-       fd = hold_lock_file_for_update(&lk, graph_name, 0);
-
-       if (fd < 0) {
-               struct strbuf folder = STRBUF_INIT;
-               strbuf_addstr(&folder, graph_name);
-               strbuf_setlen(&folder, strrchr(folder.buf, '/') - folder.buf);
-
-               if (mkdir(folder.buf, 0777) < 0)
-                       die_errno(_("cannot mkdir %s"), folder.buf);
-               strbuf_release(&folder);
-
-               fd = hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+       compute_generation_numbers(&commits);
 
-               if (fd < 0)
-                       die_errno("unable to create '%s'", graph_name);
-       }
+       graph_name = get_commit_graph_filename(obj_dir);
+       if (safe_create_leading_directories(graph_name))
+               die_errno(_("unable to create leading directories of %s"),
+                         graph_name);
 
+       hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
 
        hashwrite_be32(f, GRAPH_SIGNATURE);
index 260a468e73acd89b2a7706c859e8c25b264f3365..96cccb10f3d53a6da77f4ad06a971dbaa152f70c 100644 (file)
@@ -17,6 +17,14 @@ char *get_commit_graph_filename(const char *obj_dir);
  */
 int parse_commit_in_graph(struct commit *item);
 
+/*
+ * It is possible that we loaded commit contents from the commit buffer,
+ * but we also want to ensure the commit-graph content is correctly
+ * checked and filled. Fill the graph_pos and generation members of
+ * the given commit.
+ */
+void load_commit_graph_info(struct commit *item);
+
 struct tree *get_commit_tree_in_graph(const struct commit *c);
 
 struct commit_graph {
diff --git a/commit-slab-decl.h b/commit-slab-decl.h
new file mode 100644 (file)
index 0000000..adc7b46
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef COMMIT_SLAB_HDR_H
+#define COMMIT_SLAB_HDR_H
+
+/* allocate ~512kB at once, allowing for malloc overhead */
+#ifndef COMMIT_SLAB_SIZE
+#define COMMIT_SLAB_SIZE (512*1024-32)
+#endif
+
+#define declare_commit_slab(slabname, elemtype)                        \
+                                                                       \
+struct slabname {                                                      \
+       unsigned slab_size;                                             \
+       unsigned stride;                                                \
+       unsigned slab_count;                                            \
+       elemtype **slab;                                                \
+}
+
+/*
+ * Statically initialize a commit slab named "var". Note that this
+ * evaluates "stride" multiple times! Example:
+ *
+ *   struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees);
+ *
+ */
+#define COMMIT_SLAB_INIT(stride, var) { \
+       COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \
+       (stride), 0, NULL \
+}
+
+#define declare_commit_slab_prototypes(slabname, elemtype)             \
+                                                                       \
+void init_ ##slabname## _with_stride(struct slabname *s, unsigned stride); \
+void init_ ##slabname(struct slabname *s);                             \
+void clear_ ##slabname(struct slabname *s);                            \
+elemtype *slabname## _at_peek(struct slabname *s, const struct commit *c, int add_if_missing); \
+elemtype *slabname## _at(struct slabname *s, const struct commit *c);  \
+elemtype *slabname## _peek(struct slabname *s, const struct commit *c)
+
+#define define_shared_commit_slab(slabname, elemtype) \
+       declare_commit_slab(slabname, elemtype); \
+       declare_commit_slab_prototypes(slabname, elemtype)
+
+#endif /* COMMIT_SLAB_HDR_H */
diff --git a/commit-slab-impl.h b/commit-slab-impl.h
new file mode 100644 (file)
index 0000000..87a9cad
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef COMMIT_SLAB_IMPL_H
+#define COMMIT_SLAB_IMPL_H
+
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+#define implement_static_commit_slab(slabname, elemtype) \
+       implement_commit_slab(slabname, elemtype, static MAYBE_UNUSED)
+
+#define implement_shared_commit_slab(slabname, elemtype) \
+       implement_commit_slab(slabname, elemtype, )
+
+#define implement_commit_slab(slabname, elemtype, scope)               \
+                                                                       \
+static int stat_ ##slabname## realloc;                                 \
+                                                                       \
+scope void init_ ##slabname## _with_stride(struct slabname *s,         \
+                                                  unsigned stride)     \
+{                                                                      \
+       unsigned int elem_size;                                         \
+       if (!stride)                                                    \
+               stride = 1;                                             \
+       s->stride = stride;                                             \
+       elem_size = sizeof(elemtype) * stride;                          \
+       s->slab_size = COMMIT_SLAB_SIZE / elem_size;                    \
+       s->slab_count = 0;                                              \
+       s->slab = NULL;                                                 \
+}                                                                      \
+                                                                       \
+scope void init_ ##slabname(struct slabname *s)                                \
+{                                                                      \
+       init_ ##slabname## _with_stride(s, 1);                          \
+}                                                                      \
+                                                                       \
+scope void clear_ ##slabname(struct slabname *s)                       \
+{                                                                      \
+       unsigned int i;                                                 \
+       for (i = 0; i < s->slab_count; i++)                             \
+               free(s->slab[i]);                                       \
+       s->slab_count = 0;                                              \
+       FREE_AND_NULL(s->slab);                                         \
+}                                                                      \
+                                                                       \
+scope elemtype *slabname## _at_peek(struct slabname *s,                        \
+                                                 const struct commit *c, \
+                                                 int add_if_missing)   \
+{                                                                      \
+       unsigned int nth_slab, nth_slot;                                \
+                                                                       \
+       nth_slab = c->index / s->slab_size;                             \
+       nth_slot = c->index % s->slab_size;                             \
+                                                                       \
+       if (s->slab_count <= nth_slab) {                                \
+               unsigned int i;                                         \
+               if (!add_if_missing)                                    \
+                       return NULL;                                    \
+               REALLOC_ARRAY(s->slab, nth_slab + 1);                   \
+               stat_ ##slabname## realloc++;                           \
+               for (i = s->slab_count; i <= nth_slab; i++)             \
+                       s->slab[i] = NULL;                              \
+               s->slab_count = nth_slab + 1;                           \
+       }                                                               \
+       if (!s->slab[nth_slab]) {                                       \
+               if (!add_if_missing)                                    \
+                       return NULL;                                    \
+               s->slab[nth_slab] = xcalloc(s->slab_size,               \
+                                           sizeof(**s->slab) * s->stride);             \
+       }                                                               \
+       return &s->slab[nth_slab][nth_slot * s->stride];                \
+}                                                                      \
+                                                                       \
+scope elemtype *slabname## _at(struct slabname *s,                     \
+                                            const struct commit *c)    \
+{                                                                      \
+       return slabname##_at_peek(s, c, 1);                             \
+}                                                                      \
+                                                                       \
+scope elemtype *slabname## _peek(struct slabname *s,                   \
+                                            const struct commit *c)    \
+{                                                                      \
+       return slabname##_at_peek(s, c, 0);                             \
+}                                                                      \
+                                                                       \
+struct slabname
+
+/*
+ * Note that this redundant forward declaration is required
+ * to allow a terminating semicolon, which makes instantiations look
+ * like function declarations.  I.e., the expansion of
+ *
+ *    implement_commit_slab(indegree, int, static);
+ *
+ * ends in 'struct indegree;'.  This would otherwise
+ * be a syntax error according (at least) to ISO C.  It's hard to
+ * catch because GCC silently parses it by default.
+ */
+
+#endif /* COMMIT_SLAB_IMPL_H */
index dcaab8ca0437b3adefb523cad531ac1f4b9264e0..69bf0c807c64b79216118f3485cfd5dac3b6a273 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef COMMIT_SLAB_H
 #define COMMIT_SLAB_H
 
+#include "commit-slab-decl.h"
+#include "commit-slab-impl.h"
+
 /*
  * define_commit_slab(slabname, elemtype) creates boilerplate code to define
  * a new struct (struct slabname) that is used to associate a piece of data
  *   leaking memory.
  */
 
-/* allocate ~512kB at once, allowing for malloc overhead */
-#ifndef COMMIT_SLAB_SIZE
-#define COMMIT_SLAB_SIZE (512*1024-32)
-#endif
-
-#define MAYBE_UNUSED __attribute__((__unused__))
-
-#define define_commit_slab(slabname, elemtype)                                 \
-                                                                       \
-struct slabname {                                                      \
-       unsigned slab_size;                                             \
-       unsigned stride;                                                \
-       unsigned slab_count;                                            \
-       elemtype **slab;                                                \
-};                                                                     \
-static int stat_ ##slabname## realloc;                                 \
-                                                                       \
-static MAYBE_UNUSED void init_ ##slabname## _with_stride(struct slabname *s, \
-                                                  unsigned stride)     \
-{                                                                      \
-       unsigned int elem_size;                                         \
-       if (!stride)                                                    \
-               stride = 1;                                             \
-       s->stride = stride;                                             \
-       elem_size = sizeof(elemtype) * stride;                          \
-       s->slab_size = COMMIT_SLAB_SIZE / elem_size;                    \
-       s->slab_count = 0;                                              \
-       s->slab = NULL;                                                 \
-}                                                                      \
-                                                                       \
-static MAYBE_UNUSED void init_ ##slabname(struct slabname *s)          \
-{                                                                      \
-       init_ ##slabname## _with_stride(s, 1);                          \
-}                                                                      \
-                                                                       \
-static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s)         \
-{                                                                      \
-       unsigned int i;                                                 \
-       for (i = 0; i < s->slab_count; i++)                             \
-               free(s->slab[i]);                                       \
-       s->slab_count = 0;                                              \
-       FREE_AND_NULL(s->slab);                                         \
-}                                                                      \
-                                                                       \
-static MAYBE_UNUSED elemtype *slabname## _at_peek(struct slabname *s,  \
-                                                 const struct commit *c, \
-                                                 int add_if_missing)   \
-{                                                                      \
-       unsigned int nth_slab, nth_slot;                                \
-                                                                       \
-       nth_slab = c->index / s->slab_size;                             \
-       nth_slot = c->index % s->slab_size;                             \
-                                                                       \
-       if (s->slab_count <= nth_slab) {                                \
-               unsigned int i;                                         \
-               if (!add_if_missing)                                    \
-                       return NULL;                                    \
-               REALLOC_ARRAY(s->slab, nth_slab + 1);                   \
-               stat_ ##slabname## realloc++;                           \
-               for (i = s->slab_count; i <= nth_slab; i++)             \
-                       s->slab[i] = NULL;                              \
-               s->slab_count = nth_slab + 1;                           \
-       }                                                               \
-       if (!s->slab[nth_slab]) {                                       \
-               if (!add_if_missing)                                    \
-                       return NULL;                                    \
-               s->slab[nth_slab] = xcalloc(s->slab_size,               \
-                                           sizeof(**s->slab) * s->stride);             \
-       }                                                               \
-       return &s->slab[nth_slab][nth_slot * s->stride];                \
-}                                                                      \
-                                                                       \
-static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s,       \
-                                            const struct commit *c)    \
-{                                                                      \
-       return slabname##_at_peek(s, c, 1);                             \
-}                                                                      \
-                                                                       \
-static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s,     \
-                                            const struct commit *c)    \
-{                                                                      \
-       return slabname##_at_peek(s, c, 0);                             \
-}                                                                      \
-                                                                       \
-struct slabname
-
-/*
- * Note that this redundant forward declaration is required
- * to allow a terminating semicolon, which makes instantiations look
- * like function declarations.  I.e., the expansion of
- *
- *    define_commit_slab(indegree, int);
- *
- * ends in 'struct indegree;'.  This would otherwise
- * be a syntax error according (at least) to ISO C.  It's hard to
- * catch because GCC silently parses it by default.
- */
-
-/*
- * Statically initialize a commit slab named "var". Note that this
- * evaluates "stride" multiple times! Example:
- *
- *   struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees);
- *
- */
-#define COMMIT_SLAB_INIT(stride, var) { \
-       COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \
-       (stride), 0, NULL \
-}
+#define define_commit_slab(slabname, elemtype) \
+       declare_commit_slab(slabname, elemtype); \
+       implement_static_commit_slab(slabname, elemtype)
 
 #endif /* COMMIT_SLAB_H */
index 0030e79940ff8564b5c8c5ebab462eb1bd745174..a7c0b5f8c6ce1d1d41c7ede357116fee4a643bbc 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -2,11 +2,14 @@
 #include "tag.h"
 #include "commit.h"
 #include "commit-graph.h"
+#include "repository.h"
+#include "object-store.h"
 #include "pkt-line.h"
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
 #include "notes.h"
+#include "alloc.h"
 #include "gpg-interface.h"
 #include "mergesort.h"
 #include "commit-slab.h"
@@ -52,7 +55,8 @@ struct commit *lookup_commit(const struct object_id *oid)
 {
        struct object *obj = lookup_object(oid->hash);
        if (!obj)
-               return create_object(oid->hash, alloc_commit_node());
+               return create_object(the_repository, oid->hash,
+                                    alloc_commit_node(the_repository));
        return object_as_type(obj, OBJ_COMMIT, 0);
 }
 
@@ -96,41 +100,44 @@ static timestamp_t parse_commit_date(const char *buf, const char *tail)
        return parse_timestamp(dateptr, NULL, 10);
 }
 
-static struct commit_graft **commit_graft;
-static int commit_graft_alloc, commit_graft_nr;
-
 static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
 {
        struct commit_graft **commit_graft_table = table;
        return commit_graft_table[index]->oid.hash;
 }
 
-static int commit_graft_pos(const unsigned char *sha1)
+static int commit_graft_pos(struct repository *r, const unsigned char *sha1)
 {
-       return sha1_pos(sha1, commit_graft, commit_graft_nr,
+       return sha1_pos(sha1, r->parsed_objects->grafts,
+                       r->parsed_objects->grafts_nr,
                        commit_graft_sha1_access);
 }
 
-int register_commit_graft(struct commit_graft *graft, int ignore_dups)
+int register_commit_graft(struct repository *r, struct commit_graft *graft,
+                         int ignore_dups)
 {
-       int pos = commit_graft_pos(graft->oid.hash);
+       int pos = commit_graft_pos(r, graft->oid.hash);
 
        if (0 <= pos) {
                if (ignore_dups)
                        free(graft);
                else {
-                       free(commit_graft[pos]);
-                       commit_graft[pos] = graft;
+                       free(r->parsed_objects->grafts[pos]);
+                       r->parsed_objects->grafts[pos] = graft;
                }
                return 1;
        }
        pos = -pos - 1;
-       ALLOC_GROW(commit_graft, commit_graft_nr + 1, commit_graft_alloc);
-       commit_graft_nr++;
-       if (pos < commit_graft_nr)
-               MOVE_ARRAY(commit_graft + pos + 1, commit_graft + pos,
-                          commit_graft_nr - pos - 1);
-       commit_graft[pos] = graft;
+       ALLOC_GROW(r->parsed_objects->grafts,
+                  r->parsed_objects->grafts_nr + 1,
+                  r->parsed_objects->grafts_alloc);
+       r->parsed_objects->grafts_nr++;
+       if (pos < r->parsed_objects->grafts_nr)
+               memmove(r->parsed_objects->grafts + pos + 1,
+                       r->parsed_objects->grafts + pos,
+                       (r->parsed_objects->grafts_nr - pos - 1) *
+                       sizeof(*r->parsed_objects->grafts));
+       r->parsed_objects->grafts[pos] = graft;
        return 0;
 }
 
@@ -172,7 +179,7 @@ struct commit_graft *read_graft_line(struct strbuf *line)
        return NULL;
 }
 
-static int read_graft_file(const char *graft_file)
+static int read_graft_file(struct repository *r, const char *graft_file)
 {
        FILE *fp = fopen_or_warn(graft_file, "r");
        struct strbuf buf = STRBUF_INIT;
@@ -192,7 +199,7 @@ static int read_graft_file(const char *graft_file)
                struct commit_graft *graft = read_graft_line(&buf);
                if (!graft)
                        continue;
-               if (register_commit_graft(graft, 1))
+               if (register_commit_graft(r, graft, 1))
                        error("duplicate graft data: %s", buf.buf);
        }
        fclose(fp);
@@ -200,50 +207,50 @@ static int read_graft_file(const char *graft_file)
        return 0;
 }
 
-static void prepare_commit_graft(void)
+static void prepare_commit_graft(struct repository *r)
 {
-       static int commit_graft_prepared;
        char *graft_file;
 
-       if (commit_graft_prepared)
+       if (r->parsed_objects->commit_graft_prepared)
                return;
        if (!startup_info->have_repository)
                return;
 
-       graft_file = get_graft_file();
-       read_graft_file(graft_file);
+       graft_file = get_graft_file(r);
+       read_graft_file(r, graft_file);
        /* make sure shallows are read */
-       is_repository_shallow();
-       commit_graft_prepared = 1;
+       is_repository_shallow(r);
+       r->parsed_objects->commit_graft_prepared = 1;
 }
 
-struct commit_graft *lookup_commit_graft(const struct object_id *oid)
+struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid)
 {
        int pos;
-       prepare_commit_graft();
-       pos = commit_graft_pos(oid->hash);
+       prepare_commit_graft(r);
+       pos = commit_graft_pos(r, oid->hash);
        if (pos < 0)
                return NULL;
-       return commit_graft[pos];
+       return r->parsed_objects->grafts[pos];
 }
 
 int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
 {
        int i, ret;
-       for (i = ret = 0; i < commit_graft_nr && !ret; i++)
-               ret = fn(commit_graft[i], cb_data);
+       for (i = ret = 0; i < the_repository->parsed_objects->grafts_nr && !ret; i++)
+               ret = fn(the_repository->parsed_objects->grafts[i], cb_data);
        return ret;
 }
 
 int unregister_shallow(const struct object_id *oid)
 {
-       int pos = commit_graft_pos(oid->hash);
+       int pos = commit_graft_pos(the_repository, oid->hash);
        if (pos < 0)
                return -1;
-       if (pos + 1 < commit_graft_nr)
-               MOVE_ARRAY(commit_graft + pos, commit_graft + pos + 1,
-                          commit_graft_nr - pos - 1);
-       commit_graft_nr--;
+       if (pos + 1 < the_repository->parsed_objects->grafts_nr)
+               MOVE_ARRAY(the_repository->parsed_objects->grafts + pos,
+                          the_repository->parsed_objects->grafts + pos + 1,
+                          the_repository->parsed_objects->grafts_nr - pos - 1);
+       the_repository->parsed_objects->grafts_nr--;
        return 0;
 }
 
@@ -325,6 +332,17 @@ struct object_id *get_commit_tree_oid(const struct commit *commit)
        return &get_commit_tree(commit)->object.oid;
 }
 
+void release_commit_memory(struct commit *c)
+{
+       c->maybe_tree = NULL;
+       c->index = 0;
+       free_commit_buffer(c);
+       free_commit_list(c->parents);
+       /* TODO: what about commit->util? */
+
+       c->object.parsed = 0;
+}
+
 const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
 {
        struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
@@ -344,7 +362,7 @@ const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
        return ret;
 }
 
-int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size, int check_graph)
 {
        const char *tail = buffer;
        const char *bufptr = buffer;
@@ -368,7 +386,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
-       graft = lookup_commit_graft(&item->object.oid);
+       graft = lookup_commit_graft(the_repository, &item->object.oid);
        while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
                struct commit *new_parent;
 
@@ -399,6 +417,9 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
        }
        item->date = parse_commit_date(bufptr, tail);
 
+       if (check_graph)
+               load_commit_graph_info(item);
+
        return 0;
 }
 
@@ -425,7 +446,7 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return error("Object %s not a commit",
                             oid_to_hex(&item->object.oid));
        }
-       ret = parse_commit_buffer(item, buffer, size);
+       ret = parse_commit_buffer(item, buffer, size, 0);
        if (save_commit_buffer && !ret) {
                set_commit_buffer(item, buffer, size);
                return 0;
@@ -653,6 +674,24 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
        return 0;
 }
 
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
+{
+       const struct commit *a = a_, *b = b_;
+
+       /* newer commits first */
+       if (a->generation < b->generation)
+               return 1;
+       else if (a->generation > b->generation)
+               return -1;
+
+       /* use date as a heuristic when generations are equal */
+       if (a->date < b->date)
+               return 1;
+       else if (a->date > b->date)
+               return -1;
+       return 0;
+}
+
 int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
 {
        const struct commit *a = a_, *b = b_;
@@ -800,11 +839,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
 }
 
 /* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
+static struct commit_list *paint_down_to_common(struct commit *one, int n,
+                                               struct commit **twos,
+                                               int min_generation)
 {
-       struct prio_queue queue = { compare_commits_by_commit_date };
+       struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
        struct commit_list *result = NULL;
        int i;
+       uint32_t last_gen = GENERATION_NUMBER_INFINITY;
 
        one->object.flags |= PARENT1;
        if (!n) {
@@ -823,6 +865,15 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
                struct commit_list *parents;
                int flags;
 
+               if (commit->generation > last_gen)
+                       BUG("bad generation skip %8x > %8x at %s",
+                           commit->generation, last_gen,
+                           oid_to_hex(&commit->object.oid));
+               last_gen = commit->generation;
+
+               if (commit->generation < min_generation)
+                       break;
+
                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
@@ -871,7 +922,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                        return NULL;
        }
 
-       list = paint_down_to_common(one, n, twos);
+       list = paint_down_to_common(one, n, twos, 0);
 
        while (list) {
                struct commit *commit = pop_commit(&list);
@@ -929,6 +980,7 @@ static int remove_redundant(struct commit **array, int cnt)
                parse_commit(array[i]);
        for (i = 0; i < cnt; i++) {
                struct commit_list *common;
+               uint32_t min_generation = array[i]->generation;
 
                if (redundant[i])
                        continue;
@@ -937,8 +989,12 @@ static int remove_redundant(struct commit **array, int cnt)
                                continue;
                        filled_index[filled] = j;
                        work[filled++] = array[j];
+
+                       if (array[j]->generation < min_generation)
+                               min_generation = array[j]->generation;
                }
-               common = paint_down_to_common(array[i], filled, work);
+               common = paint_down_to_common(array[i], filled, work,
+                                             min_generation);
                if (array[i]->object.flags & PARENT2)
                        redundant[i] = 1;
                for (j = 0; j < filled; j++)
@@ -1048,14 +1104,21 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
 {
        struct commit_list *bases;
        int ret = 0, i;
+       uint32_t min_generation = GENERATION_NUMBER_INFINITY;
 
        if (parse_commit(commit))
                return ret;
-       for (i = 0; i < nr_reference; i++)
+       for (i = 0; i < nr_reference; i++) {
                if (parse_commit(reference[i]))
                        return ret;
+               if (reference[i]->generation < min_generation)
+                       min_generation = reference[i]->generation;
+       }
+
+       if (commit->generation > min_generation)
+               return ret;
 
-       bases = paint_down_to_common(commit, nr_reference, reference);
+       bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
        if (commit->object.flags & PARENT2)
                ret = 1;
        clear_commit_marks(commit, all_flags);
@@ -1605,13 +1668,21 @@ int commit_tree_extended(const char *msg, size_t msg_len,
        return result;
 }
 
+define_commit_slab(merge_desc_slab, struct merge_remote_desc *);
+static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab);
+
+struct merge_remote_desc *merge_remote_util(struct commit *commit)
+{
+       return *merge_desc_slab_at(&merge_desc_slab, commit);
+}
+
 void set_merge_remote_desc(struct commit *commit,
                           const char *name, struct object *obj)
 {
        struct merge_remote_desc *desc;
        FLEX_ALLOC_STR(desc, name, name);
        desc->obj = obj;
-       commit->util = desc;
+       *merge_desc_slab_at(&merge_desc_slab, commit) = desc;
 }
 
 struct commit *get_merge_parent(const char *name)
@@ -1623,7 +1694,7 @@ struct commit *get_merge_parent(const char *name)
                return NULL;
        obj = parse_object(&oid);
        commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-       if (commit && !commit->util)
+       if (commit && !merge_remote_util(commit))
                set_merge_remote_desc(commit, name, obj);
        return commit;
 }
index c3af512f8b1b90b3963d0921f7ec0d0a43a2fcbe..01b8b1d6896b9ce8e532cd49474cc384704fecfc 100644 (file)
--- a/commit.h
+++ b/commit.h
 #include "pretty.h"
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
+#define GENERATION_NUMBER_INFINITY 0xFFFFFFFF
+#define GENERATION_NUMBER_MAX 0x3FFFFFFF
+#define GENERATION_NUMBER_ZERO 0
 
 struct commit_list {
        struct commit *item;
        struct commit_list *next;
 };
 
+/*
+ * The size of this struct matters in full repo walk operations like
+ * 'git clone' or 'git gc'. Consider using commit-slab to attach data
+ * to a commit instead of adding new fields here.
+ */
 struct commit {
        struct object object;
-       void *util;
        timestamp_t date;
        struct commit_list *parents;
 
@@ -29,6 +36,7 @@ struct commit {
         */
        struct tree *maybe_tree;
        uint32_t graph_pos;
+       uint32_t generation;
        unsigned int index;
 };
 
@@ -68,7 +76,7 @@ struct commit *lookup_commit_reference_by_name(const char *name);
  */
 struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name);
 
-int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size, int check_graph);
 int parse_commit_gently(struct commit *item, int quiet_on_missing);
 static inline int parse_commit(struct commit *item)
 {
@@ -111,6 +119,12 @@ void free_commit_buffer(struct commit *);
 struct tree *get_commit_tree(const struct commit *);
 struct object_id *get_commit_tree_oid(const struct commit *);
 
+/*
+ * Release memory related to a commit, including the parent list and
+ * any cached object buffer.
+ */
+void release_commit_memory(struct commit *c);
+
 /*
  * Disassociate any cached object buffer from the commit, but do not free it.
  * The buffer (or NULL, if none) is returned.
@@ -180,8 +194,8 @@ struct commit_graft {
 typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
 struct commit_graft *read_graft_line(struct strbuf *line);
-int register_commit_graft(struct commit_graft *, int);
-struct commit_graft *lookup_commit_graft(const struct object_id *oid);
+int register_commit_graft(struct repository *r, struct commit_graft *, int);
+struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
 extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos);
@@ -195,15 +209,15 @@ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n,
 
 struct oid_array;
 struct ref;
-extern int register_shallow(const struct object_id *oid);
+extern int register_shallow(struct repository *r, const struct object_id *oid);
 extern int unregister_shallow(const struct object_id *oid);
 extern int for_each_commit_graft(each_commit_graft_fn, void *);
-extern int is_repository_shallow(void);
+extern int is_repository_shallow(struct repository *r);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 extern struct commit_list *get_shallow_commits_by_rev_list(
                int ac, const char **av, int shallow_flag, int not_shallow_flag);
-extern void set_alternate_shallow_file(const char *path, int override);
+extern void set_alternate_shallow_file(struct repository *r, const char *path, int override);
 extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
                                 const struct oid_array *extra);
 extern void setup_alternate_shallow(struct lock_file *shallow_lock,
@@ -312,7 +326,7 @@ struct merge_remote_desc {
        struct object *obj; /* the named object, could be a tag */
        char name[FLEX_ARRAY];
 };
-#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
+extern struct merge_remote_desc *merge_remote_util(struct commit *);
 extern void set_merge_remote_desc(struct commit *commit,
                                  const char *name, struct object *obj);
 
@@ -337,6 +351,7 @@ extern int remove_signature(struct strbuf *buf);
 extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
 
 int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);
 
 LAST_ARG_MUST_BE_NULL
 extern int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
index fbbf0f8e9f2b2ae0a96769108d8a2773f071aec1..139c903f6b3f1f35f2a443ebcd8b09b74ffcb7b5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -14,6 +14,7 @@
 #include "quote.h"
 #include "hashmap.h"
 #include "string-list.h"
+#include "object-store.h"
 #include "utf8.h"
 #include "dir.h"
 #include "color.h"
@@ -1233,7 +1234,7 @@ static int git_default_core_config(const char *var, const char *value)
                }
                eol_rndtrp_die = git_config_bool(var, value);
                global_conv_flags_eol = eol_rndtrp_die ?
-                       CONV_EOL_RNDTRP_DIE : CONV_EOL_RNDTRP_WARN;
+                       CONV_EOL_RNDTRP_DIE : 0;
                return 0;
        }
 
@@ -3245,3 +3246,16 @@ enum config_scope current_config_scope(void)
        else
                return current_parsing_scope;
 }
+
+int lookup_config(const char **mapping, int nr_mapping, const char *var)
+{
+       int i;
+
+       for (i = 0; i < nr_mapping; i++) {
+               const char *name = mapping[i];
+
+               if (name && !strcasecmp(var, name))
+                       return i;
+       }
+       return -1;
+}
index cdac2fc73e6a2d0bc3230848425557a23e88d0bf..626d4654bd6f98771be903cca208fdb368920bf4 100644 (file)
--- a/config.h
+++ b/config.h
@@ -257,4 +257,8 @@ struct key_value_info {
 extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
 extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 
+#define LOOKUP_CONFIG(mapping, var) \
+       lookup_config(mapping, ARRAY_SIZE(mapping), var)
+int lookup_config(const char **mapping, int nr_mapping, const char *var);
+
 #endif /* CONFIG_H */
index dd3e925843acf53c439e504101d46d8606ea5d69..94c95516ebec7f32787cc5a2fb45cae83c7e28e3 100644 (file)
@@ -330,9 +330,32 @@ __gitcomp ()
        case "$cur_" in
        --*=)
                ;;
+       --no-*)
+               local c i=0 IFS=$' \t\n'
+               for c in $1; do
+                       if [[ $c == "--" ]]; then
+                               continue
+                       fi
+                       c="$c${4-}"
+                       if [[ $c == "$cur_"* ]]; then
+                               case $c in
+                               --*=*|*.) ;;
+                               *) c="$c " ;;
+                               esac
+                               COMPREPLY[i++]="${2-}$c"
+                       fi
+               done
+               ;;
        *)
                local c i=0 IFS=$' \t\n'
                for c in $1; do
+                       if [[ $c == "--" ]]; then
+                               c="--no-...${4-}"
+                               if [[ $c == "$cur_"* ]]; then
+                                       COMPREPLY[i++]="${2-}$c "
+                               fi
+                               break
+                       fi
                        c="$c${4-}"
                        if [[ $c == "$cur_"* ]]; then
                                case $c in
@@ -1161,7 +1184,7 @@ _git_am ()
                return
                ;;
        --*)
-               __gitcomp_builtin am "--no-utf8" \
+               __gitcomp_builtin am "" \
                        "$__git_am_inprogress_options"
                return
        esac
@@ -1261,9 +1284,7 @@ _git_branch ()
                __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
-               __gitcomp_builtin branch "--no-color --no-abbrev
-                       --no-track --no-column
-                       "
+               __gitcomp_builtin branch
                ;;
        *)
                if [ $only_local_ref = "y" -a $has_r = "n" ]; then
@@ -1304,7 +1325,7 @@ _git_checkout ()
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
                ;;
        --*)
-               __gitcomp_builtin checkout "--no-track --no-recurse-submodules"
+               __gitcomp_builtin checkout
                ;;
        *)
                # check if --track, --no-track, or --no-guess was specified
@@ -1367,7 +1388,7 @@ _git_clone ()
 {
        case "$cur" in
        --*)
-               __gitcomp_builtin clone "--no-single-branch"
+               __gitcomp_builtin clone
                return
                ;;
        esac
@@ -1400,7 +1421,7 @@ _git_commit ()
                return
                ;;
        --*)
-               __gitcomp_builtin commit "--no-edit --verify"
+               __gitcomp_builtin commit
                return
        esac
 
@@ -1503,7 +1524,7 @@ _git_fetch ()
                return
                ;;
        --*)
-               __gitcomp_builtin fetch "--no-tags"
+               __gitcomp_builtin fetch
                return
                ;;
        esac
@@ -1540,7 +1561,7 @@ _git_fsck ()
 {
        case "$cur" in
        --*)
-               __gitcomp_builtin fsck "--no-reflogs"
+               __gitcomp_builtin fsck
                return
                ;;
        esac
@@ -1646,7 +1667,7 @@ _git_ls_files ()
 {
        case "$cur" in
        --*)
-               __gitcomp_builtin ls-files "--no-empty-directory"
+               __gitcomp_builtin ls-files
                return
                ;;
        esac
@@ -1797,12 +1818,7 @@ _git_merge ()
 
        case "$cur" in
        --*)
-               __gitcomp_builtin merge "--no-rerere-autoupdate
-                               --no-commit --no-edit --no-ff
-                               --no-log --no-progress
-                               --no-squash --no-stat
-                               --no-verify-signatures
-                               "
+               __gitcomp_builtin merge
                return
        esac
        __git_complete_refs
@@ -1901,10 +1917,7 @@ _git_pull ()
                return
                ;;
        --*)
-               __gitcomp_builtin pull "--no-autostash --no-commit --no-edit
-                                       --no-ff --no-log --no-progress --no-rebase
-                                       --no-squash --no-stat --no-tags
-                                       --no-verify-signatures"
+               __gitcomp_builtin pull
 
                return
                ;;
@@ -2095,7 +2108,7 @@ _git_status ()
                return
                ;;
        --*)
-               __gitcomp_builtin status "--no-column"
+               __gitcomp_builtin status
                return
                ;;
        esac
@@ -2142,9 +2155,24 @@ __git_config_get_set_variables ()
        __git config $config_file --name-only --list
 }
 
+__git_config_vars=
+__git_compute_config_vars ()
+{
+       test -n "$__git_config_vars" ||
+       __git_config_vars="$(git help --config-for-completion | sort | uniq)"
+}
+
 _git_config ()
 {
-       case "$prev" in
+       local varname
+
+       if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
+               varname="${prev,,}"
+       else
+               varname="$(echo "$prev" |tr A-Z a-z)"
+       fi
+
+       case "$varname" in
        branch.*.remote|branch.*.pushremote)
                __gitcomp_nl "$(__git_remotes)"
                return
@@ -2242,20 +2270,20 @@ _git_config ()
                ;;
        branch.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
+               __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_"
                return
                ;;
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
-               __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
+               __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_"
                return
                ;;
        guitool.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
-                       argprompt cmd confirm needsfile noconsole norescan
-                       prompt revprompt revunmerged title
+                       argPrompt cmd confirm needsFile noConsole noRescan
+                       prompt revPrompt revUnmerged title
                        " "$pfx" "$cur_"
                return
                ;;
@@ -2284,14 +2312,14 @@ _git_config ()
                local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
-                       receivepack uploadpack tagopt pushurl
+                       receivepack uploadpack tagOpt pushurl
                        " "$pfx" "$cur_"
                return
                ;;
        remote.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
-               __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
+               __gitcomp_nl_append "pushDefault" "$pfx" "$cur_"
                return
                ;;
        url.*.*)
@@ -2299,333 +2327,14 @@ _git_config ()
                __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
                return
                ;;
+       *.*)
+               __git_compute_config_vars
+               __gitcomp "$__git_config_vars"
+               ;;
+       *)
+               __git_compute_config_vars
+               __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')"
        esac
-       __gitcomp "
-               add.ignoreErrors
-               advice.amWorkDir
-               advice.commitBeforeMerge
-               advice.detachedHead
-               advice.implicitIdentity
-               advice.pushAlreadyExists
-               advice.pushFetchFirst
-               advice.pushNeedsForce
-               advice.pushNonFFCurrent
-               advice.pushNonFFMatching
-               advice.pushUpdateRejected
-               advice.resolveConflict
-               advice.rmHints
-               advice.statusHints
-               advice.statusUoption
-               advice.ignoredHook
-               alias.
-               am.keepcr
-               am.threeWay
-               apply.ignorewhitespace
-               apply.whitespace
-               branch.autosetupmerge
-               branch.autosetuprebase
-               browser.
-               clean.requireForce
-               color.branch
-               color.branch.current
-               color.branch.local
-               color.branch.plain
-               color.branch.remote
-               color.decorate.HEAD
-               color.decorate.branch
-               color.decorate.remoteBranch
-               color.decorate.stash
-               color.decorate.tag
-               color.diff
-               color.diff.commit
-               color.diff.frag
-               color.diff.func
-               color.diff.meta
-               color.diff.new
-               color.diff.old
-               color.diff.plain
-               color.diff.whitespace
-               color.grep
-               color.grep.context
-               color.grep.filename
-               color.grep.function
-               color.grep.linenumber
-               color.grep.match
-               color.grep.selected
-               color.grep.separator
-               color.interactive
-               color.interactive.error
-               color.interactive.header
-               color.interactive.help
-               color.interactive.prompt
-               color.pager
-               color.showbranch
-               color.status
-               color.status.added
-               color.status.changed
-               color.status.header
-               color.status.localBranch
-               color.status.nobranch
-               color.status.remoteBranch
-               color.status.unmerged
-               color.status.untracked
-               color.status.updated
-               color.ui
-               commit.cleanup
-               commit.gpgSign
-               commit.status
-               commit.template
-               commit.verbose
-               core.abbrev
-               core.askpass
-               core.attributesfile
-               core.autocrlf
-               core.bare
-               core.bigFileThreshold
-               core.checkStat
-               core.commentChar
-               core.commitGraph
-               core.compression
-               core.createObject
-               core.deltaBaseCacheLimit
-               core.editor
-               core.eol
-               core.excludesfile
-               core.fileMode
-               core.fsyncobjectfiles
-               core.gitProxy
-               core.hideDotFiles
-               core.hooksPath
-               core.ignoreStat
-               core.ignorecase
-               core.logAllRefUpdates
-               core.loosecompression
-               core.notesRef
-               core.packedGitLimit
-               core.packedGitWindowSize
-               core.packedRefsTimeout
-               core.pager
-               core.precomposeUnicode
-               core.preferSymlinkRefs
-               core.preloadindex
-               core.protectHFS
-               core.protectNTFS
-               core.quotepath
-               core.repositoryFormatVersion
-               core.safecrlf
-               core.sharedRepository
-               core.sparseCheckout
-               core.splitIndex
-               core.sshCommand
-               core.symlinks
-               core.trustctime
-               core.untrackedCache
-               core.warnAmbiguousRefs
-               core.whitespace
-               core.worktree
-               credential.helper
-               credential.useHttpPath
-               credential.username
-               credentialCache.ignoreSIGHUP
-               diff.autorefreshindex
-               diff.external
-               diff.ignoreSubmodules
-               diff.mnemonicprefix
-               diff.noprefix
-               diff.renameLimit
-               diff.renames
-               diff.statGraphWidth
-               diff.submodule
-               diff.suppressBlankEmpty
-               diff.tool
-               diff.wordRegex
-               diff.algorithm
-               difftool.
-               difftool.prompt
-               fetch.recurseSubmodules
-               fetch.unpackLimit
-               format.attach
-               format.cc
-               format.coverLetter
-               format.from
-               format.headers
-               format.numbered
-               format.pretty
-               format.signature
-               format.signoff
-               format.subjectprefix
-               format.suffix
-               format.thread
-               format.to
-               gc.
-               gc.aggressiveDepth
-               gc.aggressiveWindow
-               gc.auto
-               gc.autoDetach
-               gc.autopacklimit
-               gc.logExpiry
-               gc.packrefs
-               gc.pruneexpire
-               gc.reflogexpire
-               gc.reflogexpireunreachable
-               gc.rerereresolved
-               gc.rerereunresolved
-               gc.worktreePruneExpire
-               gitcvs.allbinary
-               gitcvs.commitmsgannotation
-               gitcvs.dbTableNamePrefix
-               gitcvs.dbdriver
-               gitcvs.dbname
-               gitcvs.dbpass
-               gitcvs.dbuser
-               gitcvs.enabled
-               gitcvs.logfile
-               gitcvs.usecrlfattr
-               guitool.
-               gui.blamehistoryctx
-               gui.commitmsgwidth
-               gui.copyblamethreshold
-               gui.diffcontext
-               gui.encoding
-               gui.fastcopyblame
-               gui.matchtrackingbranch
-               gui.newbranchtemplate
-               gui.pruneduringfetch
-               gui.spellingdictionary
-               gui.trustmtime
-               help.autocorrect
-               help.browser
-               help.format
-               http.lowSpeedLimit
-               http.lowSpeedTime
-               http.maxRequests
-               http.minSessions
-               http.noEPSV
-               http.postBuffer
-               http.proxy
-               http.sslCipherList
-               http.sslVersion
-               http.sslCAInfo
-               http.sslCAPath
-               http.sslCert
-               http.sslCertPasswordProtected
-               http.sslKey
-               http.sslVerify
-               http.useragent
-               i18n.commitEncoding
-               i18n.logOutputEncoding
-               imap.authMethod
-               imap.folder
-               imap.host
-               imap.pass
-               imap.port
-               imap.preformattedHTML
-               imap.sslverify
-               imap.tunnel
-               imap.user
-               init.templatedir
-               instaweb.browser
-               instaweb.httpd
-               instaweb.local
-               instaweb.modulepath
-               instaweb.port
-               interactive.singlekey
-               log.date
-               log.decorate
-               log.showroot
-               mailmap.file
-               man.
-               man.viewer
-               merge.
-               merge.conflictstyle
-               merge.log
-               merge.renameLimit
-               merge.renormalize
-               merge.stat
-               merge.tool
-               merge.verbosity
-               mergetool.
-               mergetool.keepBackup
-               mergetool.keepTemporaries
-               mergetool.prompt
-               notes.displayRef
-               notes.rewrite.
-               notes.rewrite.amend
-               notes.rewrite.rebase
-               notes.rewriteMode
-               notes.rewriteRef
-               pack.compression
-               pack.deltaCacheLimit
-               pack.deltaCacheSize
-               pack.depth
-               pack.indexVersion
-               pack.packSizeLimit
-               pack.threads
-               pack.window
-               pack.windowMemory
-               pager.
-               pretty.
-               pull.octopus
-               pull.twohead
-               push.default
-               push.followTags
-               rebase.autosquash
-               rebase.stat
-               receive.autogc
-               receive.denyCurrentBranch
-               receive.denyDeleteCurrent
-               receive.denyDeletes
-               receive.denyNonFastForwards
-               receive.fsckObjects
-               receive.unpackLimit
-               receive.updateserverinfo
-               remote.pushdefault
-               remotes.
-               repack.usedeltabaseoffset
-               rerere.autoupdate
-               rerere.enabled
-               sendemail.
-               sendemail.aliasesfile
-               sendemail.aliasfiletype
-               sendemail.bcc
-               sendemail.cc
-               sendemail.cccmd
-               sendemail.chainreplyto
-               sendemail.confirm
-               sendemail.envelopesender
-               sendemail.from
-               sendemail.identity
-               sendemail.multiedit
-               sendemail.signedoffbycc
-               sendemail.smtpdomain
-               sendemail.smtpencryption
-               sendemail.smtppass
-               sendemail.smtpserver
-               sendemail.smtpserveroption
-               sendemail.smtpserverport
-               sendemail.smtpuser
-               sendemail.suppresscc
-               sendemail.suppressfrom
-               sendemail.thread
-               sendemail.to
-               sendemail.tocmd
-               sendemail.validate
-               sendemail.smtpbatchsize
-               sendemail.smtprelogindelay
-               showbranch.default
-               status.relativePaths
-               status.showUntrackedFiles
-               status.submodulesummary
-               submodule.
-               tar.umask
-               transfer.unpackLimit
-               url.
-               user.email
-               user.name
-               user.signingkey
-               web.browser
-               branch. remote.
-       "
 }
 
 _git_remote ()
@@ -2649,7 +2358,7 @@ _git_remote ()
 
        case "$subcommand,$cur" in
        add,--*)
-               __gitcomp_builtin remote_add "--no-tags"
+               __gitcomp_builtin remote_add
                ;;
        add,*)
                ;;
@@ -2666,7 +2375,7 @@ _git_remote ()
                __gitcomp_builtin remote_update
                ;;
        update,*)
-               __gitcomp "$(__git_get_config_variables "remotes")"
+               __gitcomp "$(__git_remotes) $(__git_get_config_variables "remotes")"
                ;;
        set-url,--*)
                __gitcomp_builtin remote_set-url
@@ -2729,7 +2438,7 @@ _git_revert ()
        fi
        case "$cur" in
        --*)
-               __gitcomp_builtin revert "--no-edit" \
+               __gitcomp_builtin revert "" \
                        "$__git_revert_inprogress_options"
                return
                ;;
@@ -2799,7 +2508,7 @@ _git_show_branch ()
 {
        case "$cur" in
        --*)
-               __gitcomp_builtin show-branch "--no-color"
+               __gitcomp_builtin show-branch
                return
                ;;
        esac
index 0ffa40719126fe285722cfa8fc7b461d0584cd3a..6174e3bb83826273f005b01f6d9e9b2093664e33 100644 (file)
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
 test:
        ./t-git-credential-netrc.sh
 
index 58191a62f8623642d4bf1f2af50130132d529fb3..07227d02287618394d649ae915a1b42d93140950 100755 (executable)
        # set up test repository
 
        test_expect_success \
-    'set up test repository' \
-    'git config --add gpg.program test.git-config-gpg'
+               'set up test repository' \
+               'git config --add gpg.program test.git-config-gpg'
 
        # The external test will outputs its own plan
        test_external_has_tap=1
 
+       export PERL5LIB="$GITPERLLIB"
        test_external \
-    'git-credential-netrc' \
-    perl "$TEST_DIRECTORY"/../contrib/credential/netrc/test.pl
+               'git-credential-netrc' \
+               perl "$GIT_BUILD_DIR"/contrib/credential/netrc/test.pl
 
        test_done
 )
index 1e1001030e7631dd49cbfeb002b429158019f191..c0fb3718b280c70316941de7617871360b724525 100755 (executable)
@@ -1,5 +1,4 @@
 #!/usr/bin/perl
-use lib (split(/:/, $ENV{GITPERLLIB}));
 
 use warnings;
 use strict;
@@ -12,7 +11,6 @@ BEGIN
        # t-git-credential-netrc.sh kicks off our testing, so we have to go
        # from there.
        Test::More->builder->current_test(1);
-       Test::More->builder->no_ending(1);
 }
 
 my @global_credential_args = @ARGV;
@@ -104,6 +102,9 @@ BEGIN
 
 ok(scalar keys %$cred == 2, 'Got keys decrypted by command option');
 
+my $is_passing = eval { Test::More->is_passing };
+exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
+
 sub run_credential
 {
        my $args = shift @_;
index 64d0d30e08de4acd496bf955d9ce64afa0ff5b8b..56cfe31ec5363c6f1e9b3d51ec700c7d46730080 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1,6 +1,7 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "attr.h"
 #include "run-command.h"
 #include "quote.h"
index 104f954a25700a4eee95507e7ebb7734e94b67c6..a9f38eb5a3e0e17d111301b581d06876ce5fd510 100644 (file)
@@ -389,8 +389,12 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        struct rev_info *revs = o->unpack_data;
        int match_missing, cached;
 
-       /* i-t-a entries do not actually exist in the index */
-       if (revs->diffopt.ita_invisible_in_index &&
+       /*
+        * i-t-a entries do not actually exist in the index (if we're
+        * looking at its content)
+        */
+       if (o->index_only &&
+           revs->diffopt.ita_invisible_in_index &&
            idx && ce_intent_to_add(idx)) {
                idx = NULL;
                if (!tree)
diff --git a/diff.c b/diff.c
index 136d44b45560d5c9db7e88a1ff22aa6b086108e8..dc53a19bab609eac8de52e57f437c79da7fbb1fa 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -13,6 +13,7 @@
 #include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
+#include "object-store.h"
 #include "userdiff.h"
 #include "submodule-config.h"
 #include "submodule.h"
@@ -22,6 +23,7 @@
 #include "argv-array.h"
 #include "graph.h"
 #include "packfile.h"
+#include "help.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -69,46 +71,37 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
 };
 
+static const char *color_diff_slots[] = {
+       [DIFF_CONTEXT]                = "context",
+       [DIFF_METAINFO]               = "meta",
+       [DIFF_FRAGINFO]               = "frag",
+       [DIFF_FILE_OLD]               = "old",
+       [DIFF_FILE_NEW]               = "new",
+       [DIFF_COMMIT]                 = "commit",
+       [DIFF_WHITESPACE]             = "whitespace",
+       [DIFF_FUNCINFO]               = "func",
+       [DIFF_FILE_OLD_MOVED]         = "oldMoved",
+       [DIFF_FILE_OLD_MOVED_ALT]     = "oldMovedAlternative",
+       [DIFF_FILE_OLD_MOVED_DIM]     = "oldMovedDimmed",
+       [DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed",
+       [DIFF_FILE_NEW_MOVED]         = "newMoved",
+       [DIFF_FILE_NEW_MOVED_ALT]     = "newMovedAlternative",
+       [DIFF_FILE_NEW_MOVED_DIM]     = "newMovedDimmed",
+       [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed",
+};
+
 static NORETURN void die_want_option(const char *option_name)
 {
        die(_("option '%s' requires a value"), option_name);
 }
 
+define_list_config_array_extra(color_diff_slots, {"plain"});
+
 static int parse_diff_color_slot(const char *var)
 {
-       if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
+       if (!strcasecmp(var, "plain"))
                return DIFF_CONTEXT;
-       if (!strcasecmp(var, "meta"))
-               return DIFF_METAINFO;
-       if (!strcasecmp(var, "frag"))
-               return DIFF_FRAGINFO;
-       if (!strcasecmp(var, "old"))
-               return DIFF_FILE_OLD;
-       if (!strcasecmp(var, "new"))
-               return DIFF_FILE_NEW;
-       if (!strcasecmp(var, "commit"))
-               return DIFF_COMMIT;
-       if (!strcasecmp(var, "whitespace"))
-               return DIFF_WHITESPACE;
-       if (!strcasecmp(var, "func"))
-               return DIFF_FUNCINFO;
-       if (!strcasecmp(var, "oldmoved"))
-               return DIFF_FILE_OLD_MOVED;
-       if (!strcasecmp(var, "oldmovedalternative"))
-               return DIFF_FILE_OLD_MOVED_ALT;
-       if (!strcasecmp(var, "oldmoveddimmed"))
-               return DIFF_FILE_OLD_MOVED_DIM;
-       if (!strcasecmp(var, "oldmovedalternativedimmed"))
-               return DIFF_FILE_OLD_MOVED_ALT_DIM;
-       if (!strcasecmp(var, "newmoved"))
-               return DIFF_FILE_NEW_MOVED;
-       if (!strcasecmp(var, "newmovedalternative"))
-               return DIFF_FILE_NEW_MOVED_ALT;
-       if (!strcasecmp(var, "newmoveddimmed"))
-               return DIFF_FILE_NEW_MOVED_DIM;
-       if (!strcasecmp(var, "newmovedalternativedimmed"))
-               return DIFF_FILE_NEW_MOVED_ALT_DIM;
-       return -1;
+       return LOOKUP_CONFIG(color_diff_slots, var);
 }
 
 static int parse_dirstat_params(struct diff_options *options, const char *params_string,
index 0b7e4989a87214faa22e4f8ec75a719d3fd857ae..d775183c2fd1bed06a97facb56c4721e4e5df2f6 100644 (file)
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "object-store.h"
 #include "hashmap.h"
 #include "progress.h"
 
diff --git a/dir.c b/dir.c
index fe9bf58e4c72790dca5492de4bd98df06798d27a..a1fe57e925f8ceede7780fc2542ade4f55b15bca 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -11,6 +11,7 @@
 #include "cache.h"
 #include "config.h"
 #include "dir.h"
+#include "object-store.h"
 #include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
diff --git a/entry.c b/entry.c
index 2101201a111785f449a65e785e9ed5b57b7aa196..b5d1d3cf2312f61b551258759fcc4b217d8a6fa7 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "blob.h"
+#include "object-store.h"
 #include "dir.h"
 #include "streaming.h"
 #include "submodule.h"
index 2a6de2330bc024d19ab0c1d8cc594f146ca6da11..013e845235ea88e977c3181258b3e77e6ecb4c2f 100644 (file)
@@ -192,7 +192,7 @@ void setup_git_env(const char *git_dir)
        git_namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
        shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
        if (shallow_file)
-               set_alternate_shallow_file(shallow_file, 0);
+               set_alternate_shallow_file(the_repository, shallow_file, 0);
 }
 
 int is_bare_repository(void)
@@ -319,11 +319,11 @@ char *get_index_file(void)
        return the_repository->index_file;
 }
 
-char *get_graft_file(void)
+char *get_graft_file(struct repository *r)
 {
-       if (!the_repository->graft_file)
+       if (!r->graft_file)
                BUG("git environment hasn't been setup");
-       return the_repository->graft_file;
+       return r->graft_file;
 }
 
 static void set_git_dir_1(const char *path)
index 756bdd050e6df9e90bb08edc73617351d1c1f9b1..52f1178db4ce35103545a3e1ee00a3000b08eb90 100644 (file)
@@ -45,14 +45,6 @@ void bitmap_set(struct bitmap *self, size_t pos)
        self->words[block] |= EWAH_MASK(pos);
 }
 
-void bitmap_clear(struct bitmap *self, size_t pos)
-{
-       size_t block = EWAH_BLOCK(pos);
-
-       if (block < self->word_alloc)
-               self->words[block] &= ~EWAH_MASK(pos);
-}
-
 int bitmap_get(struct bitmap *self, size_t pos)
 {
        size_t block = EWAH_BLOCK(pos);
@@ -137,30 +129,6 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
                self->words[i++] |= word;
 }
 
-void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)
-{
-       size_t pos = 0, i;
-
-       for (i = 0; i < self->word_alloc; ++i) {
-               eword_t word = self->words[i];
-               uint32_t offset;
-
-               if (word == (eword_t)~0) {
-                       for (offset = 0; offset < BITS_IN_EWORD; ++offset)
-                               callback(pos++, data);
-               } else {
-                       for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-                               if ((word >> offset) == 0)
-                                       break;
-
-                               offset += ewah_bit_ctz64(word >> offset);
-                               callback(pos + offset, data);
-                       }
-                       pos += BITS_IN_EWORD;
-               }
-       }
-}
-
 size_t bitmap_popcount(struct bitmap *self)
 {
        size_t i, count = 0;
index b9fdda1d3d2e033ddcfa7c1b31730d4188c75306..d59b1afe3d6a83b89dc30e19e61f8a3e9b1cc0aa 100644 (file)
@@ -276,6 +276,18 @@ void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), vo
        }
 }
 
+/**
+ * Clear all the bits in the bitmap. Does not free or resize
+ * memory.
+ */
+static void ewah_clear(struct ewah_bitmap *self)
+{
+       self->buffer_size = 1;
+       self->buffer[0] = 0;
+       self->bit_size = 0;
+       self->rlw = self->buffer;
+}
+
 struct ewah_bitmap *ewah_new(void)
 {
        struct ewah_bitmap *self;
@@ -288,14 +300,6 @@ struct ewah_bitmap *ewah_new(void)
        return self;
 }
 
-void ewah_clear(struct ewah_bitmap *self)
-{
-       self->buffer_size = 1;
-       self->buffer[0] = 0;
-       self->bit_size = 0;
-       self->rlw = self->buffer;
-}
-
 void ewah_free(struct ewah_bitmap *self)
 {
        if (!self)
@@ -376,25 +380,6 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)
                read_new_rlw(it);
 }
 
-void ewah_not(struct ewah_bitmap *self)
-{
-       size_t pointer = 0;
-
-       while (pointer < self->buffer_size) {
-               eword_t *word = &self->buffer[pointer];
-               size_t literals, k;
-
-               rlw_xor_run_bit(word);
-               ++pointer;
-
-               literals = rlw_get_literal_words(word);
-               for (k = 0; k < literals; ++k) {
-                       self->buffer[pointer] = ~self->buffer[pointer];
-                       ++pointer;
-               }
-       }
-}
-
 void ewah_xor(
        struct ewah_bitmap *ewah_i,
        struct ewah_bitmap *ewah_j,
@@ -459,216 +444,6 @@ void ewah_xor(
        out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
 }
 
-void ewah_and(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if (predator->rlw.running_bit == 0) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, 0);
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
-                                       rlw_j.buffer[rlw_j.literal_word_start + k]
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge_empty(&rlw_i, out);
-       else
-               rlwit_discharge_empty(&rlw_j, out);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-void ewah_and_not(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if ((predator->rlw.running_bit && prey == &rlw_i) ||
-                               (!predator->rlw.running_bit && prey != &rlw_i)) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index;
-                               int negate_words;
-
-                               negate_words = (&rlw_i != prey);
-                               index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, negate_words);
-                               ewah_add_empty_words(out, negate_words,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
-                                       ~(rlw_j.buffer[rlw_j.literal_word_start + k])
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge(&rlw_i, out, ~0, 0);
-       else
-               rlwit_discharge_empty(&rlw_j, out);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-void ewah_or(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if (predator->rlw.running_bit) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, 0);
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] |
-                                       rlw_j.buffer[rlw_j.literal_word_start + k]
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge(&rlw_i, out, ~0, 0);
-       else
-               rlwit_discharge(&rlw_j, out, ~0, 0);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-
 #define BITMAP_POOL_MAX 16
 static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];
 static size_t bitmap_pool_size;
index 33c08c40f896f32d9a1c251c1456755e949d91bd..9035ee65ea8db67c373492fcb1755f8b1499679e 100644 (file)
 #include "ewok.h"
 #include "strbuf.h"
 
-int ewah_serialize_native(struct ewah_bitmap *self, int fd)
-{
-       uint32_t write32;
-       size_t to_write = self->buffer_size * 8;
-
-       /* 32 bit -- bit size for the map */
-       write32 = (uint32_t)self->bit_size;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       /** 32 bit -- number of compressed 64-bit words */
-       write32 = (uint32_t)self->buffer_size;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       if (write(fd, self->buffer, to_write) != to_write)
-               return -1;
-
-       /** 32 bit -- position for the RLW */
-       write32 = self->rlw - self->buffer;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       return (3 * 4) + to_write;
-}
-
 int ewah_serialize_to(struct ewah_bitmap *self,
                      int (*write_fun)(void *, const void *, size_t),
                      void *data)
@@ -100,16 +74,6 @@ int ewah_serialize_to(struct ewah_bitmap *self,
        return (3 * 4) + (self->buffer_size * 8);
 }
 
-static int write_helper(void *fd, const void *buf, size_t len)
-{
-       return write((intptr_t)fd, buf, len);
-}
-
-int ewah_serialize(struct ewah_bitmap *self, int fd)
-{
-       return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
-}
-
 static int write_strbuf(void *user_data, const void *data, size_t len)
 {
        struct strbuf *sb = user_data;
@@ -168,58 +132,3 @@ ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 
        return ptr - (const uint8_t *)map;
 }
-
-int ewah_deserialize(struct ewah_bitmap *self, int fd)
-{
-       size_t i;
-       eword_t dump[2048];
-       const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
-       uint32_t bitsize, word_count, rlw_pos;
-
-       eword_t *buffer = NULL;
-       size_t words_left;
-
-       ewah_clear(self);
-
-       /* 32 bit -- bit size for the map */
-       if (read(fd, &bitsize, 4) != 4)
-               return -1;
-
-       self->bit_size = (size_t)ntohl(bitsize);
-
-       /** 32 bit -- number of compressed 64-bit words */
-       if (read(fd, &word_count, 4) != 4)
-               return -1;
-
-       self->buffer_size = self->alloc_size = (size_t)ntohl(word_count);
-       REALLOC_ARRAY(self->buffer, self->alloc_size);
-
-       /** 64 bit x N -- compressed words */
-       buffer = self->buffer;
-       words_left = self->buffer_size;
-
-       while (words_left >= words_per_dump) {
-               if (read(fd, dump, sizeof(dump)) != sizeof(dump))
-                       return -1;
-
-               for (i = 0; i < words_per_dump; ++i, ++buffer)
-                       *buffer = ntohll(dump[i]);
-
-               words_left -= words_per_dump;
-       }
-
-       if (words_left) {
-               if (read(fd, dump, words_left * 8) != words_left * 8)
-                       return -1;
-
-               for (i = 0; i < words_left; ++i, ++buffer)
-                       *buffer = ntohll(dump[i]);
-       }
-
-       /** 32 bit -- position for the RLW */
-       if (read(fd, &rlw_pos, 4) != 4)
-               return -1;
-
-       self->rlw = self->buffer + ntohl(rlw_pos);
-       return 0;
-}
index b9643b7d0f4420b0ebbd7315a3308407bdfafa53..5093d43e2f00e2bec4290c1fe918d8a0593e2e8c 100644 (file)
@@ -104,11 +104,3 @@ size_t rlwit_discharge(
 
        return index;
 }
-
-void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)
-{
-       while (rlwit_word_size(it) > 0) {
-               ewah_add_empty_words(out, 0, rlwit_word_size(it));
-               rlwit_discard_first_words(it, rlwit_word_size(it));
-       }
-}
index 357fd93c84edbe79f9bcb10e2947ed935ac9f0df..84b2a29faa0c57410168ad5afb2dbaa7a49ddd60 100644 (file)
@@ -72,12 +72,6 @@ void ewah_pool_free(struct ewah_bitmap *self);
  */
 struct ewah_bitmap *ewah_new(void);
 
-/**
- * Clear all the bits in the bitmap. Does not free or resize
- * memory.
- */
-void ewah_clear(struct ewah_bitmap *self);
-
 /**
  * Free all the memory of the bitmap
  */
@@ -86,22 +80,12 @@ void ewah_free(struct ewah_bitmap *self);
 int ewah_serialize_to(struct ewah_bitmap *self,
                      int (*write_fun)(void *out, const void *buf, size_t len),
                      void *out);
-int ewah_serialize(struct ewah_bitmap *self, int fd);
-int ewah_serialize_native(struct ewah_bitmap *self, int fd);
 int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *);
 
-int ewah_deserialize(struct ewah_bitmap *self, int fd);
 ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
 
 uint32_t ewah_checksum(struct ewah_bitmap *self);
 
-/**
- * Logical not (bitwise negation) in-place on the bitmap
- *
- * This operation is linear time based on the size of the bitmap.
- */
-void ewah_not(struct ewah_bitmap *self);
-
 /**
  * Call the given callback with the position of every single bit
  * that has been set on the bitmap.
@@ -164,26 +148,11 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);
  */
 int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);
 
-void ewah_or(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
-void ewah_and_not(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
 void ewah_xor(
        struct ewah_bitmap *ewah_i,
        struct ewah_bitmap *ewah_j,
        struct ewah_bitmap *out);
 
-void ewah_and(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
 /**
  * Direct word access
  */
@@ -204,7 +173,6 @@ struct bitmap {
 
 struct bitmap *bitmap_new(void);
 void bitmap_set(struct bitmap *self, size_t pos);
-void bitmap_clear(struct bitmap *self, size_t pos);
 int bitmap_get(struct bitmap *self, size_t pos);
 void bitmap_reset(struct bitmap *self);
 void bitmap_free(struct bitmap *self);
@@ -218,7 +186,6 @@ void bitmap_and_not(struct bitmap *self, struct bitmap *other);
 void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
 void bitmap_or(struct bitmap *self, const struct bitmap *other);
 
-void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);
 size_t bitmap_popcount(struct bitmap *self);
 
 #endif
index bb3c6ff7e05c0e3cb51a2ae5a065baa372e1d7f4..7cdfdd0c020e37abbc30624b2ea1bcf4ef0ddfee 100644 (file)
@@ -98,7 +98,6 @@ void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);
 void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);
 size_t rlwit_discharge(
        struct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);
-void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);
 
 static inline size_t rlwit_word_size(struct rlw_iterator *it)
 {
index a320ce9872b5e752aeb592a16cc9108ae87e9d71..dbd879ac7fd92f370a0056593a1d633750e2f7fd 100644 (file)
@@ -19,6 +19,7 @@
 #include "sha1-array.h"
 #include "oidset.h"
 #include "packfile.h"
+#include "object-store.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -396,7 +397,7 @@ static int find_common(struct fetch_pack_args *args,
                return 1;
        }
 
-       if (is_repository_shallow())
+       if (is_repository_shallow(the_repository))
                write_shallow_commits(&req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
@@ -427,7 +428,7 @@ static int find_common(struct fetch_pack_args *args,
                        if (skip_prefix(line, "shallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
                                        die(_("invalid shallow line: %s"), line);
-                               register_shallow(&oid);
+                               register_shallow(the_repository, &oid);
                                continue;
                        }
                        if (skip_prefix(line, "unshallow ", &arg)) {
@@ -657,11 +658,11 @@ static void filter_refs(struct fetch_pack_args *args,
                                }
                                i++;
                        }
-               }
 
-               if (!keep && args->fetch_all &&
-                   (!args->deepen || !starts_with(ref->name, "refs/tags/")))
-                       keep = 1;
+                       if (!keep && args->fetch_all &&
+                           (!args->deepen || !starts_with(ref->name, "refs/tags/")))
+                               keep = 1;
+               }
 
                if (keep) {
                        *newtail = ref;
@@ -985,7 +986,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        sort_ref_list(&ref, ref_compare_name);
        QSORT(sought, nr_sought, cmp_ref_by_name);
 
-       if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
+       if ((args->depth > 0 || is_repository_shallow(the_repository)) && !server_supports("shallow"))
                die(_("Server does not support shallow clients"));
        if (args->depth > 0 || args->deepen_since || args->deepen_not)
                args->deepen = 1;
@@ -1083,7 +1084,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 static void add_shallow_requests(struct strbuf *req_buf,
                                 const struct fetch_pack_args *args)
 {
-       if (is_repository_shallow())
+       if (is_repository_shallow(the_repository))
                write_shallow_commits(req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(req_buf, "deepen %d", args->depth);
@@ -1195,7 +1196,7 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
        /* Add shallow-info and deepen request */
        if (server_supports_feature("fetch", "shallow", 0))
                add_shallow_requests(&req_buf, args);
-       else if (is_repository_shallow() || args->deepen)
+       else if (is_repository_shallow(the_repository) || args->deepen)
                die(_("Server does not support shallow requests"));
 
        /* Add filter */
@@ -1308,7 +1309,7 @@ static void receive_shallow_info(struct fetch_pack_args *args,
                if (skip_prefix(reader->line, "shallow ", &arg)) {
                        if (get_oid_hex(arg, &oid))
                                die(_("invalid shallow line: %s"), reader->line);
-                       register_shallow(&oid);
+                       register_shallow(the_repository, &oid);
                        continue;
                }
                if (skip_prefix(reader->line, "unshallow ", &arg)) {
@@ -1479,7 +1480,7 @@ static void update_shallow(struct fetch_pack_args *args,
 
        if (args->deepen && alternate_shallow_file) {
                if (*alternate_shallow_file == '\0') { /* --unshallow */
-                       unlink_or_warn(git_path_shallow());
+                       unlink_or_warn(git_path_shallow(the_repository));
                        rollback_lock_file(&shallow_lock);
                } else
                        commit_lock_file(&shallow_lock);
diff --git a/fsck.c b/fsck.c
index 48e7e36869a7cbcb47e2e2a0401dd1047612e5d7..4dfe65f715467cb31dab418af658968e491a9645 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "object.h"
 #include "blob.h"
 #include "tree.h"
@@ -14,6 +15,7 @@
 #include "packfile.h"
 #include "submodule-config.h"
 #include "config.h"
+#include "help.h"
 
 static struct oidset gitmodules_found = OIDSET_INIT;
 static struct oidset gitmodules_done = OIDSET_INIT;
@@ -86,37 +88,60 @@ enum fsck_msg_id {
 #undef MSG_ID
 
 #define STR(x) #x
-#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },
+#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
 static struct {
        const char *id_string;
        const char *downcased;
+       const char *camelcased;
        int msg_type;
 } msg_id_info[FSCK_MSG_MAX + 1] = {
        FOREACH_MSG_ID(MSG_ID)
-       { NULL, NULL, -1 }
+       { NULL, NULL, NULL, -1 }
 };
 #undef MSG_ID
 
-static int parse_msg_id(const char *text)
+static void prepare_msg_ids(void)
 {
        int i;
 
-       if (!msg_id_info[0].downcased) {
-               /* convert id_string to lower case, without underscores. */
-               for (i = 0; i < FSCK_MSG_MAX; i++) {
-                       const char *p = msg_id_info[i].id_string;
-                       int len = strlen(p);
-                       char *q = xmalloc(len);
-
-                       msg_id_info[i].downcased = q;
-                       while (*p)
-                               if (*p == '_')
-                                       p++;
-                               else
-                                       *(q)++ = tolower(*(p)++);
-                       *q = '\0';
+       if (msg_id_info[0].downcased)
+               return;
+
+       /* convert id_string to lower case, without underscores. */
+       for (i = 0; i < FSCK_MSG_MAX; i++) {
+               const char *p = msg_id_info[i].id_string;
+               int len = strlen(p);
+               char *q = xmalloc(len);
+
+               msg_id_info[i].downcased = q;
+               while (*p)
+                       if (*p == '_')
+                               p++;
+                       else
+                               *(q)++ = tolower(*(p)++);
+               *q = '\0';
+
+               p = msg_id_info[i].id_string;
+               q = xmalloc(len);
+               msg_id_info[i].camelcased = q;
+               while (*p) {
+                       if (*p == '_') {
+                               p++;
+                               if (*p)
+                                       *q++ = *p++;
+                       } else {
+                               *q++ = tolower(*p++);
+                       }
                }
+               *q = '\0';
        }
+}
+
+static int parse_msg_id(const char *text)
+{
+       int i;
+
+       prepare_msg_ids();
 
        for (i = 0; i < FSCK_MSG_MAX; i++)
                if (!strcmp(text, msg_id_info[i].downcased))
@@ -125,6 +150,16 @@ static int parse_msg_id(const char *text)
        return -1;
 }
 
+void list_config_fsck_msg_ids(struct string_list *list, const char *prefix)
+{
+       int i;
+
+       prepare_msg_ids();
+
+       for (i = 0; i < FSCK_MSG_MAX; i++)
+               list_config_item(list, prefix, msg_id_info[i].camelcased);
+}
+
 static int fsck_msg_type(enum fsck_msg_id msg_id,
        struct fsck_options *options)
 {
@@ -761,7 +796,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
                buffer = p + 1;
                parent_line_count++;
        }
-       graft = lookup_commit_graft(&commit->object.oid);
+       graft = lookup_commit_graft(the_repository, &commit->object.oid);
        parent_count = commit_list_count(commit->parents);
        if (graft) {
                if (graft->nr_parent == -1 && !parent_count)
index 8d6d8b45ceb0138797eb7e54b15f7546925598ed..c4124acbe7802369ff64c12b48a513437c929a20 100755 (executable)
@@ -76,6 +76,23 @@ print_command_list () {
        echo "};"
 }
 
+print_config_list () {
+       cat <<EOF
+static const char *config_name_list[] = {
+EOF
+       grep '^[a-zA-Z].*\..*::$' Documentation/config.txt |
+       sed '/deprecated/d; s/::$//; s/,  */\n/g' |
+       sort |
+       while read line
+       do
+               echo "  \"$line\","
+       done
+       cat <<EOF
+       NULL,
+};
+EOF
+}
+
 echo "/* Automatically generated by generate-cmdlist.sh */
 struct cmdname_help {
        const char *name;
@@ -88,3 +105,5 @@ echo
 define_category_names "$1"
 echo
 print_command_list "$1"
+echo
+print_config_list
index 36f38ced902e1902a626933bdcda09819acf39a7..20eb81cc92f947d872b31a179d43d97772ff25e4 100755 (executable)
@@ -1062,7 +1062,7 @@ sub recount_edited_hunk {
                        $o_cnt++;
                } elsif ($mode eq '+') {
                        $n_cnt++;
-               } elsif ($mode eq ' ') {
+               } elsif ($mode eq ' ' or $mode eq "\n") {
                        $o_cnt++;
                        $n_cnt++;
                }
index 0354d4df5cadd5145245d755a8868608b849719f..b449db1cc9020ad1d7a5e66418d6c97f088419b2 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
 import ctypes
 import errno
 
+# support basestring in python3
+try:
+    unicode = unicode
+except NameError:
+    # 'unicode' is undefined, must be Python 3
+    str = str
+    unicode = str
+    bytes = bytes
+    basestring = (str,bytes)
+else:
+    # 'unicode' exists, must be Python 2
+    str = str
+    unicode = unicode
+    bytes = str
+    basestring = basestring
+
 try:
     from subprocess import CalledProcessError
 except ImportError:
@@ -767,7 +783,7 @@ def gitDeleteRef(ref):
 _gitConfig = {}
 
 def gitConfig(key, typeSpecifier=None):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         cmd = [ "git", "config" ]
         if typeSpecifier:
             cmd += [ typeSpecifier ]
@@ -781,12 +797,12 @@ def gitConfigBool(key):
        variable is set to true, and False if set to false or not present
        in the config."""
 
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         _gitConfig[key] = gitConfig(key, '--bool') == "true"
     return _gitConfig[key]
 
 def gitConfigInt(key):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         cmd = [ "git", "config", "--int", key ]
         s = read_pipe(cmd, ignore_error=True)
         v = s.strip()
@@ -797,7 +813,7 @@ def gitConfigInt(key):
     return _gitConfig[key]
 
 def gitConfigList(key):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
         _gitConfig[key] = s.strip().splitlines()
         if _gitConfig[key] == ['']:
@@ -855,7 +871,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
         tip = branches[branch]
         log = extractLogMessageFromGitCommit(tip)
         settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
+        if "depot-paths" in settings:
             paths = ",".join(settings["depot-paths"])
             branchByDepotPath[paths] = "remotes/p4/" + branch
 
@@ -865,9 +881,9 @@ def findUpstreamBranchPoint(head = "HEAD"):
         commit = head + "~%s" % parent
         log = extractLogMessageFromGitCommit(commit)
         settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
+        if "depot-paths" in settings:
             paths = ",".join(settings["depot-paths"])
-            if branchByDepotPath.has_key(paths):
+            if paths in branchByDepotPath:
                 return [branchByDepotPath[paths], settings]
 
         parent = parent + 1
@@ -876,7 +892,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
 
 def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
     if not silent:
-        print ("Creating/updating branch(es) in %s based on origin branch(es)"
+        print("Creating/updating branch(es) in %s based on origin branch(es)"
                % localRefPrefix)
 
     originPrefix = "origin/p4/"
@@ -891,29 +907,29 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
         originHead = line
 
         original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
-        if (not original.has_key('depot-paths')
-            or not original.has_key('change')):
+        if ('depot-paths' not in original
+            or 'change' not in original):
             continue
 
         update = False
         if not gitBranchExists(remoteHead):
             if verbose:
-                print "creating %s" % remoteHead
+                print("creating %s" % remoteHead)
             update = True
         else:
             settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
-            if settings.has_key('change') > 0:
+            if 'change' in settings:
                 if settings['depot-paths'] == original['depot-paths']:
                     originP4Change = int(original['change'])
                     p4Change = int(settings['change'])
                     if originP4Change > p4Change:
-                        print ("%s (%s) is newer than %s (%s). "
+                        print("%s (%s) is newer than %s (%s). "
                                "Updating p4 branch from origin."
                                % (originHead, originP4Change,
                                   remoteHead, p4Change))
                         update = True
                 else:
-                    print ("Ignoring: %s was imported from %s while "
+                    print("Ignoring: %s was imported from %s while "
                            "%s was imported from %s"
                            % (originHead, ','.join(original['depot-paths']),
                               remoteHead, ','.join(settings['depot-paths'])))
@@ -1002,7 +1018,7 @@ def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
 
         # Insert changes in chronological order
         for entry in reversed(result):
-            if not entry.has_key('change'):
+            if 'change' not in entry:
                 continue
             changes.add(int(entry['change']))
 
@@ -1312,7 +1328,7 @@ def p4UserId(self):
 
         results = p4CmdList("user -o")
         for r in results:
-            if r.has_key('User'):
+            if 'User' in r:
                 self.myP4UserId = r['User']
                 return r['User']
         die("Could not find your p4 user id")
@@ -1336,7 +1352,7 @@ def getUserMapFromPerforceServer(self):
         self.emails = {}
 
         for output in p4CmdList("users"):
-            if not output.has_key("User"):
+            if "User" not in output:
                 continue
             self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
             self.emails[output["Email"]] = output["User"]
@@ -1381,9 +1397,9 @@ def __init__(self):
     def run(self, args):
         j = 0
         for output in p4CmdList(args):
-            print 'Element: %d' % j
+            print('Element: %d' % j)
             j += 1
-            print output
+            print(output)
         return True
 
 class P4RollBack(Command):
@@ -1424,14 +1440,14 @@ def run(self, args):
 
                 if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
                                                            for p in depotPaths]))) == 0:
-                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+                    print("Branch %s did not exist at change %s, deleting." % (ref, maxChange))
                     system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
                     continue
 
                 while change and int(change) > maxChange:
                     changed = True
                     if self.verbose:
-                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+                        print("%s is at %s ; rewinding towards %s" % (ref, change, maxChange))
                     system("git update-ref %s \"%s^\"" % (ref, ref))
                     log = extractLogMessageFromGitCommit(ref)
                     settings =  extractSettingsGitLog(log)
@@ -1441,7 +1457,7 @@ def run(self, args):
                     change = settings['change']
 
                 if changed:
-                    print "%s rewound to %s" % (ref, change)
+                    print("%s rewound to %s" % (ref, change))
 
         return True
 
@@ -1577,10 +1593,10 @@ def patchRCSKeywords(self, file, pattern):
         except:
             # cleanup our temporary file
             os.unlink(outFileName)
-            print "Failed to strip RCS keywords in %s" % file
+            print("Failed to strip RCS keywords in %s" % file)
             raise
 
-        print "Patched up RCS keywords in %s" % file
+        print("Patched up RCS keywords in %s" % file)
 
     def p4UserForCommit(self,id):
         # Return the tuple (perforce user,git email) for a given git commit id
@@ -1588,7 +1604,7 @@ def p4UserForCommit(self,id):
         gitEmail = read_pipe(["git", "log", "--max-count=1",
                               "--format=%ae", id])
         gitEmail = gitEmail.strip()
-        if not self.emails.has_key(gitEmail):
+        if gitEmail not in self.emails:
             return (None,gitEmail)
         else:
             return (self.emails[gitEmail],gitEmail)
@@ -1600,7 +1616,7 @@ def checkValidP4Users(self,commits):
             if not user:
                 msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
                 if gitConfigBool("git-p4.allowMissingP4Users"):
-                    print "%s" % msg
+                    print("%s" % msg)
                 else:
                     die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
 
@@ -1612,14 +1628,14 @@ def lastP4Changelist(self):
         results = p4CmdList("client -o")        # find the current client
         client = None
         for r in results:
-            if r.has_key('Client'):
+            if 'Client' in r:
                 client = r['Client']
                 break
         if not client:
             die("could not get client spec")
         results = p4CmdList(["changes", "-c", client, "-m", "1"])
         for r in results:
-            if r.has_key('change'):
+            if 'change' in r:
                 return r['change']
         die("Could not get changelist number for last submit - cannot patch up user details")
 
@@ -1637,10 +1653,10 @@ def modifyChangelistUser(self, changelist, newUser):
 
         result = p4CmdList("change -f -i", stdin=input)
         for r in result:
-            if r.has_key('code'):
+            if 'code' in r:
                 if r['code'] == 'error':
                     die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
-            if r.has_key('data'):
+            if 'data' in r:
                 print("Updated user field for changelist %s to %s" % (changelist, newUser))
                 return
         die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
@@ -1650,7 +1666,7 @@ def canChangeChangelists(self):
         # which are required to modify changelists.
         results = p4CmdList(["protects", self.depotPath])
         for r in results:
-            if r.has_key('perm'):
+            if 'perm' in r:
                 if r['perm'] == 'admin':
                     return 1
                 if r['perm'] == 'super':
@@ -1690,7 +1706,7 @@ def prepareSubmitTemplate(self, changelist=None):
         if changelist:
             args.append(str(changelist))
         for entry in p4CmdList(args):
-            if not entry.has_key('code'):
+            if 'code' not in entry:
                 continue
             if entry['code'] == 'stat':
                 change_entry = entry
@@ -1699,7 +1715,7 @@ def prepareSubmitTemplate(self, changelist=None):
             die('Failed to decode output of p4 change -o')
         for key, value in change_entry.iteritems():
             if key.startswith('File'):
-                if settings.has_key('depot-paths'):
+                if 'depot-paths' in settings:
                     if not [p for p in settings['depot-paths']
                             if p4PathStartsWith(value, p)]:
                         continue
@@ -1710,7 +1726,7 @@ def prepareSubmitTemplate(self, changelist=None):
                 continue
         # Output in the order expected by prepareLogMessage
         for key in ['Change', 'Client', 'User', 'Status', 'Description', 'Jobs']:
-            if not change_entry.has_key(key):
+            if key not in change_entry:
                 continue
             template += '\n'
             template += key + ':'
@@ -1738,7 +1754,7 @@ def edit_template(self, template_file):
         mtime = os.stat(template_file).st_mtime
 
         # invoke the editor
-        if os.environ.has_key("P4EDITOR") and (os.environ.get("P4EDITOR") != ""):
+        if "P4EDITOR" in os.environ and (os.environ.get("P4EDITOR") != ""):
             editor = os.environ.get("P4EDITOR")
         else:
             editor = read_pipe("git var GIT_EDITOR").strip()
@@ -1762,7 +1778,7 @@ def edit_template(self, template_file):
 
     def get_diff_description(self, editedFiles, filesToAdd, symlinks):
         # diff
-        if os.environ.has_key("P4DIFF"):
+        if "P4DIFF" in os.environ:
             del(os.environ["P4DIFF"])
         diff = ""
         for editedFile in editedFiles:
@@ -1792,8 +1808,8 @@ def get_diff_description(self, editedFiles, filesToAdd, symlinks):
     def applyCommit(self, id):
         """Apply one commit, return True if it succeeded."""
 
-        print "Applying", read_pipe(["git", "show", "-s",
-                                     "--format=format:%h %s", id])
+        print("Applying", read_pipe(["git", "show", "-s",
+                                     "--format=format:%h %s", id]))
 
         (p4User, gitEmail) = self.p4UserForCommit(id)
 
@@ -1825,7 +1841,7 @@ def applyCommit(self, id):
                     filesToDelete.remove(path)
 
                 dst_mode = int(diff['dst_mode'], 8)
-                if dst_mode == 0120000:
+                if dst_mode == 0o120000:
                     symlinks.add(path)
 
             elif modifier == "D":
@@ -1883,7 +1899,7 @@ def applyCommit(self, id):
         if os.system(tryPatchCmd) != 0:
             fixed_rcs_keywords = False
             patch_succeeded = False
-            print "Unfortunately applying the change failed!"
+            print("Unfortunately applying the change failed!")
 
             # Patch failed, maybe it's just RCS keyword woes. Look through
             # the patch to see if that's possible.
@@ -1901,13 +1917,13 @@ def applyCommit(self, id):
                         for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
                             if regexp.search(line):
                                 if verbose:
-                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
+                                    print("got keyword match on %s in %s in %s" % (pattern, line, file))
                                 kwfiles[file] = pattern
                                 break
 
                 for file in kwfiles:
                     if verbose:
-                        print "zapping %s with %s" % (line,pattern)
+                        print("zapping %s with %s" % (line,pattern))
                     # File is being deleted, so not open in p4.  Must
                     # disable the read-only bit on windows.
                     if self.isWindows and file not in editedFiles:
@@ -1916,7 +1932,7 @@ def applyCommit(self, id):
                     fixed_rcs_keywords = True
 
             if fixed_rcs_keywords:
-                print "Retrying the patch with RCS keywords cleaned up"
+                print("Retrying the patch with RCS keywords cleaned up")
                 if os.system(tryPatchCmd) == 0:
                     patch_succeeded = True
 
@@ -1984,34 +2000,34 @@ def applyCommit(self, id):
             # Leave the p4 tree prepared, and the submit template around
             # and let the user decide what to do next
             #
-            print
-            print "P4 workspace prepared for submission."
-            print "To submit or revert, go to client workspace"
-            print "  " + self.clientPath
-            print
-            print "To submit, use \"p4 submit\" to write a new description,"
-            print "or \"p4 submit -i <%s\" to use the one prepared by" \
-                  " \"git p4\"." % fileName
-            print "You can delete the file \"%s\" when finished." % fileName
+            print()
+            print("P4 workspace prepared for submission.")
+            print("To submit or revert, go to client workspace")
+            print("  " + self.clientPath)
+            print()
+            print("To submit, use \"p4 submit\" to write a new description,")
+            print("or \"p4 submit -i <%s\" to use the one prepared by" \
+                  " \"git p4\"." % fileName)
+            print("You can delete the file \"%s\" when finished." % fileName)
 
             if self.preserveUser and p4User and not self.p4UserIsMe(p4User):
-                print "To preserve change ownership by user %s, you must\n" \
+                print("To preserve change ownership by user %s, you must\n" \
                       "do \"p4 change -f <change>\" after submitting and\n" \
-                      "edit the User field."
+                      "edit the User field.")
             if pureRenameCopy:
-                print "After submitting, renamed files must be re-synced."
-                print "Invoke \"p4 sync -f\" on each of these files:"
+                print("After submitting, renamed files must be re-synced.")
+                print("Invoke \"p4 sync -f\" on each of these files:")
                 for f in pureRenameCopy:
-                    print "  " + f
+                    print("  " + f)
 
-            print
-            print "To revert the changes, use \"p4 revert ...\", and delete"
-            print "the submit template file \"%s\"" % fileName
+            print()
+            print("To revert the changes, use \"p4 revert ...\", and delete")
+            print("the submit template file \"%s\"" % fileName)
             if filesToAdd:
-                print "Since the commit adds new files, they must be deleted:"
+                print("Since the commit adds new files, they must be deleted:")
                 for f in filesToAdd:
-                    print "  " + f
-            print
+                    print("  " + f)
+            print()
             return True
 
         #
@@ -2078,17 +2094,17 @@ def exportGitTags(self, gitTags):
 
             if not m.match(name):
                 if verbose:
-                    print "tag %s does not match regexp %s" % (name, validLabelRegexp)
+                    print("tag %s does not match regexp %s" % (name, validLabelRegexp))
                 continue
 
             # Get the p4 commit this corresponds to
             logMessage = extractLogMessageFromGitCommit(name)
             values = extractSettingsGitLog(logMessage)
 
-            if not values.has_key('change'):
+            if 'change' not in values:
                 # a tag pointing to something not sent to p4; ignore
                 if verbose:
-                    print "git tag %s does not give a p4 commit" % name
+                    print("git tag %s does not give a p4 commit" % name)
                 continue
             else:
                 changelist = values['change']
@@ -2123,10 +2139,10 @@ def exportGitTags(self, gitTags):
                 labelTemplate += "\t%s\n" % depot_side
 
             if self.dry_run:
-                print "Would create p4 label %s for tag" % name
+                print("Would create p4 label %s for tag" % name)
             elif self.prepare_p4_only:
-                print "Not creating p4 label %s for tag due to option" \
-                      " --prepare-p4-only" % name
+                print("Not creating p4 label %s for tag due to option" \
+                      " --prepare-p4-only" % name)
             else:
                 p4_write_pipe(["label", "-i"], labelTemplate)
 
@@ -2135,7 +2151,7 @@ def exportGitTags(self, gitTags):
                           ["%s@%s" % (depot_side, changelist) for depot_side in clientSpec.mappings])
 
                 if verbose:
-                    print "created p4 label for tag %s" % name
+                    print("created p4 label for tag %s" % name)
 
     def run(self, args):
         if len(args) == 0:
@@ -2179,10 +2195,10 @@ def run(self, args):
             self.conflict_behavior = val
 
         if self.verbose:
-            print "Origin branch is " + self.origin
+            print("Origin branch is " + self.origin)
 
         if len(self.depotPath) == 0:
-            print "Internal error: cannot locate perforce depot path from existing branches"
+            print("Internal error: cannot locate perforce depot path from existing branches")
             sys.exit(128)
 
         self.useClientSpec = False
@@ -2203,7 +2219,7 @@ def run(self, args):
         if self.clientPath == "":
             die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
 
-        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
+        print("Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath))
         self.oldWorkingDirectory = os.getcwd()
 
         # ensure the clientPath exists
@@ -2214,9 +2230,9 @@ def run(self, args):
 
         chdir(self.clientPath, is_client_path=True)
         if self.dry_run:
-            print "Would synchronize p4 checkout in %s" % self.clientPath
+            print("Would synchronize p4 checkout in %s" % self.clientPath)
         else:
-            print "Synchronizing p4 checkout..."
+            print("Synchronizing p4 checkout...")
             if new_client_dir:
                 # old one was destroyed, and maybe nobody told p4
                 p4_sync("...", "-f")
@@ -2292,13 +2308,13 @@ def run(self, args):
         # continue to try the rest of the patches, or quit.
         #
         if self.dry_run:
-            print "Would apply"
+            print("Would apply")
         applied = []
         last = len(commits) - 1
         for i, commit in enumerate(commits):
             if self.dry_run:
-                print " ", read_pipe(["git", "show", "-s",
-                                      "--format=format:%h %s", commit])
+                print(" ", read_pipe(["git", "show", "-s",
+                                      "--format=format:%h %s", commit]))
                 ok = True
             else:
                 ok = self.applyCommit(commit)
@@ -2306,15 +2322,15 @@ def run(self, args):
                 applied.append(commit)
             else:
                 if self.prepare_p4_only and i < last:
-                    print "Processing only the first commit due to option" \
-                          " --prepare-p4-only"
+                    print("Processing only the first commit due to option" \
+                          " --prepare-p4-only")
                     break
                 if i < last:
                     quit = False
                     while True:
                         # prompt for what to do, or use the option/variable
                         if self.conflict_behavior == "ask":
-                            print "What do you want to do?"
+                            print("What do you want to do?")
                             response = raw_input("[s]kip this commit but apply"
                                                  " the rest, or [q]uit? ")
                             if not response:
@@ -2328,10 +2344,10 @@ def run(self, args):
                                 self.conflict_behavior)
 
                         if response[0] == "s":
-                            print "Skipping this commit, but applying the rest"
+                            print("Skipping this commit, but applying the rest")
                             break
                         if response[0] == "q":
-                            print "Quitting"
+                            print("Quitting")
                             quit = True
                             break
                     if quit:
@@ -2344,7 +2360,7 @@ def run(self, args):
         elif self.prepare_p4_only:
             pass
         elif len(commits) == len(applied):
-            print ("All commits {0}!".format(shelved_applied))
+            print("All commits {0}!".format(shelved_applied))
 
             sync = P4Sync()
             if self.branch:
@@ -2360,17 +2376,17 @@ def run(self, args):
 
         else:
             if len(applied) == 0:
-                print ("No commits {0}.".format(shelved_applied))
+                print("No commits {0}.".format(shelved_applied))
             else:
-                print ("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
+                print("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
                 for c in commits:
                     if c in applied:
                         star = "*"
                     else:
                         star = " "
-                    print star, read_pipe(["git", "show", "-s",
-                                           "--format=format:%h %s",  c])
-                print "You will have to do 'git p4 sync' and rebase."
+                    print(star, read_pipe(["git", "show", "-s",
+                                           "--format=format:%h %s",  c]))
+                print("You will have to do 'git p4 sync' and rebase.")
 
         if gitConfigBool("git-p4.exportLabels"):
             self.exportLabels = True
@@ -2580,7 +2596,7 @@ def checkpoint(self):
         self.gitStream.write("progress checkpoint\n\n")
         out = self.gitOutput.readline()
         if self.verbose:
-            print "checkpoint finished: " + out
+            print("checkpoint finished: " + out)
 
     def cmp_shelved(self, path, filerev, revision):
         """ Determine if a path at revision #filerev is the same as the file
@@ -2600,7 +2616,7 @@ def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_r
                              for path in self.cloneExclude]
         files = []
         fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
+        while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
 
             if [p for p in self.cloneExclude
@@ -2638,7 +2654,7 @@ def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_r
     def extractJobsFromCommit(self, commit):
         jobs = []
         jnum = 0
-        while commit.has_key("job%s" % jnum):
+        while "job%s" % jnum in commit:
             job = commit["job%s" % jnum]
             jobs.append(job)
             jnum = jnum + 1
@@ -2686,7 +2702,7 @@ def splitFilesIntoBranches(self, commit):
 
         branches = {}
         fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
+        while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
             found = [p for p in self.depotPaths
                      if p4PathStartsWith(path, p)]
@@ -2735,7 +2751,7 @@ def encodeWithUTF8(self, path):
                 encoding = gitConfig('git-p4.pathEncoding')
             path = path.decode(encoding, 'replace').encode('utf8', 'replace')
             if self.verbose:
-                print 'Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, path)
+                print('Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, path))
         return path
 
     # output one file from the P4 stream
@@ -2764,7 +2780,7 @@ def streamOneP4File(self, file, contents):
                 # to nothing.  This causes p4 errors when checking out such
                 # a change, and errors here too.  Work around it by ignoring
                 # the bad symlink; hopefully a future change fixes it.
-                print "\nIgnoring empty symlink in %s" % file['depotFile']
+                print("\nIgnoring empty symlink in %s" % file['depotFile'])
                 return
             elif data[-1] == '\n':
                 contents = [data[:-1]]
@@ -2804,7 +2820,7 @@ def streamOneP4File(self, file, contents):
             # Ideally, someday, this script can learn how to generate
             # appledouble files directly and import those to git, but
             # non-mac machines can never find a use for apple filetype.
-            print "\nIgnoring apple filetype file %s" % file['depotFile']
+            print("\nIgnoring apple filetype file %s" % file['depotFile'])
             return
 
         # Note that we do not try to de-mangle keywords on utf16 files,
@@ -2866,7 +2882,7 @@ def streamP4FilesCb(self, marshalled):
             else:
                 die("Error from p4 print: %s" % err)
 
-        if marshalled.has_key('depotFile') and self.stream_have_file_info:
+        if 'depotFile' in marshalled and self.stream_have_file_info:
             # start of a new file - output the old one first
             self.streamOneP4File(self.stream_file, self.stream_contents)
             self.stream_file = {}
@@ -2938,7 +2954,7 @@ def streamP4FilesCbSelf(entry):
                       cb=streamP4FilesCbSelf)
 
             # do the last chunk
-            if self.stream_file.has_key('depotFile'):
+            if 'depotFile' in self.stream_file:
                 self.streamOneP4File(self.stream_file, self.stream_contents)
 
     def make_email(self, userid):
@@ -2953,11 +2969,11 @@ def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
         """
 
         if verbose:
-            print "writing tag %s for commit %s" % (labelName, commit)
+            print("writing tag %s for commit %s" % (labelName, commit))
         gitStream.write("tag %s\n" % labelName)
         gitStream.write("from %s\n" % commit)
 
-        if labelDetails.has_key('Owner'):
+        if 'Owner' in labelDetails:
             owner = labelDetails["Owner"]
         else:
             owner = None
@@ -2972,8 +2988,8 @@ def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
 
         gitStream.write("tagger %s\n" % tagger)
 
-        print "labelDetails=",labelDetails
-        if labelDetails.has_key('Description'):
+        print("labelDetails=",labelDetails)
+        if 'Description' in labelDetails:
             description = labelDetails['Description']
         else:
             description = 'Label from git p4'
@@ -3044,7 +3060,7 @@ def commit(self, details, files, branch, parent = ""):
 
         if len(parent) > 0:
             if self.verbose:
-                print "parent %s" % parent
+                print("parent %s" % parent)
             self.gitStream.write("from %s\n" % parent)
 
         self.streamP4Files(files)
@@ -3052,12 +3068,12 @@ def commit(self, details, files, branch, parent = ""):
 
         change = int(details["change"])
 
-        if self.labels.has_key(change):
+        if change in self.labels:
             label = self.labels[change]
             labelDetails = label[0]
             labelRevisions = label[1]
             if self.verbose:
-                print "Change %s is labelled %s" % (change, labelDetails)
+                print("Change %s is labelled %s" % (change, labelDetails))
 
             files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
                                                 for p in self.branchPrefixes])
@@ -3075,12 +3091,12 @@ def commit(self, details, files, branch, parent = ""):
 
                 else:
                     if not self.silent:
-                        print ("Tag %s does not match with change %s: files do not match."
+                        print("Tag %s does not match with change %s: files do not match."
                                % (labelDetails["label"], change))
 
             else:
                 if not self.silent:
-                    print ("Tag %s does not match with change %s: file count is different."
+                    print("Tag %s does not match with change %s: file count is different."
                            % (labelDetails["label"], change))
 
     # Build a dictionary of changelists and labels, for "detect-labels" option.
@@ -3089,14 +3105,14 @@ def getLabels(self):
 
         l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
         if len(l) > 0 and not self.silent:
-            print "Finding files belonging to labels in %s" % `self.depotPaths`
+            print("Finding files belonging to labels in %s" % self.depotPaths)
 
         for output in l:
             label = output["label"]
             revisions = {}
             newestChange = 0
             if self.verbose:
-                print "Querying files for label %s" % label
+                print("Querying files for label %s" % label)
             for file in p4CmdList(["files"] +
                                       ["%s...@%s" % (p, label)
                                           for p in self.depotPaths]):
@@ -3108,7 +3124,7 @@ def getLabels(self):
             self.labels[newestChange] = [output, revisions]
 
         if self.verbose:
-            print "Label changes: %s" % self.labels.keys()
+            print("Label changes: %s" % self.labels.keys())
 
     # Import p4 labels as git tags. A direct mapping does not
     # exist, so assume that if all the files are at the same revision
@@ -3116,7 +3132,7 @@ def getLabels(self):
     # just ignore.
     def importP4Labels(self, stream, p4Labels):
         if verbose:
-            print "import p4 labels: " + ' '.join(p4Labels)
+            print("import p4 labels: " + ' '.join(p4Labels))
 
         ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
         validLabelRegexp = gitConfig("git-p4.labelImportRegexp")
@@ -3129,7 +3145,7 @@ def importP4Labels(self, stream, p4Labels):
 
             if not m.match(name):
                 if verbose:
-                    print "label %s does not match regexp %s" % (name,validLabelRegexp)
+                    print("label %s does not match regexp %s" % (name,validLabelRegexp))
                 continue
 
             if name in ignoredP4Labels:
@@ -3141,7 +3157,7 @@ def importP4Labels(self, stream, p4Labels):
             change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
                                 for p in self.depotPaths])
 
-            if change.has_key('change'):
+            if 'change' in change:
                 # find the corresponding git commit; take the oldest commit
                 changelist = int(change['change'])
                 if changelist in self.committedChanges:
@@ -3151,7 +3167,7 @@ def importP4Labels(self, stream, p4Labels):
                     gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
                         "--reverse", ":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
                     if len(gitCommit) == 0:
-                        print "importing label %s: could not find git commit for changelist %d" % (name, changelist)
+                        print("importing label %s: could not find git commit for changelist %d" % (name, changelist))
                     else:
                         commitFound = True
                         gitCommit = gitCommit.strip()
@@ -3161,16 +3177,16 @@ def importP4Labels(self, stream, p4Labels):
                     try:
                         tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
                     except ValueError:
-                        print "Could not convert label time %s" % labelDetails['Update']
+                        print("Could not convert label time %s" % labelDetails['Update'])
                         tmwhen = 1
 
                     when = int(time.mktime(tmwhen))
                     self.streamTag(stream, name, labelDetails, gitCommit, when)
                     if verbose:
-                        print "p4 label %s mapped to git commit %s" % (name, gitCommit)
+                        print("p4 label %s mapped to git commit %s" % (name, gitCommit))
             else:
                 if verbose:
-                    print "Label %s has no changelists - possibly deleted?" % name
+                    print("Label %s has no changelists - possibly deleted?" % name)
 
             if not commitFound:
                 # We can't import this label; don't try again as it will get very
@@ -3200,7 +3216,7 @@ def getBranchMapping(self):
         for info in p4CmdList(command):
             details = p4Cmd(["branch", "-o", info["branch"]])
             viewIdx = 0
-            while details.has_key("View%s" % viewIdx):
+            while "View%s" % viewIdx in details:
                 paths = details["View%s" % viewIdx].split(" ")
                 viewIdx = viewIdx + 1
                 # require standard //depot/foo/... //depot/bar/... mapping
@@ -3215,8 +3231,8 @@ def getBranchMapping(self):
 
                     if destination in self.knownBranches:
                         if not self.silent:
-                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
-                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+                            print("p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination))
+                            print("but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination))
                         continue
 
                     self.knownBranches[destination] = source
@@ -3266,7 +3282,7 @@ def updateOptionDict(self, d):
         d["options"] = ' '.join(sorted(option_keys.keys()))
 
     def readOptions(self, d):
-        self.keepRepoPath = (d.has_key('options')
+        self.keepRepoPath = ('options' in d
                              and ('keepRepoPath' in d['options']))
 
     def gitRefForBranch(self, branch):
@@ -3280,28 +3296,28 @@ def gitRefForBranch(self, branch):
 
     def gitCommitByP4Change(self, ref, change):
         if self.verbose:
-            print "looking in ref " + ref + " for change %s using bisect..." % change
+            print("looking in ref " + ref + " for change %s using bisect..." % change)
 
         earliestCommit = ""
         latestCommit = parseRevision(ref)
 
         while True:
             if self.verbose:
-                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+                print("trying: earliest %s latest %s" % (earliestCommit, latestCommit))
             next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
             if len(next) == 0:
                 if self.verbose:
-                    print "argh"
+                    print("argh")
                 return ""
             log = extractLogMessageFromGitCommit(next)
             settings = extractSettingsGitLog(log)
             currentChange = int(settings['change'])
             if self.verbose:
-                print "current change %s" % currentChange
+                print("current change %s" % currentChange)
 
             if currentChange == change:
                 if self.verbose:
-                    print "found %s" % next
+                    print("found %s" % next)
                 return next
 
             if currentChange < change:
@@ -3347,7 +3363,7 @@ def searchParent(self, parent, branch, target):
             if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
                 parentFound = True
                 if self.verbose:
-                    print "Found parent of %s in commit %s" % (branch, blob)
+                    print("Found parent of %s in commit %s" % (branch, blob))
                 break
         if parentFound:
             return blob
@@ -3378,7 +3394,7 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
                         filesForCommit = branches[branch]
 
                         if self.verbose:
-                            print "branch is %s" % branch
+                            print("branch is %s" % branch)
 
                         self.updatedBranches.add(branch)
 
@@ -3399,13 +3415,13 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
                                         print("\n    Resuming with change %s" % change);
 
                                 if self.verbose:
-                                    print "parent determined through known branches: %s" % parent
+                                    print("parent determined through known branches: %s" % parent)
 
                         branch = self.gitRefForBranch(branch)
                         parent = self.gitRefForBranch(parent)
 
                         if self.verbose:
-                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+                            print("looking for initial parent for %s; current parent is %s" % (branch, parent))
 
                         if len(parent) == 0 and branch in self.initialParents:
                             parent = self.initialParents[branch]
@@ -3415,7 +3431,7 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
                         if len(parent) > 0:
                             tempBranch = "%s/%d" % (self.tempBranchLocation, change)
                             if self.verbose:
-                                print "Creating temporary branch: " + tempBranch
+                                print("Creating temporary branch: " + tempBranch)
                             self.commit(description, filesForCommit, tempBranch)
                             self.tempBranches.append(tempBranch)
                             self.checkpoint()
@@ -3424,7 +3440,7 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
                             self.commit(description, filesForCommit, branch, blob)
                         else:
                             if self.verbose:
-                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
+                                print("Parent of %s not found. Committing into head of %s" % (branch, parent))
                             self.commit(description, filesForCommit, branch, parent)
                 else:
                     files = self.extractFilesFromCommit(description, shelved, change, origin_revision)
@@ -3433,7 +3449,7 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
                     # only needed once, to connect to the previous commit
                     self.initialParent = ""
             except IOError:
-                print self.gitError.read()
+                print(self.gitError.read())
                 sys.exit(1)
 
     def sync_origin_only(self):
@@ -3441,11 +3457,11 @@ def sync_origin_only(self):
             self.hasOrigin = originP4BranchesExist()
             if self.hasOrigin:
                 if not self.silent:
-                    print 'Syncing with origin first, using "git fetch origin"'
+                    print('Syncing with origin first, using "git fetch origin"')
                 system("git fetch origin")
 
     def importHeadRevision(self, revision):
-        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+        print("Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch))
 
         details = {}
         details["user"] = "git perforce import user"
@@ -3497,8 +3513,8 @@ def importHeadRevision(self, revision):
         try:
             self.commit(details, self.extractFilesFromCommit(details), self.branch)
         except IOError:
-            print "IO error with git fast-import. Is your git version recent enough?"
-            print self.gitError.read()
+            print("IO error with git fast-import. Is your git version recent enough?")
+            print(self.gitError.read())
 
     def openStreams(self):
         self.importProcess = subprocess.Popen(["git", "fast-import"],
@@ -3560,14 +3576,14 @@ def run(self, args):
 
             if len(self.p4BranchesInGit) > 1:
                 if not self.silent:
-                    print "Importing from/into multiple branches"
+                    print("Importing from/into multiple branches")
                 self.detectBranches = True
                 for branch in branches.keys():
                     self.initialParents[self.refPrefix + branch] = \
                         branches[branch]
 
             if self.verbose:
-                print "branches: %s" % self.p4BranchesInGit
+                print("branches: %s" % self.p4BranchesInGit)
 
             p4Change = 0
             for branch in self.p4BranchesInGit:
@@ -3576,8 +3592,8 @@ def run(self, args):
                 settings = extractSettingsGitLog(logMsg)
 
                 self.readOptions(settings)
-                if (settings.has_key('depot-paths')
-                    and settings.has_key ('change')):
+                if ('depot-paths' in settings
+                    and 'change' in settings):
                     change = int(settings['change']) + 1
                     p4Change = max(p4Change, change)
 
@@ -3590,7 +3606,7 @@ def run(self, args):
                             prev_list = prev.split("/")
                             cur_list = cur.split("/")
                             for i in range(0, min(len(cur_list), len(prev_list))):
-                                if cur_list[i] <> prev_list[i]:
+                                if cur_list[i] != prev_list[i]:
                                     i = i - 1
                                     break
 
@@ -3602,7 +3618,7 @@ def run(self, args):
                 self.depotPaths = sorted(self.previousDepotPaths)
                 self.changeRange = "@%s,#head" % p4Change
                 if not self.silent and not self.detectBranches:
-                    print "Performing incremental import into %s git branch" % self.branch
+                    print("Performing incremental import into %s git branch" % self.branch)
 
         # accept multiple ref name abbreviations:
         #    refs/foo/bar/branch -> use it exactly
@@ -3619,10 +3635,10 @@ def run(self, args):
 
         if len(args) == 0 and self.depotPaths:
             if not self.silent:
-                print "Depot paths: %s" % ' '.join(self.depotPaths)
+                print("Depot paths: %s" % ' '.join(self.depotPaths))
         else:
             if self.depotPaths and self.depotPaths != args:
-                print ("previous import used depot path %s and now %s was specified. "
+                print("previous import used depot path %s and now %s was specified. "
                        "This doesn't work!" % (' '.join (self.depotPaths),
                                                ' '.join (args)))
                 sys.exit(1)
@@ -3689,8 +3705,8 @@ def run(self, args):
             else:
                 self.getBranchMapping()
             if self.verbose:
-                print "p4-git branches: %s" % self.p4BranchesInGit
-                print "initial parents: %s" % self.initialParents
+                print("p4-git branches: %s" % self.p4BranchesInGit)
+                print("initial parents: %s" % self.initialParents)
             for b in self.p4BranchesInGit:
                 if b != "master":
 
@@ -3734,8 +3750,8 @@ def run(self, args):
                                     self.branch)
 
                 if self.verbose:
-                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
-                                                              self.changeRange)
+                    print("Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+                                                              self.changeRange))
                 changes = p4ChangesForPaths(self.depotPaths, self.changeRange, self.changes_block_size)
 
                 if len(self.maxChanges) > 0:
@@ -3743,10 +3759,10 @@ def run(self, args):
 
             if len(changes) == 0:
                 if not self.silent:
-                    print "No changes to import!"
+                    print("No changes to import!")
             else:
                 if not self.silent and not self.detectBranches:
-                    print "Import destination: %s" % self.branch
+                    print("Import destination: %s" % self.branch)
 
                 self.updatedBranches = set()
 
@@ -3761,7 +3777,7 @@ def run(self, args):
                 self.importChanges(changes)
 
                 if not self.silent:
-                    print ""
+                    print("")
                     if len(self.updatedBranches) > 0:
                         sys.stdout.write("Updated branches: ")
                         for b in self.updatedBranches:
@@ -3825,7 +3841,7 @@ def rebase(self):
         # the branchpoint may be p4/foo~3, so strip off the parent
         upstream = re.sub("~[0-9]+$", "", upstream)
 
-        print "Rebasing the current branch onto %s" % upstream
+        print("Rebasing the current branch onto %s" % upstream)
         oldHead = read_pipe("git rev-parse HEAD").strip()
         system("git rebase %s" % upstream)
         system("git diff-tree --stat --summary -M %s HEAD --" % oldHead)
@@ -3879,7 +3895,7 @@ def run(self, args):
         if not self.cloneDestination:
             self.cloneDestination = self.defaultDestination(args)
 
-        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+        print("Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination))
 
         if not os.path.exists(self.cloneDestination):
             os.makedirs(self.cloneDestination)
@@ -3901,8 +3917,8 @@ def run(self, args):
             if not self.cloneBare:
                 system([ "git", "checkout", "-f" ])
         else:
-            print 'Not checking out any branch, use ' \
-                  '"git checkout -q -b master <branch>"'
+            print('Not checking out any branch, use ' \
+                  '"git checkout -q -b master <branch>"')
 
         # auto-set this variable if invoked with --use-client-spec
         if self.useClientSpec_from_options:
@@ -3950,7 +3966,7 @@ def findLastP4Revision(self, starting_point):
         for parent in (range(65535)):
             log = extractLogMessageFromGitCommit("{0}^{1}".format(starting_point, parent))
             settings = extractSettingsGitLog(log)
-            if settings.has_key('change'):
+            if 'change' in settings:
                 return settings
 
         sys.exit("could not find git-p4 commits in {0}".format(self.origin))
@@ -4018,7 +4034,7 @@ def run(self, args):
             log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
             settings = extractSettingsGitLog(log)
 
-            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+            print("%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]))
         return True
 
 class HelpFormatter(optparse.IndentedHelpFormatter):
@@ -4032,12 +4048,12 @@ def format_description(self, description):
             return ""
 
 def printUsage(commands):
-    print "usage: %s <command> [options]" % sys.argv[0]
-    print ""
-    print "valid commands: %s" % ", ".join(commands)
-    print ""
-    print "Try %s <command> --help for command specific help." % sys.argv[0]
-    print ""
+    print("usage: %s <command> [options]" % sys.argv[0])
+    print("")
+    print("valid commands: %s" % ", ".join(commands))
+    print("")
+    print("Try %s <command> --help for command specific help." % sys.argv[0])
+    print("")
 
 commands = {
     "debug" : P4Debug,
@@ -4062,8 +4078,8 @@ def main():
         klass = commands[cmdName]
         cmd = klass()
     except KeyError:
-        print "unknown command %s" % cmdName
-        print ""
+        print("unknown command %s" % cmdName)
+        print("")
         printUsage(commands.keys())
         sys.exit(2)
 
index 06a7b793075d7701b5dafa69b3576d13ba7c9de6..299ded21375ed3146052b9887dd2b56216404105 100644 (file)
 # file and written to the tail of $done.
 todo="$state_dir"/git-rebase-todo
 
-# The rebase command lines that have already been processed.  A line
-# is moved here when it is first handled, before any associated user
-# actions.
-done="$state_dir"/done
-
-# The commit message that is planned to be used for any changes that
-# need to be committed following a user interaction.
-msg="$state_dir"/message
-
-# The file into which is accumulated the suggested commit message for
-# squash/fixup commands.  When the first of a series of squash/fixups
-# is seen, the file is created and the commit message from the
-# previous commit and from the first squash/fixup commit are written
-# to it.  The commit message for each subsequent squash/fixup commit
-# is appended to the file as it is processed.
-#
-# The first line of the file is of the form
-#     # This is a combination of $count commits.
-# where $count is the number of commits whose messages have been
-# written to the file so far (including the initial "pick" commit).
-# Each time that a commit message is processed, this line is read and
-# updated.  It is deleted just before the combined commit is made.
-squash_msg="$state_dir"/message-squash
-
-# If the current series of squash/fixups has not yet included a squash
-# command, then this file exists and holds the commit message of the
-# original "pick" commit.  (If the series ends without a "squash"
-# command, then this can be used as the commit message of the combined
-# commit without opening the editor.)
-fixup_msg="$state_dir"/message-fixup
-
-# $rewritten is the name of a directory containing files for each
-# commit that is reachable by at least one merge base of $head and
-# $upstream. They are not necessarily rewritten, but their children
-# might be.  This ensures that commits on merged, but otherwise
-# unrelated side branches are left alone. (Think "X" in the man page's
-# example.)
-rewritten="$state_dir"/rewritten
-
-dropped="$state_dir"/dropped
-
-end="$state_dir"/end
-msgnum="$state_dir"/msgnum
-
-# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
-# GIT_AUTHOR_DATE that will be used for the commit that is currently
-# being rebased.
-author_script="$state_dir"/author-script
-
-# When an "edit" rebase command is being processed, the SHA1 of the
-# commit to be edited is recorded in this file.  When "git rebase
-# --continue" is executed, if there are any staged changes then they
-# will be amended to the HEAD commit, but only provided the HEAD
-# commit is still the commit to be edited.  When any other rebase
-# command is processed, this file is deleted.
-amend="$state_dir"/amend
-
-# For the post-rewrite hook, we make a list of rewritten commits and
-# their new sha1s.  The rewritten-pending list keeps the sha1s of
-# commits that have been processed, but not committed yet,
-# e.g. because they are waiting for a 'squash' command.
-rewritten_list="$state_dir"/rewritten-list
-rewritten_pending="$state_dir"/rewritten-pending
-
-# Work around Git for Windows' Bash whose "read" does not strip CRLF
-# and leaves CR at the end instead.
-cr=$(printf "\015")
-
-empty_tree=$(git hash-object -t tree /dev/null)
-
-strategy_args=${strategy:+--strategy=$strategy}
-test -n "$strategy_opts" &&
-eval '
-       for strategy_opt in '"$strategy_opts"'
-       do
-               strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
-       done
-'
-
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
@@ -107,15 +28,6 @@ case "$comment_char" in
        ;;
 esac
 
-warn () {
-       printf '%s\n' "$*" >&2
-}
-
-# Output the commit message for the specified commit.
-commit_message () {
-       git cat-file commit "$1" | sed "1,/^$/d"
-}
-
 orig_reflog_action="$GIT_REFLOG_ACTION"
 
 comment_for_reflog () {
@@ -127,33 +39,6 @@ comment_for_reflog () {
        esac
 }
 
-last_count=
-mark_action_done () {
-       sed -e 1q < "$todo" >> "$done"
-       sed -e 1d < "$todo" >> "$todo".new
-       mv -f "$todo".new "$todo"
-       new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
-       echo $new_count >"$msgnum"
-       total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
-       echo $total >"$end"
-       if test "$last_count" != "$new_count"
-       then
-               last_count=$new_count
-               eval_gettext "Rebasing (\$new_count/\$total)"; printf "\r"
-               test -z "$verbose" || echo
-       fi
-}
-
-# Put the last action marked done at the beginning of the todo list
-# again. If there has not been an action marked done yet, leave the list of
-# items on the todo list unchanged.
-reschedule_last_action () {
-       tail -n 1 "$done" | cat - "$todo" >"$todo".new
-       sed -e \$d <"$done" >"$done".new
-       mv -f "$todo".new "$todo"
-       mv -f "$done".new "$done"
-}
-
 append_todo_help () {
        gettext "
 Commands:
@@ -186,50 +71,6 @@ If you remove a line here THAT COMMIT WILL BE LOST.
        fi
 }
 
-make_patch () {
-       sha1_and_parents="$(git rev-list --parents -1 "$1")"
-       case "$sha1_and_parents" in
-       ?*' '?*' '?*)
-               git diff --cc $sha1_and_parents
-               ;;
-       ?*' '?*)
-               git diff-tree -p "$1^!"
-               ;;
-       *)
-               echo "Root commit"
-               ;;
-       esac > "$state_dir"/patch
-       test -f "$msg" ||
-               commit_message "$1" > "$msg"
-       test -f "$author_script" ||
-               get_author_ident_from_commit "$1" > "$author_script"
-}
-
-die_with_patch () {
-       echo "$1" > "$state_dir"/stopped-sha
-       git update-ref REBASE_HEAD "$1"
-       make_patch "$1"
-       die "$2"
-}
-
-exit_with_patch () {
-       echo "$1" > "$state_dir"/stopped-sha
-       git update-ref REBASE_HEAD "$1"
-       make_patch $1
-       git rev-parse --verify HEAD > "$amend"
-       gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
-       warn "$(eval_gettext "\
-You can amend the commit now, with
-
-       git commit --amend \$gpg_sign_opt_quoted
-
-Once you are satisfied with your changes, run
-
-       git rebase --continue")"
-       warn
-       exit $2
-}
-
 die_abort () {
        apply_autostash
        rm -rf "$state_dir"
@@ -240,30 +81,6 @@ has_action () {
        test -n "$(git stripspace --strip-comments <"$1")"
 }
 
-is_empty_commit() {
-       tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null) || {
-               sha1=$1
-               die "$(eval_gettext "\$sha1: not a commit that can be picked")"
-       }
-       ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) ||
-               ptree=$empty_tree
-       test "$tree" = "$ptree"
-}
-
-is_merge_commit()
-{
-       git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1
-}
-
-# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
-# GIT_AUTHOR_DATE exported from the current environment.
-do_with_author () {
-       (
-               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
-               "$@"
-       )
-}
-
 git_sequence_editor () {
        if test -z "$GIT_SEQUENCE_EDITOR"
        then
@@ -277,455 +94,6 @@ git_sequence_editor () {
        eval "$GIT_SEQUENCE_EDITOR" '"$@"'
 }
 
-pick_one () {
-       ff=--ff
-
-       case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
-       case "$force_rebase" in '') ;; ?*) ff= ;; esac
-       output git rev-parse --verify $sha1 || die "$(eval_gettext "Invalid commit name: \$sha1")"
-
-       if is_empty_commit "$sha1"
-       then
-               empty_args="--allow-empty"
-       fi
-
-       test -d "$rewritten" &&
-               pick_one_preserving_merges "$@" && return
-       output eval git cherry-pick $allow_rerere_autoupdate $allow_empty_message \
-                       ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
-                       $signoff "$strategy_args" $empty_args $ff "$@"
-
-       # If cherry-pick dies it leaves the to-be-picked commit unrecorded. Reschedule
-       # previous task so this commit is not lost.
-       ret=$?
-       case "$ret" in [01]) ;; *) reschedule_last_action ;; esac
-       return $ret
-}
-
-pick_one_preserving_merges () {
-       fast_forward=t
-       case "$1" in
-       -n)
-               fast_forward=f
-               sha1=$2
-               ;;
-       *)
-               sha1=$1
-               ;;
-       esac
-       sha1=$(git rev-parse $sha1)
-
-       if test -f "$state_dir"/current-commit && test "$fast_forward" = t
-       then
-               while read current_commit
-               do
-                       git rev-parse HEAD > "$rewritten"/$current_commit
-               done <"$state_dir"/current-commit
-               rm "$state_dir"/current-commit ||
-                       die "$(gettext "Cannot write current commit's replacement sha1")"
-       fi
-
-       echo $sha1 >> "$state_dir"/current-commit
-
-       # rewrite parents; if none were rewritten, we can fast-forward.
-       new_parents=
-       pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
-       if test "$pend" = " "
-       then
-               pend=" root"
-       fi
-       while [ "$pend" != "" ]
-       do
-               p=$(expr "$pend" : ' \([^ ]*\)')
-               pend="${pend# $p}"
-
-               if test -f "$rewritten"/$p
-               then
-                       new_p=$(cat "$rewritten"/$p)
-
-                       # If the todo reordered commits, and our parent is marked for
-                       # rewriting, but hasn't been gotten to yet, assume the user meant to
-                       # drop it on top of the current HEAD
-                       if test -z "$new_p"
-                       then
-                               new_p=$(git rev-parse HEAD)
-                       fi
-
-                       test $p != $new_p && fast_forward=f
-                       case "$new_parents" in
-                       *$new_p*)
-                               ;; # do nothing; that parent is already there
-                       *)
-                               new_parents="$new_parents $new_p"
-                               ;;
-                       esac
-               else
-                       if test -f "$dropped"/$p
-                       then
-                               fast_forward=f
-                               replacement="$(cat "$dropped"/$p)"
-                               test -z "$replacement" && replacement=root
-                               pend=" $replacement$pend"
-                       else
-                               new_parents="$new_parents $p"
-                       fi
-               fi
-       done
-       case $fast_forward in
-       t)
-               output warn "$(eval_gettext "Fast-forward to \$sha1")"
-               output git reset --hard $sha1 ||
-                       die "$(eval_gettext "Cannot fast-forward to \$sha1")"
-               ;;
-       f)
-               first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-
-               if [ "$1" != "-n" ]
-               then
-                       # detach HEAD to current parent
-                       output git checkout $first_parent 2> /dev/null ||
-                               die "$(eval_gettext "Cannot move HEAD to \$first_parent")"
-               fi
-
-               case "$new_parents" in
-               ' '*' '*)
-                       test "a$1" = a-n && die "$(eval_gettext "Refusing to squash a merge: \$sha1")"
-
-                       # redo merge
-                       author_script_content=$(get_author_ident_from_commit $sha1)
-                       eval "$author_script_content"
-                       msg_content="$(commit_message $sha1)"
-                       # No point in merging the first parent, that's HEAD
-                       new_parents=${new_parents# $first_parent}
-                       merge_args="--no-log --no-ff"
-                       if ! do_with_author output eval \
-                               git merge ${gpg_sign_opt:+$(git rev-parse \
-                                       --sq-quote "$gpg_sign_opt")} \
-                               $allow_rerere_autoupdate "$merge_args" \
-                               "$strategy_args" \
-                               -m "$(git rev-parse --sq-quote "$msg_content")" \
-                               "$new_parents"
-                       then
-                               printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
-                               die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
-                       fi
-                       echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
-                       ;;
-               *)
-                       output eval git cherry-pick $allow_rerere_autoupdate \
-                               $allow_empty_message \
-                               ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
-                               "$strategy_args" "$@" ||
-                               die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
-                       ;;
-               esac
-               ;;
-       esac
-}
-
-this_nth_commit_message () {
-       n=$1
-       eval_gettext "This is the commit message #\${n}:"
-}
-
-skip_nth_commit_message () {
-       n=$1
-       eval_gettext "The commit message #\${n} will be skipped:"
-}
-
-update_squash_messages () {
-       if test -f "$squash_msg"; then
-               mv "$squash_msg" "$squash_msg".bak || exit
-               count=$(($(sed -n \
-                       -e "1s/^$comment_char[^0-9]*\([0-9][0-9]*\).*/\1/p" \
-                       -e "q" < "$squash_msg".bak)+1))
-               {
-                       printf '%s\n' "$comment_char $(eval_ngettext \
-                               "This is a combination of \$count commit." \
-                               "This is a combination of \$count commits." \
-                               $count)"
-                       sed -e 1d -e '2,/^./{
-                               /^$/d
-                       }' <"$squash_msg".bak
-               } >"$squash_msg"
-       else
-               commit_message HEAD >"$fixup_msg" ||
-               die "$(eval_gettext "Cannot write \$fixup_msg")"
-               count=2
-               {
-                       printf '%s\n' "$comment_char $(gettext "This is a combination of 2 commits.")"
-                       printf '%s\n' "$comment_char $(gettext "This is the 1st commit message:")"
-                       echo
-                       cat "$fixup_msg"
-               } >"$squash_msg"
-       fi
-       case $1 in
-       squash)
-               rm -f "$fixup_msg"
-               echo
-               printf '%s\n' "$comment_char $(this_nth_commit_message $count)"
-               echo
-               commit_message $2
-               ;;
-       fixup)
-               echo
-               printf '%s\n' "$comment_char $(skip_nth_commit_message $count)"
-               echo
-               # Change the space after the comment character to TAB:
-               commit_message $2 | git stripspace --comment-lines | sed -e 's/ /       /'
-               ;;
-       esac >>"$squash_msg"
-}
-
-peek_next_command () {
-       git stripspace --strip-comments <"$todo" | sed -n -e 's/ .*//p' -e q
-}
-
-# A squash/fixup has failed.  Prepare the long version of the squash
-# commit message, then die_with_patch.  This code path requires the
-# user to edit the combined commit message for all commits that have
-# been squashed/fixedup so far.  So also erase the old squash
-# messages, effectively causing the combined commit to be used as the
-# new basis for any further squash/fixups.  Args: sha1 rest
-die_failed_squash() {
-       sha1=$1
-       rest=$2
-       mv "$squash_msg" "$msg" || exit
-       rm -f "$fixup_msg"
-       cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
-       warn
-       warn "$(eval_gettext "Could not apply \$sha1... \$rest")"
-       die_with_patch $sha1 ""
-}
-
-flush_rewritten_pending() {
-       test -s "$rewritten_pending" || return
-       newsha1="$(git rev-parse HEAD^0)"
-       sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list"
-       rm -f "$rewritten_pending"
-}
-
-record_in_rewritten() {
-       oldsha1="$(git rev-parse $1)"
-       echo "$oldsha1" >> "$rewritten_pending"
-
-       case "$(peek_next_command)" in
-       squash|s|fixup|f)
-               ;;
-       *)
-               flush_rewritten_pending
-               ;;
-       esac
-}
-
-do_pick () {
-       sha1=$1
-       rest=$2
-       if test "$(git rev-parse HEAD)" = "$squash_onto"
-       then
-               # Set the correct commit message and author info on the
-               # sentinel root before cherry-picking the original changes
-               # without committing (-n).  Finally, update the sentinel again
-               # to include these changes.  If the cherry-pick results in a
-               # conflict, this means our behaviour is similar to a standard
-               # failed cherry-pick during rebase, with a dirty index to
-               # resolve before manually running git commit --amend then git
-               # rebase --continue.
-               git commit --allow-empty --allow-empty-message --amend \
-                          --no-post-rewrite -n -q -C $sha1 $signoff &&
-                       pick_one -n $sha1 &&
-                       git commit --allow-empty --allow-empty-message \
-                                  --amend --no-post-rewrite -n -q -C $sha1 $signoff \
-                                  ${gpg_sign_opt:+"$gpg_sign_opt"} ||
-                                  die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
-       else
-               pick_one $sha1 ||
-                       die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
-       fi
-}
-
-do_next () {
-       rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
-       read -r command sha1 rest < "$todo"
-       case "$command" in
-       "$comment_char"*|''|noop|drop|d)
-               mark_action_done
-               ;;
-       "$cr")
-               # Work around CR left by "read" (e.g. with Git for Windows' Bash).
-               mark_action_done
-               ;;
-       pick|p)
-               comment_for_reflog pick
-
-               mark_action_done
-               do_pick $sha1 "$rest"
-               record_in_rewritten $sha1
-               ;;
-       reword|r)
-               comment_for_reflog reword
-
-               mark_action_done
-               do_pick $sha1 "$rest"
-               git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} \
-                       $allow_empty_message || {
-                       warn "$(eval_gettext "\
-Could not amend commit after successfully picking \$sha1... \$rest
-This is most likely due to an empty commit message, or the pre-commit hook
-failed. If the pre-commit hook failed, you may need to resolve the issue before
-you are able to reword the commit.")"
-                       exit_with_patch $sha1 1
-               }
-               record_in_rewritten $sha1
-               ;;
-       edit|e)
-               comment_for_reflog edit
-
-               mark_action_done
-               do_pick $sha1 "$rest"
-               sha1_abbrev=$(git rev-parse --short $sha1)
-               warn "$(eval_gettext "Stopped at \$sha1_abbrev... \$rest")"
-               exit_with_patch $sha1 0
-               ;;
-       squash|s|fixup|f)
-               case "$command" in
-               squash|s)
-                       squash_style=squash
-                       ;;
-               fixup|f)
-                       squash_style=fixup
-                       ;;
-               esac
-               comment_for_reflog $squash_style
-
-               test -f "$done" && has_action "$done" ||
-                       die "$(eval_gettext "Cannot '\$squash_style' without a previous commit")"
-
-               mark_action_done
-               update_squash_messages $squash_style $sha1
-               author_script_content=$(get_author_ident_from_commit HEAD)
-               echo "$author_script_content" > "$author_script"
-               eval "$author_script_content"
-               if ! pick_one -n $sha1
-               then
-                       git rev-parse --verify HEAD >"$amend"
-                       die_failed_squash $sha1 "$rest"
-               fi
-               case "$(peek_next_command)" in
-               squash|s|fixup|f)
-                       # This is an intermediate commit; its message will only be
-                       # used in case of trouble.  So use the long version:
-                       do_with_author output git commit --amend --no-verify -F "$squash_msg" \
-                               ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
-                               die_failed_squash $sha1 "$rest"
-                       ;;
-               *)
-                       # This is the final command of this squash/fixup group
-                       if test -f "$fixup_msg"
-                       then
-                               do_with_author git commit --amend --no-verify -F "$fixup_msg" \
-                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
-                                       die_failed_squash $sha1 "$rest"
-                       else
-                               cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
-                               rm -f "$GIT_DIR"/MERGE_MSG
-                               do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
-                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
-                                       die_failed_squash $sha1 "$rest"
-                       fi
-                       rm -f "$squash_msg" "$fixup_msg"
-                       ;;
-               esac
-               record_in_rewritten $sha1
-               ;;
-       x|"exec")
-               read -r command rest < "$todo"
-               mark_action_done
-               eval_gettextln "Executing: \$rest"
-               "${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
-               status=$?
-               # Run in subshell because require_clean_work_tree can die.
-               dirty=f
-               (require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
-               if test "$status" -ne 0
-               then
-                       warn "$(eval_gettext "Execution failed: \$rest")"
-                       test "$dirty" = f ||
-                               warn "$(gettext "and made changes to the index and/or the working tree")"
-
-                       warn "$(gettext "\
-You can fix the problem, and then run
-
-       git rebase --continue")"
-                       warn
-                       if test $status -eq 127         # command not found
-                       then
-                               status=1
-                       fi
-                       exit "$status"
-               elif test "$dirty" = t
-               then
-                       # TRANSLATORS: after these lines is a command to be issued by the user
-                       warn "$(eval_gettext "\
-Execution succeeded: \$rest
-but left changes to the index and/or the working tree
-Commit or stash your changes, and then run
-
-       git rebase --continue")"
-                       warn
-                       exit 1
-               fi
-               ;;
-       *)
-               warn "$(eval_gettext "Unknown command: \$command \$sha1 \$rest")"
-               fixtodo="$(gettext "Please fix this using 'git rebase --edit-todo'.")"
-               if git rev-parse --verify -q "$sha1" >/dev/null
-               then
-                       die_with_patch $sha1 "$fixtodo"
-               else
-                       die "$fixtodo"
-               fi
-               ;;
-       esac
-       test -s "$todo" && return
-
-       comment_for_reflog finish &&
-       newhead=$(git rev-parse HEAD) &&
-       case $head_name in
-       refs/*)
-               message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
-               git update-ref -m "$message" $head_name $newhead $orig_head &&
-               git symbolic-ref \
-                 -m "$GIT_REFLOG_ACTION: returning to $head_name" \
-                 HEAD $head_name
-               ;;
-       esac && {
-               test ! -f "$state_dir"/verbose ||
-                       git diff-tree --stat $orig_head..HEAD
-       } &&
-       {
-               test -s "$rewritten_list" &&
-               git notes copy --for-rewrite=rebase < "$rewritten_list" ||
-               true # we don't care if this copying failed
-       } &&
-       hook="$(git rev-parse --git-path hooks/post-rewrite)"
-       if test -x "$hook" && test -s "$rewritten_list"; then
-               "$hook" rebase < "$rewritten_list"
-               true # we don't care if this hook failed
-       fi &&
-               warn "$(eval_gettext "Successfully rebased and updated \$head_name.")"
-
-       return 1 # not failure; just to break the do_rest loop
-}
-
-# can only return 0, when the infinite loop breaks
-do_rest () {
-       while :
-       do
-               do_next || break
-       done
-}
-
 expand_todo_ids() {
        git rebase--helper --expand-ids
 }
@@ -763,77 +131,13 @@ get_missing_commit_check_level () {
 initiate_action () {
        case "$1" in
        continue)
-               if test ! -d "$rewritten"
-               then
-                       exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-                               --continue
-               fi
-               # do we have anything to commit?
-               if git diff-index --cached --quiet HEAD --
-               then
-                       # Nothing to commit -- skip this commit
-
-                       test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
-                       rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
-                       die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
-               else
-                       if ! test -f "$author_script"
-                       then
-                               gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
-                               die "$(eval_gettext "\
-You have staged changes in your working tree.
-If these changes are meant to be
-squashed into the previous commit, run:
-
-  git commit --amend \$gpg_sign_opt_quoted
-
-If they are meant to go into a new commit, run:
-
-  git commit \$gpg_sign_opt_quoted
-
-In both cases, once you're done, continue with:
-
-  git rebase --continue
-")"
-                       fi
-                       . "$author_script" ||
-                               die "$(gettext "Error trying to find the author identity to amend commit")"
-                       if test -f "$amend"
-                       then
-                               current_head=$(git rev-parse --verify HEAD)
-                               test "$current_head" = $(cat "$amend") ||
-                               die "$(gettext "\
-You have uncommitted changes in your working tree. Please commit them
-first and then run 'git rebase --continue' again.")"
-                               do_with_author git commit --amend --no-verify -F "$msg" -e \
-                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
-                                       die "$(gettext "Could not commit staged changes.")"
-                       else
-                               do_with_author git commit --no-verify -F "$msg" -e \
-                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
-                                       die "$(gettext "Could not commit staged changes.")"
-                       fi
-               fi
-
-               if test -r "$state_dir"/stopped-sha
-               then
-                       record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
-               fi
-
-               require_clean_work_tree "rebase"
-               do_rest
-               return 0
+               exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
+                    --continue
                ;;
        skip)
                git rerere clear
-
-               if test ! -d "$rewritten"
-               then
-                       exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-                               --continue
-               fi
-               do_rest
-               return 0
+               exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
+                    --continue
                ;;
        edit-todo)
                git stripspace --strip-comments <"$todo" >"$todo".new
@@ -947,18 +251,14 @@ EOF
 
        expand_todo_ids
 
-       test -d "$rewritten" || test -n "$force_rebase" ||
+       test -n "$force_rebase" ||
        onto="$(git rebase--helper --skip-unnecessary-picks)" ||
        die "Could not skip unnecessary pick commands"
 
        checkout_onto
-       if test ! -d "$rewritten"
-       then
-               require_clean_work_tree "rebase"
-               exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
-                       --continue
-       fi
-       do_rest
+       require_clean_work_tree "rebase"
+       exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
+            --continue
 }
 
 git_rebase__interactive () {
@@ -981,91 +281,3 @@ git_rebase__interactive () {
 
        complete_action
 }
-
-git_rebase__interactive__preserve_merges () {
-       initiate_action "$action"
-       ret=$?
-       if test $ret = 0; then
-               return 0
-       fi
-
-       setup_reflog_action
-       init_basic_state
-
-       if test -z "$rebase_root"
-       then
-               mkdir "$rewritten" &&
-               for c in $(git merge-base --all $orig_head $upstream)
-               do
-                       echo $onto > "$rewritten"/$c ||
-                               die "$(gettext "Could not init rewritten commits")"
-               done
-       else
-               mkdir "$rewritten" &&
-               echo $onto > "$rewritten"/root ||
-                       die "$(gettext "Could not init rewritten commits")"
-       fi
-
-       init_revisions_and_shortrevisions
-
-       format=$(git config --get rebase.instructionFormat)
-       # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
-       git rev-list --format="%m%H ${format:-%s}" \
-               --reverse --left-right --topo-order \
-               $revisions ${restrict_revision+^$restrict_revision} | \
-               sed -n "s/^>//p" |
-       while read -r sha1 rest
-       do
-               if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
-               then
-                       comment_out="$comment_char "
-               else
-                       comment_out=
-               fi
-
-               if test -z "$rebase_root"
-               then
-                       preserve=t
-                       for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
-                       do
-                               if test -f "$rewritten"/$p
-                               then
-                                       preserve=f
-                               fi
-                       done
-               else
-                       preserve=f
-               fi
-               if test f = "$preserve"
-               then
-                       touch "$rewritten"/$sha1
-                       printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
-               fi
-       done
-
-       # Watch for commits that been dropped by --cherry-pick
-       mkdir "$dropped"
-       # Save all non-cherry-picked changes
-       git rev-list $revisions --left-right --cherry-pick | \
-               sed -n "s/^>//p" > "$state_dir"/not-cherry-picks
-       # Now all commits and note which ones are missing in
-       # not-cherry-picks and hence being dropped
-       git rev-list $revisions |
-       while read rev
-       do
-               if test -f "$rewritten"/$rev &&
-                  ! sane_grep "$rev" "$state_dir"/not-cherry-picks >/dev/null
-               then
-                       # Use -f2 because if rev-list is telling us this commit is
-                       # not worthwhile, we don't want to track its multiple heads,
-                       # just the history of its first-parent for others that will
-                       # be rebasing on top of it
-                       git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev
-                       sha1=$(git rev-list -1 $rev)
-                       sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
-                       rm "$rewritten"/$rev
-               fi
-       done
-
-       complete_action
-}
diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh
new file mode 100644 (file)
index 0000000..c51c782
--- /dev/null
@@ -0,0 +1,1012 @@
+# This shell script fragment is sourced by git-rebase to implement its
+# preserve-merges mode.
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+# The file containing rebase commands, comments, and empty lines.
+# This file is created by "git rebase -i" then edited by the user.  As
+# the lines are processed, they are removed from the front of this
+# file and written to the tail of $done.
+todo="$state_dir"/git-rebase-todo
+
+# The rebase command lines that have already been processed.  A line
+# is moved here when it is first handled, before any associated user
+# actions.
+done="$state_dir"/done
+
+# The commit message that is planned to be used for any changes that
+# need to be committed following a user interaction.
+msg="$state_dir"/message
+
+# The file into which is accumulated the suggested commit message for
+# squash/fixup commands.  When the first of a series of squash/fixups
+# is seen, the file is created and the commit message from the
+# previous commit and from the first squash/fixup commit are written
+# to it.  The commit message for each subsequent squash/fixup commit
+# is appended to the file as it is processed.
+#
+# The first line of the file is of the form
+#     # This is a combination of $count commits.
+# where $count is the number of commits whose messages have been
+# written to the file so far (including the initial "pick" commit).
+# Each time that a commit message is processed, this line is read and
+# updated.  It is deleted just before the combined commit is made.
+squash_msg="$state_dir"/message-squash
+
+# If the current series of squash/fixups has not yet included a squash
+# command, then this file exists and holds the commit message of the
+# original "pick" commit.  (If the series ends without a "squash"
+# command, then this can be used as the commit message of the combined
+# commit without opening the editor.)
+fixup_msg="$state_dir"/message-fixup
+
+# $rewritten is the name of a directory containing files for each
+# commit that is reachable by at least one merge base of $head and
+# $upstream. They are not necessarily rewritten, but their children
+# might be.  This ensures that commits on merged, but otherwise
+# unrelated side branches are left alone. (Think "X" in the man page's
+# example.)
+rewritten="$state_dir"/rewritten
+
+dropped="$state_dir"/dropped
+
+end="$state_dir"/end
+msgnum="$state_dir"/msgnum
+
+# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+# GIT_AUTHOR_DATE that will be used for the commit that is currently
+# being rebased.
+author_script="$state_dir"/author-script
+
+# When an "edit" rebase command is being processed, the SHA1 of the
+# commit to be edited is recorded in this file.  When "git rebase
+# --continue" is executed, if there are any staged changes then they
+# will be amended to the HEAD commit, but only provided the HEAD
+# commit is still the commit to be edited.  When any other rebase
+# command is processed, this file is deleted.
+amend="$state_dir"/amend
+
+# For the post-rewrite hook, we make a list of rewritten commits and
+# their new sha1s.  The rewritten-pending list keeps the sha1s of
+# commits that have been processed, but not committed yet,
+# e.g. because they are waiting for a 'squash' command.
+rewritten_list="$state_dir"/rewritten-list
+rewritten_pending="$state_dir"/rewritten-pending
+
+# Work around Git for Windows' Bash whose "read" does not strip CRLF
+# and leaves CR at the end instead.
+cr=$(printf "\015")
+
+strategy_args=${strategy:+--strategy=$strategy}
+test -n "$strategy_opts" &&
+eval '
+       for strategy_opt in '"$strategy_opts"'
+       do
+               strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
+       done
+'
+
+GIT_CHERRY_PICK_HELP="$resolvemsg"
+export GIT_CHERRY_PICK_HELP
+
+comment_char=$(git config --get core.commentchar 2>/dev/null)
+case "$comment_char" in
+'' | auto)
+       comment_char="#"
+       ;;
+?)
+       ;;
+*)
+       comment_char=$(echo "$comment_char" | cut -c1)
+       ;;
+esac
+
+warn () {
+       printf '%s\n' "$*" >&2
+}
+
+# Output the commit message for the specified commit.
+commit_message () {
+       git cat-file commit "$1" | sed "1,/^$/d"
+}
+
+orig_reflog_action="$GIT_REFLOG_ACTION"
+
+comment_for_reflog () {
+       case "$orig_reflog_action" in
+       ''|rebase*)
+               GIT_REFLOG_ACTION="rebase -i ($1)"
+               export GIT_REFLOG_ACTION
+               ;;
+       esac
+}
+
+last_count=
+mark_action_done () {
+       sed -e 1q < "$todo" >> "$done"
+       sed -e 1d < "$todo" >> "$todo".new
+       mv -f "$todo".new "$todo"
+       new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
+       echo $new_count >"$msgnum"
+       total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
+       echo $total >"$end"
+       if test "$last_count" != "$new_count"
+       then
+               last_count=$new_count
+               eval_gettext "Rebasing (\$new_count/\$total)"; printf "\r"
+               test -z "$verbose" || echo
+       fi
+}
+
+# Put the last action marked done at the beginning of the todo list
+# again. If there has not been an action marked done yet, leave the list of
+# items on the todo list unchanged.
+reschedule_last_action () {
+       tail -n 1 "$done" | cat - "$todo" >"$todo".new
+       sed -e \$d <"$done" >"$done".new
+       mv -f "$todo".new "$todo"
+       mv -f "$done".new "$done"
+}
+
+append_todo_help () {
+       gettext "
+Commands:
+p, pick <commit> = use commit
+r, reword <commit> = use commit, but edit the commit message
+e, edit <commit> = use commit, but stop for amending
+s, squash <commit> = use commit, but meld into previous commit
+f, fixup <commit> = like \"squash\", but discard this commit's log message
+x, exec <commit> = run command (the rest of the line) using shell
+d, drop <commit> = remove commit
+l, label <label> = label current HEAD with a name
+t, reset <label> = reset HEAD to a label
+m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
+.       create a merge commit using the original merge commit's
+.       message (or the oneline, if no original merge commit was
+.       specified). Use -c <commit> to reword the commit message.
+
+These lines can be re-ordered; they are executed from top to bottom.
+" | git stripspace --comment-lines >>"$todo"
+
+       if test $(get_missing_commit_check_level) = error
+       then
+               gettext "
+Do not remove any line. Use 'drop' explicitly to remove a commit.
+" | git stripspace --comment-lines >>"$todo"
+       else
+               gettext "
+If you remove a line here THAT COMMIT WILL BE LOST.
+" | git stripspace --comment-lines >>"$todo"
+       fi
+}
+
+make_patch () {
+       sha1_and_parents="$(git rev-list --parents -1 "$1")"
+       case "$sha1_and_parents" in
+       ?*' '?*' '?*)
+               git diff --cc $sha1_and_parents
+               ;;
+       ?*' '?*)
+               git diff-tree -p "$1^!"
+               ;;
+       *)
+               echo "Root commit"
+               ;;
+       esac > "$state_dir"/patch
+       test -f "$msg" ||
+               commit_message "$1" > "$msg"
+       test -f "$author_script" ||
+               get_author_ident_from_commit "$1" > "$author_script"
+}
+
+die_with_patch () {
+       echo "$1" > "$state_dir"/stopped-sha
+       git update-ref REBASE_HEAD "$1"
+       make_patch "$1"
+       die "$2"
+}
+
+exit_with_patch () {
+       echo "$1" > "$state_dir"/stopped-sha
+       git update-ref REBASE_HEAD "$1"
+       make_patch $1
+       git rev-parse --verify HEAD > "$amend"
+       gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
+       warn "$(eval_gettext "\
+You can amend the commit now, with
+
+       git commit --amend \$gpg_sign_opt_quoted
+
+Once you are satisfied with your changes, run
+
+       git rebase --continue")"
+       warn
+       exit $2
+}
+
+die_abort () {
+       apply_autostash
+       rm -rf "$state_dir"
+       die "$1"
+}
+
+has_action () {
+       test -n "$(git stripspace --strip-comments <"$1")"
+}
+
+is_empty_commit() {
+       tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null) || {
+               sha1=$1
+               die "$(eval_gettext "\$sha1: not a commit that can be picked")"
+       }
+       ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) ||
+               ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+       test "$tree" = "$ptree"
+}
+
+is_merge_commit()
+{
+       git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1
+}
+
+# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+# GIT_AUTHOR_DATE exported from the current environment.
+do_with_author () {
+       (
+               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+               "$@"
+       )
+}
+
+git_sequence_editor () {
+       if test -z "$GIT_SEQUENCE_EDITOR"
+       then
+               GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
+               if [ -z "$GIT_SEQUENCE_EDITOR" ]
+               then
+                       GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
+               fi
+       fi
+
+       eval "$GIT_SEQUENCE_EDITOR" '"$@"'
+}
+
+pick_one () {
+       ff=--ff
+
+       case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
+       case "$force_rebase" in '') ;; ?*) ff= ;; esac
+       output git rev-parse --verify $sha1 || die "$(eval_gettext "Invalid commit name: \$sha1")"
+
+       if is_empty_commit "$sha1"
+       then
+               empty_args="--allow-empty"
+       fi
+
+       pick_one_preserving_merges "$@"
+}
+
+pick_one_preserving_merges () {
+       fast_forward=t
+       case "$1" in
+       -n)
+               fast_forward=f
+               sha1=$2
+               ;;
+       *)
+               sha1=$1
+               ;;
+       esac
+       sha1=$(git rev-parse $sha1)
+
+       if test -f "$state_dir"/current-commit && test "$fast_forward" = t
+       then
+               while read current_commit
+               do
+                       git rev-parse HEAD > "$rewritten"/$current_commit
+               done <"$state_dir"/current-commit
+               rm "$state_dir"/current-commit ||
+                       die "$(gettext "Cannot write current commit's replacement sha1")"
+       fi
+
+       echo $sha1 >> "$state_dir"/current-commit
+
+       # rewrite parents; if none were rewritten, we can fast-forward.
+       new_parents=
+       pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
+       if test "$pend" = " "
+       then
+               pend=" root"
+       fi
+       while [ "$pend" != "" ]
+       do
+               p=$(expr "$pend" : ' \([^ ]*\)')
+               pend="${pend# $p}"
+
+               if test -f "$rewritten"/$p
+               then
+                       new_p=$(cat "$rewritten"/$p)
+
+                       # If the todo reordered commits, and our parent is marked for
+                       # rewriting, but hasn't been gotten to yet, assume the user meant to
+                       # drop it on top of the current HEAD
+                       if test -z "$new_p"
+                       then
+                               new_p=$(git rev-parse HEAD)
+                       fi
+
+                       test $p != $new_p && fast_forward=f
+                       case "$new_parents" in
+                       *$new_p*)
+                               ;; # do nothing; that parent is already there
+                       *)
+                               new_parents="$new_parents $new_p"
+                               ;;
+                       esac
+               else
+                       if test -f "$dropped"/$p
+                       then
+                               fast_forward=f
+                               replacement="$(cat "$dropped"/$p)"
+                               test -z "$replacement" && replacement=root
+                               pend=" $replacement$pend"
+                       else
+                               new_parents="$new_parents $p"
+                       fi
+               fi
+       done
+       case $fast_forward in
+       t)
+               output warn "$(eval_gettext "Fast-forward to \$sha1")"
+               output git reset --hard $sha1 ||
+                       die "$(eval_gettext "Cannot fast-forward to \$sha1")"
+               ;;
+       f)
+               first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
+
+               if [ "$1" != "-n" ]
+               then
+                       # detach HEAD to current parent
+                       output git checkout $first_parent 2> /dev/null ||
+                               die "$(eval_gettext "Cannot move HEAD to \$first_parent")"
+               fi
+
+               case "$new_parents" in
+               ' '*' '*)
+                       test "a$1" = a-n && die "$(eval_gettext "Refusing to squash a merge: \$sha1")"
+
+                       # redo merge
+                       author_script_content=$(get_author_ident_from_commit $sha1)
+                       eval "$author_script_content"
+                       msg_content="$(commit_message $sha1)"
+                       # No point in merging the first parent, that's HEAD
+                       new_parents=${new_parents# $first_parent}
+                       merge_args="--no-log --no-ff"
+                       if ! do_with_author output eval \
+                               git merge ${gpg_sign_opt:+$(git rev-parse \
+                                       --sq-quote "$gpg_sign_opt")} \
+                               $allow_rerere_autoupdate "$merge_args" \
+                               "$strategy_args" \
+                               -m "$(git rev-parse --sq-quote "$msg_content")" \
+                               "$new_parents"
+                       then
+                               printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
+                               die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
+                       fi
+                       echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
+                       ;;
+               *)
+                       output eval git cherry-pick $allow_rerere_autoupdate \
+                               $allow_empty_message \
+                               ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
+                               "$strategy_args" "$@" ||
+                               die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
+                       ;;
+               esac
+               ;;
+       esac
+}
+
+this_nth_commit_message () {
+       n=$1
+       eval_gettext "This is the commit message #\${n}:"
+}
+
+skip_nth_commit_message () {
+       n=$1
+       eval_gettext "The commit message #\${n} will be skipped:"
+}
+
+update_squash_messages () {
+       if test -f "$squash_msg"; then
+               mv "$squash_msg" "$squash_msg".bak || exit
+               count=$(($(sed -n \
+                       -e "1s/^$comment_char[^0-9]*\([0-9][0-9]*\).*/\1/p" \
+                       -e "q" < "$squash_msg".bak)+1))
+               {
+                       printf '%s\n' "$comment_char $(eval_ngettext \
+                               "This is a combination of \$count commit." \
+                               "This is a combination of \$count commits." \
+                               $count)"
+                       sed -e 1d -e '2,/^./{
+                               /^$/d
+                       }' <"$squash_msg".bak
+               } >"$squash_msg"
+       else
+               commit_message HEAD >"$fixup_msg" ||
+               die "$(eval_gettext "Cannot write \$fixup_msg")"
+               count=2
+               {
+                       printf '%s\n' "$comment_char $(gettext "This is a combination of 2 commits.")"
+                       printf '%s\n' "$comment_char $(gettext "This is the 1st commit message:")"
+                       echo
+                       cat "$fixup_msg"
+               } >"$squash_msg"
+       fi
+       case $1 in
+       squash)
+               rm -f "$fixup_msg"
+               echo
+               printf '%s\n' "$comment_char $(this_nth_commit_message $count)"
+               echo
+               commit_message $2
+               ;;
+       fixup)
+               echo
+               printf '%s\n' "$comment_char $(skip_nth_commit_message $count)"
+               echo
+               # Change the space after the comment character to TAB:
+               commit_message $2 | git stripspace --comment-lines | sed -e 's/ /       /'
+               ;;
+       esac >>"$squash_msg"
+}
+
+peek_next_command () {
+       git stripspace --strip-comments <"$todo" | sed -n -e 's/ .*//p' -e q
+}
+
+# A squash/fixup has failed.  Prepare the long version of the squash
+# commit message, then die_with_patch.  This code path requires the
+# user to edit the combined commit message for all commits that have
+# been squashed/fixedup so far.  So also erase the old squash
+# messages, effectively causing the combined commit to be used as the
+# new basis for any further squash/fixups.  Args: sha1 rest
+die_failed_squash() {
+       sha1=$1
+       rest=$2
+       mv "$squash_msg" "$msg" || exit
+       rm -f "$fixup_msg"
+       cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
+       warn
+       warn "$(eval_gettext "Could not apply \$sha1... \$rest")"
+       die_with_patch $sha1 ""
+}
+
+flush_rewritten_pending() {
+       test -s "$rewritten_pending" || return
+       newsha1="$(git rev-parse HEAD^0)"
+       sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list"
+       rm -f "$rewritten_pending"
+}
+
+record_in_rewritten() {
+       oldsha1="$(git rev-parse $1)"
+       echo "$oldsha1" >> "$rewritten_pending"
+
+       case "$(peek_next_command)" in
+       squash|s|fixup|f)
+               ;;
+       *)
+               flush_rewritten_pending
+               ;;
+       esac
+}
+
+do_pick () {
+       sha1=$1
+       rest=$2
+       if test "$(git rev-parse HEAD)" = "$squash_onto"
+       then
+               # Set the correct commit message and author info on the
+               # sentinel root before cherry-picking the original changes
+               # without committing (-n).  Finally, update the sentinel again
+               # to include these changes.  If the cherry-pick results in a
+               # conflict, this means our behaviour is similar to a standard
+               # failed cherry-pick during rebase, with a dirty index to
+               # resolve before manually running git commit --amend then git
+               # rebase --continue.
+               git commit --allow-empty --allow-empty-message --amend \
+                          --no-post-rewrite -n -q -C $sha1 $signoff &&
+                       pick_one -n $sha1 &&
+                       git commit --allow-empty --allow-empty-message \
+                                  --amend --no-post-rewrite -n -q -C $sha1 $signoff \
+                                  ${gpg_sign_opt:+"$gpg_sign_opt"} ||
+                                  die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
+       else
+               pick_one $sha1 ||
+                       die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
+       fi
+}
+
+do_next () {
+       rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
+       read -r command sha1 rest < "$todo"
+       case "$command" in
+       "$comment_char"*|''|noop|drop|d)
+               mark_action_done
+               ;;
+       "$cr")
+               # Work around CR left by "read" (e.g. with Git for Windows' Bash).
+               mark_action_done
+               ;;
+       pick|p)
+               comment_for_reflog pick
+
+               mark_action_done
+               do_pick $sha1 "$rest"
+               record_in_rewritten $sha1
+               ;;
+       reword|r)
+               comment_for_reflog reword
+
+               mark_action_done
+               do_pick $sha1 "$rest"
+               git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} \
+                       $allow_empty_message || {
+                       warn "$(eval_gettext "\
+Could not amend commit after successfully picking \$sha1... \$rest
+This is most likely due to an empty commit message, or the pre-commit hook
+failed. If the pre-commit hook failed, you may need to resolve the issue before
+you are able to reword the commit.")"
+                       exit_with_patch $sha1 1
+               }
+               record_in_rewritten $sha1
+               ;;
+       edit|e)
+               comment_for_reflog edit
+
+               mark_action_done
+               do_pick $sha1 "$rest"
+               sha1_abbrev=$(git rev-parse --short $sha1)
+               warn "$(eval_gettext "Stopped at \$sha1_abbrev... \$rest")"
+               exit_with_patch $sha1 0
+               ;;
+       squash|s|fixup|f)
+               case "$command" in
+               squash|s)
+                       squash_style=squash
+                       ;;
+               fixup|f)
+                       squash_style=fixup
+                       ;;
+               esac
+               comment_for_reflog $squash_style
+
+               test -f "$done" && has_action "$done" ||
+                       die "$(eval_gettext "Cannot '\$squash_style' without a previous commit")"
+
+               mark_action_done
+               update_squash_messages $squash_style $sha1
+               author_script_content=$(get_author_ident_from_commit HEAD)
+               echo "$author_script_content" > "$author_script"
+               eval "$author_script_content"
+               if ! pick_one -n $sha1
+               then
+                       git rev-parse --verify HEAD >"$amend"
+                       die_failed_squash $sha1 "$rest"
+               fi
+               case "$(peek_next_command)" in
+               squash|s|fixup|f)
+                       # This is an intermediate commit; its message will only be
+                       # used in case of trouble.  So use the long version:
+                       do_with_author output git commit --amend --no-verify -F "$squash_msg" \
+                               ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
+                               die_failed_squash $sha1 "$rest"
+                       ;;
+               *)
+                       # This is the final command of this squash/fixup group
+                       if test -f "$fixup_msg"
+                       then
+                               do_with_author git commit --amend --no-verify -F "$fixup_msg" \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
+                                       die_failed_squash $sha1 "$rest"
+                       else
+                               cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
+                               rm -f "$GIT_DIR"/MERGE_MSG
+                               do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
+                                       die_failed_squash $sha1 "$rest"
+                       fi
+                       rm -f "$squash_msg" "$fixup_msg"
+                       ;;
+               esac
+               record_in_rewritten $sha1
+               ;;
+       x|"exec")
+               read -r command rest < "$todo"
+               mark_action_done
+               eval_gettextln "Executing: \$rest"
+               "${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
+               status=$?
+               # Run in subshell because require_clean_work_tree can die.
+               dirty=f
+               (require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
+               if test "$status" -ne 0
+               then
+                       warn "$(eval_gettext "Execution failed: \$rest")"
+                       test "$dirty" = f ||
+                               warn "$(gettext "and made changes to the index and/or the working tree")"
+
+                       warn "$(gettext "\
+You can fix the problem, and then run
+
+       git rebase --continue")"
+                       warn
+                       if test $status -eq 127         # command not found
+                       then
+                               status=1
+                       fi
+                       exit "$status"
+               elif test "$dirty" = t
+               then
+                       # TRANSLATORS: after these lines is a command to be issued by the user
+                       warn "$(eval_gettext "\
+Execution succeeded: \$rest
+but left changes to the index and/or the working tree
+Commit or stash your changes, and then run
+
+       git rebase --continue")"
+                       warn
+                       exit 1
+               fi
+               ;;
+       *)
+               warn "$(eval_gettext "Unknown command: \$command \$sha1 \$rest")"
+               fixtodo="$(gettext "Please fix this using 'git rebase --edit-todo'.")"
+               if git rev-parse --verify -q "$sha1" >/dev/null
+               then
+                       die_with_patch $sha1 "$fixtodo"
+               else
+                       die "$fixtodo"
+               fi
+               ;;
+       esac
+       test -s "$todo" && return
+
+       comment_for_reflog finish &&
+       newhead=$(git rev-parse HEAD) &&
+       case $head_name in
+       refs/*)
+               message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
+               git update-ref -m "$message" $head_name $newhead $orig_head &&
+               git symbolic-ref \
+                 -m "$GIT_REFLOG_ACTION: returning to $head_name" \
+                 HEAD $head_name
+               ;;
+       esac && {
+               test ! -f "$state_dir"/verbose ||
+                       git diff-tree --stat $orig_head..HEAD
+       } &&
+       {
+               test -s "$rewritten_list" &&
+               git notes copy --for-rewrite=rebase < "$rewritten_list" ||
+               true # we don't care if this copying failed
+       } &&
+       hook="$(git rev-parse --git-path hooks/post-rewrite)"
+       if test -x "$hook" && test -s "$rewritten_list"; then
+               "$hook" rebase < "$rewritten_list"
+               true # we don't care if this hook failed
+       fi &&
+               warn "$(eval_gettext "Successfully rebased and updated \$head_name.")"
+
+       return 1 # not failure; just to break the do_rest loop
+}
+
+# can only return 0, when the infinite loop breaks
+do_rest () {
+       while :
+       do
+               do_next || break
+       done
+}
+
+expand_todo_ids() {
+       git rebase--helper --expand-ids
+}
+
+collapse_todo_ids() {
+       git rebase--helper --shorten-ids
+}
+
+# Switch to the branch in $into and notify it in the reflog
+checkout_onto () {
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
+       output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
+       git update-ref ORIG_HEAD $orig_head
+}
+
+get_missing_commit_check_level () {
+       check_level=$(git config --get rebase.missingCommitsCheck)
+       check_level=${check_level:-ignore}
+       # Don't be case sensitive
+       printf '%s' "$check_level" | tr 'A-Z' 'a-z'
+}
+
+# Initiate an action. If the cannot be any
+# further action it  may exec a command
+# or exit and not return.
+#
+# TODO: Consider a cleaner return model so it
+# never exits and always return 0 if process
+# is complete.
+#
+# Parameter 1 is the action to initiate.
+#
+# Returns 0 if the action was able to complete
+# and if 1 if further processing is required.
+initiate_action () {
+       case "$1" in
+       continue)
+               # do we have anything to commit?
+               if git diff-index --cached --quiet HEAD --
+               then
+                       # Nothing to commit -- skip this commit
+
+                       test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
+                       rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
+                       die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
+               else
+                       if ! test -f "$author_script"
+                       then
+                               gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
+                               die "$(eval_gettext "\
+You have staged changes in your working tree.
+If these changes are meant to be
+squashed into the previous commit, run:
+
+  git commit --amend \$gpg_sign_opt_quoted
+
+If they are meant to go into a new commit, run:
+
+  git commit \$gpg_sign_opt_quoted
+
+In both cases, once you're done, continue with:
+
+  git rebase --continue
+")"
+                       fi
+                       . "$author_script" ||
+                               die "$(gettext "Error trying to find the author identity to amend commit")"
+                       if test -f "$amend"
+                       then
+                               current_head=$(git rev-parse --verify HEAD)
+                               test "$current_head" = $(cat "$amend") ||
+                               die "$(gettext "\
+You have uncommitted changes in your working tree. Please commit them
+first and then run 'git rebase --continue' again.")"
+                               do_with_author git commit --amend --no-verify -F "$msg" -e \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
+                                       die "$(gettext "Could not commit staged changes.")"
+                       else
+                               do_with_author git commit --no-verify -F "$msg" -e \
+                                       ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
+                                       die "$(gettext "Could not commit staged changes.")"
+                       fi
+               fi
+
+               if test -r "$state_dir"/stopped-sha
+               then
+                       record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
+               fi
+
+               require_clean_work_tree "rebase"
+               do_rest
+               return 0
+               ;;
+       skip)
+               git rerere clear
+               do_rest
+               return 0
+               ;;
+       edit-todo)
+               git stripspace --strip-comments <"$todo" >"$todo".new
+               mv -f "$todo".new "$todo"
+               collapse_todo_ids
+               append_todo_help
+               gettext "
+You are editing the todo file of an ongoing interactive rebase.
+To continue rebase after editing, run:
+    git rebase --continue
+
+" | git stripspace --comment-lines >>"$todo"
+
+               git_sequence_editor "$todo" ||
+                       die "$(gettext "Could not execute editor")"
+               expand_todo_ids
+
+               exit
+               ;;
+       show-current-patch)
+               exec git show REBASE_HEAD --
+               ;;
+       *)
+               return 1 # continue
+               ;;
+       esac
+}
+
+setup_reflog_action () {
+       comment_for_reflog start
+
+       if test ! -z "$switch_to"
+       then
+               GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
+               output git checkout "$switch_to" -- ||
+                       die "$(eval_gettext "Could not checkout \$switch_to")"
+
+               comment_for_reflog start
+       fi
+}
+
+init_basic_state () {
+       orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
+       mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
+       rm -f "$(git rev-parse --git-path REBASE_HEAD)"
+
+       : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
+       write_basic_state
+}
+
+init_revisions_and_shortrevisions () {
+       shorthead=$(git rev-parse --short $orig_head)
+       shortonto=$(git rev-parse --short $onto)
+       if test -z "$rebase_root"
+               # this is now equivalent to ! -z "$upstream"
+       then
+               shortupstream=$(git rev-parse --short $upstream)
+               revisions=$upstream...$orig_head
+               shortrevisions=$shortupstream..$shorthead
+       else
+               revisions=$onto...$orig_head
+               shortrevisions=$shorthead
+               test -z "$squash_onto" ||
+               echo "$squash_onto" >"$state_dir"/squash-onto
+       fi
+}
+
+complete_action() {
+       test -s "$todo" || echo noop >> "$todo"
+       test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
+       test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
+
+       todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
+       todocount=${todocount##* }
+
+cat >>"$todo" <<EOF
+
+$comment_char $(eval_ngettext \
+       "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
+       "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
+       "$todocount")
+EOF
+       append_todo_help
+       gettext "
+       However, if you remove everything, the rebase will be aborted.
+
+       " | git stripspace --comment-lines >>"$todo"
+
+       if test -z "$keep_empty"
+       then
+               printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
+       fi
+
+
+       has_action "$todo" ||
+               return 2
+
+       cp "$todo" "$todo".backup
+       collapse_todo_ids
+       git_sequence_editor "$todo" ||
+               die_abort "$(gettext "Could not execute editor")"
+
+       has_action "$todo" ||
+               return 2
+
+       git rebase--helper --check-todo-list || {
+               ret=$?
+               checkout_onto
+               exit $ret
+       }
+
+       expand_todo_ids
+       checkout_onto
+       do_rest
+}
+
+git_rebase__preserve_merges () {
+       initiate_action "$action"
+       ret=$?
+       if test $ret = 0; then
+               return 0
+       fi
+
+       setup_reflog_action
+       init_basic_state
+
+       if test -z "$rebase_root"
+       then
+               mkdir "$rewritten" &&
+               for c in $(git merge-base --all $orig_head $upstream)
+               do
+                       echo $onto > "$rewritten"/$c ||
+                               die "$(gettext "Could not init rewritten commits")"
+               done
+       else
+               mkdir "$rewritten" &&
+               echo $onto > "$rewritten"/root ||
+                       die "$(gettext "Could not init rewritten commits")"
+       fi
+
+       init_revisions_and_shortrevisions
+
+       format=$(git config --get rebase.instructionFormat)
+       # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
+       git rev-list --format="%m%H ${format:-%s}" \
+               --reverse --left-right --topo-order \
+               $revisions ${restrict_revision+^$restrict_revision} | \
+               sed -n "s/^>//p" |
+       while read -r sha1 rest
+       do
+               if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
+               then
+                       comment_out="$comment_char "
+               else
+                       comment_out=
+               fi
+
+               if test -z "$rebase_root"
+               then
+                       preserve=t
+                       for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
+                       do
+                               if test -f "$rewritten"/$p
+                               then
+                                       preserve=f
+                               fi
+                       done
+               else
+                       preserve=f
+               fi
+               if test f = "$preserve"
+               then
+                       touch "$rewritten"/$sha1
+                       printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
+               fi
+       done
+
+       # Watch for commits that been dropped by --cherry-pick
+       mkdir "$dropped"
+       # Save all non-cherry-picked changes
+       git rev-list $revisions --left-right --cherry-pick | \
+               sed -n "s/^>//p" > "$state_dir"/not-cherry-picks
+       # Now all commits and note which ones are missing in
+       # not-cherry-picks and hence being dropped
+       git rev-list $revisions |
+       while read rev
+       do
+               if test -f "$rewritten"/$rev &&
+                  ! sane_grep "$rev" "$state_dir"/not-cherry-picks >/dev/null
+               then
+                       # Use -f2 because if rev-list is telling us this commit is
+                       # not worthwhile, we don't want to track its multiple heads,
+                       # just the history of its first-parent for others that will
+                       # be rebasing on top of it
+                       git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev
+                       sha1=$(git rev-list -1 $rev)
+                       sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
+                       rm "$rewritten"/$rev
+               fi
+       done
+
+       complete_action
+}
index 40be59ecc4704da05a0e153f1709d4af2c62cd18..19bdebb48025e236c596d554768012951d348632 100755 (executable)
@@ -207,7 +207,14 @@ run_specific_rebase () {
                autosquash=
        fi
        . git-rebase--$type
-       git_rebase__$type${preserve_merges:+__preserve_merges}
+
+       if test -z "$preserve_merges"
+       then
+               git_rebase__$type
+       else
+               git_rebase__preserve_merges
+       fi
+
        ret=$?
        if test $ret -eq 0
        then
@@ -239,7 +246,12 @@ then
        state_dir="$apply_dir"
 elif test -d "$merge_dir"
 then
-       if test -f "$merge_dir"/interactive
+       if test -d "$merge_dir"/rewritten
+       then
+               type=preserve-merges
+               interactive_rebase=explicit
+               preserve_merges=t
+       elif test -f "$merge_dir"/interactive
        then
                type=interactive
                interactive_rebase=explicit
@@ -402,14 +414,14 @@ if test -n "$action"
 then
        test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
        # Only interactive rebase uses detailed reflog messages
-       if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
+       if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase
        then
                GIT_REFLOG_ACTION="rebase -i ($action)"
                export GIT_REFLOG_ACTION
        fi
 fi
 
-if test "$action" = "edit-todo" && test "$type" != "interactive"
+if test "$action" = "edit-todo" && test -z "$interactive_rebase"
 then
        die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
 fi
@@ -487,7 +499,13 @@ fi
 
 if test -n "$interactive_rebase"
 then
-       type=interactive
+       if test -z "$preserve_merges"
+       then
+               type=interactive
+       else
+               type=preserve-merges
+       fi
+
        state_dir="$merge_dir"
 elif test -n "$do_merge"
 then
@@ -647,7 +665,7 @@ require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 # but this should be done only when upstream and onto are the same
 # and if this is not an interactive rebase.
 mb=$(git merge-base "$onto" "$orig_head")
-if test "$type" != interactive && test "$upstream" = "$onto" &&
+if test -z "$interactive_rebase" && test "$upstream" = "$onto" &&
        test "$mb" = "$onto" && test -z "$restrict_revision" &&
        # linear history?
        ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
@@ -691,7 +709,7 @@ then
        GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
 fi
 
-test "$type" = interactive && run_specific_rebase
+test -n "$interactive_rebase" && run_specific_rebase
 
 # Detach HEAD and reset the tree
 say "$(gettext "First, rewinding head to replay your work on top of it...")"
index 78073cd87d1b1775bb4f4264557aba18c73c5911..8b5ad59bdee39eba4fc56b28d6771a291dac55f0 100755 (executable)
@@ -335,45 +335,7 @@ cmd_foreach()
                shift
        done
 
-       toplevel=$(pwd)
-
-       # dup stdin so that it can be restored when running the external
-       # command in the subshell (and a recursive call to this function)
-       exec 3<&0
-
-       {
-               git submodule--helper list --prefix "$wt_prefix" ||
-               echo "#unmatched" $?
-       } |
-       while read -r mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode" "$sha1"
-               if test -e "$sm_path"/.git
-               then
-                       displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
-                       say "$(eval_gettext "Entering '\$displaypath'")"
-                       name=$(git submodule--helper name "$sm_path")
-                       (
-                               prefix="$prefix$sm_path/"
-                               sanitize_submodule_env
-                               cd "$sm_path" &&
-                               sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") &&
-                               # we make $path available to scripts ...
-                               path=$sm_path &&
-                               if test $# -eq 1
-                               then
-                                       eval "$1"
-                               else
-                                       "$@"
-                               fi &&
-                               if test -n "$recursive"
-                               then
-                                       cmd_foreach "--recursive" "$@"
-                               fi
-                       ) <&3 3<&- ||
-                       die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
 }
 
 #
@@ -615,6 +577,11 @@ cmd_update()
                        die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
                fi
 
+               if ! $(git config -f "$(git rev-parse --git-common-dir)/modules/$name/config" core.worktree) 2>/dev/null
+               then
+                       git submodule--helper connect-gitdir-workingtree "$name" "$sm_path"
+               fi
+
                if test "$subsha1" != "$sha1" || test -n "$force"
                then
                        subforce=$force
diff --git a/git.c b/git.c
index c2f48d53dd4aaba0752aa5f2e6633242d320b248..3fded745195a603d15da399823e060bb75fb26ef 100644 (file)
--- a/git.c
+++ b/git.c
@@ -267,7 +267,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                } else if (!strcmp(cmd, "--shallow-file")) {
                        (*argv)++;
                        (*argc)--;
-                       set_alternate_shallow_file((*argv)[0], 1);
+                       set_alternate_shallow_file(the_repository, (*argv)[0], 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
@@ -537,6 +537,7 @@ static struct cmd_struct commands[] = {
        { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
        { "show", cmd_show, RUN_SETUP },
        { "show-branch", cmd_show_branch, RUN_SETUP },
+       { "show-index", cmd_show_index },
        { "show-ref", cmd_show_ref, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
diff --git a/grep.c b/grep.c
index 45ec7e636c004dbd35d4e0d05c243bc1d1479d19..7e2f440955a01ae305664df37f77ed87a55c2b06 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,18 +1,31 @@
 #include "cache.h"
 #include "config.h"
 #include "grep.h"
+#include "object-store.h"
 #include "userdiff.h"
 #include "xdiff-interface.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "commit.h"
 #include "quote.h"
+#include "help.h"
 
 static int grep_source_load(struct grep_source *gs);
 static int grep_source_is_binary(struct grep_source *gs);
 
 static struct grep_opt grep_defaults;
 
+static const char *color_grep_slots[] = {
+       [GREP_COLOR_CONTEXT]        = "context",
+       [GREP_COLOR_FILENAME]       = "filename",
+       [GREP_COLOR_FUNCTION]       = "function",
+       [GREP_COLOR_LINENO]         = "lineNumber",
+       [GREP_COLOR_MATCH_CONTEXT]  = "matchContext",
+       [GREP_COLOR_MATCH_SELECTED] = "matchSelected",
+       [GREP_COLOR_SELECTED]       = "selected",
+       [GREP_COLOR_SEP]            = "separator",
+};
+
 static void std_output(struct grep_opt *opt, const void *buf, size_t size)
 {
        fwrite(buf, size, 1, stdout);
@@ -42,14 +55,14 @@ void init_grep_defaults(void)
        opt->pathname = 1;
        opt->max_depth = -1;
        opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
-       color_set(opt->color_context, "");
-       color_set(opt->color_filename, "");
-       color_set(opt->color_function, "");
-       color_set(opt->color_lineno, "");
-       color_set(opt->color_match_context, GIT_COLOR_BOLD_RED);
-       color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);
-       color_set(opt->color_selected, "");
-       color_set(opt->color_sep, GIT_COLOR_CYAN);
+       color_set(opt->colors[GREP_COLOR_CONTEXT], "");
+       color_set(opt->colors[GREP_COLOR_FILENAME], "");
+       color_set(opt->colors[GREP_COLOR_FUNCTION], "");
+       color_set(opt->colors[GREP_COLOR_LINENO], "");
+       color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED);
+       color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED);
+       color_set(opt->colors[GREP_COLOR_SELECTED], "");
+       color_set(opt->colors[GREP_COLOR_SEP], GIT_COLOR_CYAN);
        opt->color = -1;
        opt->output = std_output;
 }
@@ -69,6 +82,8 @@ static int parse_pattern_type_arg(const char *opt, const char *arg)
        die("bad %s argument: %s", opt, arg);
 }
 
+define_list_config_array_extra(color_grep_slots, {"match"});
+
 /*
  * Read the configuration file once and store it in
  * the grep_defaults template.
@@ -76,7 +91,7 @@ static int parse_pattern_type_arg(const char *opt, const char *arg)
 int grep_config(const char *var, const char *value, void *cb)
 {
        struct grep_opt *opt = &grep_defaults;
-       char *color = NULL;
+       const char *slot;
 
        if (userdiff_config(var, value) < 0)
                return -1;
@@ -103,32 +118,18 @@ int grep_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "color.grep"))
                opt->color = git_config_colorbool(var, value);
-       else if (!strcmp(var, "color.grep.context"))
-               color = opt->color_context;
-       else if (!strcmp(var, "color.grep.filename"))
-               color = opt->color_filename;
-       else if (!strcmp(var, "color.grep.function"))
-               color = opt->color_function;
-       else if (!strcmp(var, "color.grep.linenumber"))
-               color = opt->color_lineno;
-       else if (!strcmp(var, "color.grep.matchcontext"))
-               color = opt->color_match_context;
-       else if (!strcmp(var, "color.grep.matchselected"))
-               color = opt->color_match_selected;
-       else if (!strcmp(var, "color.grep.selected"))
-               color = opt->color_selected;
-       else if (!strcmp(var, "color.grep.separator"))
-               color = opt->color_sep;
-       else if (!strcmp(var, "color.grep.match")) {
-               int rc = 0;
-               if (!value)
-                       return config_error_nonbool(var);
-               rc |= color_parse(value, opt->color_match_context);
-               rc |= color_parse(value, opt->color_match_selected);
-               return rc;
-       }
-
-       if (color) {
+       if (!strcmp(var, "color.grep.match")) {
+               if (grep_config("color.grep.matchcontext", value, cb) < 0)
+                       return -1;
+               if (grep_config("color.grep.matchselected", value, cb) < 0)
+                       return -1;
+       } else if (skip_prefix(var, "color.grep.", &slot)) {
+               int i = LOOKUP_CONFIG(color_grep_slots, slot);
+               char *color;
+
+               if (i < 0)
+                       return -1;
+               color = opt->colors[i];
                if (!value)
                        return config_error_nonbool(var);
                return color_parse(value, color);
@@ -144,6 +145,7 @@ int grep_config(const char *var, const char *value, void *cb)
 void grep_init(struct grep_opt *opt, const char *prefix)
 {
        struct grep_opt *def = &grep_defaults;
+       int i;
 
        memset(opt, 0, sizeof(*opt));
        opt->prefix = prefix;
@@ -160,14 +162,8 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        opt->relative = def->relative;
        opt->output = def->output;
 
-       color_set(opt->color_context, def->color_context);
-       color_set(opt->color_filename, def->color_filename);
-       color_set(opt->color_function, def->color_function);
-       color_set(opt->color_lineno, def->color_lineno);
-       color_set(opt->color_match_context, def->color_match_context);
-       color_set(opt->color_match_selected, def->color_match_selected);
-       color_set(opt->color_selected, def->color_selected);
-       color_set(opt->color_sep, def->color_sep);
+       for (i = 0; i < NR_GREP_COLORS; i++)
+               color_set(opt->colors[i], def->colors[i]);
 }
 
 static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
@@ -1098,12 +1094,12 @@ static void output_sep(struct grep_opt *opt, char sign)
        if (opt->null_following_name)
                opt->output(opt, "\0", 1);
        else
-               output_color(opt, &sign, 1, opt->color_sep);
+               output_color(opt, &sign, 1, opt->colors[GREP_COLOR_SEP]);
 }
 
 static void show_name(struct grep_opt *opt, const char *name)
 {
-       output_color(opt, name, strlen(name), opt->color_filename);
+       output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
 }
 
@@ -1370,28 +1366,28 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        } else if (opt->pre_context || opt->post_context || opt->funcbody) {
                if (opt->last_shown == 0) {
                        if (opt->show_hunk_mark) {
-                               output_color(opt, "--", 2, opt->color_sep);
+                               output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
                                opt->output(opt, "\n", 1);
                        }
                } else if (lno > opt->last_shown + 1) {
-                       output_color(opt, "--", 2, opt->color_sep);
+                       output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
                        opt->output(opt, "\n", 1);
                }
        }
        if (opt->heading && opt->last_shown == 0) {
-               output_color(opt, name, strlen(name), opt->color_filename);
+               output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
                opt->output(opt, "\n", 1);
        }
        opt->last_shown = lno;
 
        if (!opt->heading && opt->pathname) {
-               output_color(opt, name, strlen(name), opt->color_filename);
+               output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
                output_sep(opt, sign);
        }
        if (opt->linenum) {
                char buf[32];
                xsnprintf(buf, sizeof(buf), "%d", lno);
-               output_color(opt, buf, strlen(buf), opt->color_lineno);
+               output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_LINENO]);
                output_sep(opt, sign);
        }
        if (opt->color) {
@@ -1401,15 +1397,15 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
                int eflags = 0;
 
                if (sign == ':')
-                       match_color = opt->color_match_selected;
+                       match_color = opt->colors[GREP_COLOR_MATCH_SELECTED];
                else
-                       match_color = opt->color_match_context;
+                       match_color = opt->colors[GREP_COLOR_MATCH_CONTEXT];
                if (sign == ':')
-                       line_color = opt->color_selected;
+                       line_color = opt->colors[GREP_COLOR_SELECTED];
                else if (sign == '-')
-                       line_color = opt->color_context;
+                       line_color = opt->colors[GREP_COLOR_CONTEXT];
                else if (sign == '=')
-                       line_color = opt->color_function;
+                       line_color = opt->colors[GREP_COLOR_FUNCTION];
                *eol = '\0';
                while (next_match(opt, bol, eol, ctx, &match, eflags)) {
                        if (match.rm_so == match.rm_eo)
@@ -1816,7 +1812,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                        if (binary_match_only) {
                                opt->output(opt, "Binary file ", 12);
                                output_color(opt, gs->name, strlen(gs->name),
-                                            opt->color_filename);
+                                            opt->colors[GREP_COLOR_FILENAME]);
                                opt->output(opt, " matches\n", 9);
                                return 1;
                        }
@@ -1890,7 +1886,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                char buf[32];
                if (opt->pathname) {
                        output_color(opt, gs->name, strlen(gs->name),
-                                    opt->color_filename);
+                                    opt->colors[GREP_COLOR_FILENAME]);
                        output_sep(opt, ':');
                }
                xsnprintf(buf, sizeof(buf), "%u\n", count);
diff --git a/grep.h b/grep.h
index 399381c908c2c93cbcf447498fb435e517dddde0..ed25be271b5b6a002fdb582fd5c11a499a4f0c94 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -62,6 +62,18 @@ enum grep_header_field {
        GREP_HEADER_FIELD_MAX
 };
 
+enum grep_color {
+       GREP_COLOR_CONTEXT,
+       GREP_COLOR_FILENAME,
+       GREP_COLOR_FUNCTION,
+       GREP_COLOR_LINENO,
+       GREP_COLOR_MATCH_CONTEXT,
+       GREP_COLOR_MATCH_SELECTED,
+       GREP_COLOR_SELECTED,
+       GREP_COLOR_SEP,
+       NR_GREP_COLORS
+};
+
 struct grep_pat {
        struct grep_pat *next;
        const char *origin;
@@ -155,14 +167,7 @@ struct grep_opt {
        int funcbody;
        int extended_regexp_option;
        int pattern_type_option;
-       char color_context[COLOR_MAXLEN];
-       char color_filename[COLOR_MAXLEN];
-       char color_function[COLOR_MAXLEN];
-       char color_lineno[COLOR_MAXLEN];
-       char color_match_context[COLOR_MAXLEN];
-       char color_match_selected[COLOR_MAXLEN];
-       char color_selected[COLOR_MAXLEN];
-       char color_sep[COLOR_MAXLEN];
+       char colors[NR_GREP_COLORS][COLOR_MAXLEN];
        unsigned pre_context;
        unsigned post_context;
        unsigned last_shown;
diff --git a/help.c b/help.c
index dd35fcc133094e4dc89898463868e89162637076..3ebf0568dba1f20039b6e4d02c18c652078ad2cb 100644 (file)
--- a/help.c
+++ b/help.c
@@ -409,6 +409,90 @@ void list_common_guides_help(void)
        putchar('\n');
 }
 
+struct slot_expansion {
+       const char *prefix;
+       const char *placeholder;
+       void (*fn)(struct string_list *list, const char *prefix);
+       int found;
+};
+
+void list_config_help(int for_human)
+{
+       struct slot_expansion slot_expansions[] = {
+               { "advice", "*", list_config_advices },
+               { "color.branch", "<slot>", list_config_color_branch_slots },
+               { "color.decorate", "<slot>", list_config_color_decorate_slots },
+               { "color.diff", "<slot>", list_config_color_diff_slots },
+               { "color.grep", "<slot>", list_config_color_grep_slots },
+               { "color.interactive", "<slot>", list_config_color_interactive_slots },
+               { "color.status", "<slot>", list_config_color_status_slots },
+               { "fsck", "<msg-id>", list_config_fsck_msg_ids },
+               { "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },
+               { NULL, NULL, NULL }
+       };
+       const char **p;
+       struct slot_expansion *e;
+       struct string_list keys = STRING_LIST_INIT_DUP;
+       int i;
+
+       for (p = config_name_list; *p; p++) {
+               const char *var = *p;
+               struct strbuf sb = STRBUF_INIT;
+
+               for (e = slot_expansions; e->prefix; e++) {
+
+                       strbuf_reset(&sb);
+                       strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder);
+                       if (!strcasecmp(var, sb.buf)) {
+                               e->fn(&keys, e->prefix);
+                               e->found++;
+                               break;
+                       }
+               }
+               strbuf_release(&sb);
+               if (!e->prefix)
+                       string_list_append(&keys, var);
+       }
+
+       for (e = slot_expansions; e->prefix; e++)
+               if (!e->found)
+                       BUG("slot_expansion %s.%s is not used",
+                           e->prefix, e->placeholder);
+
+       string_list_sort(&keys);
+       for (i = 0; i < keys.nr; i++) {
+               const char *var = keys.items[i].string;
+               const char *wildcard, *tag, *cut;
+
+               if (for_human) {
+                       puts(var);
+                       continue;
+               }
+
+               wildcard = strchr(var, '*');
+               tag = strchr(var, '<');
+
+               if (!wildcard && !tag) {
+                       puts(var);
+                       continue;
+               }
+
+               if (wildcard && !tag)
+                       cut = wildcard;
+               else if (!wildcard && tag)
+                       cut = tag;
+               else
+                       cut = wildcard < tag ? wildcard : tag;
+
+               /*
+                * We may produce duplicates, but that's up to
+                * git-completion.bash to handle
+                */
+               printf("%.*s\n", (int)(cut - var), var);
+       }
+       string_list_clear(&keys, 0);
+}
+
 void list_all_cmds_help(void)
 {
        print_cmd_by_category(main_categories);
diff --git a/help.h b/help.h
index 3b38292a1b317ba32d934be6006cb86e0e7d0d94..f8b15323a60dd318820c7295a4eebbcbd1de43dc 100644 (file)
--- a/help.h
+++ b/help.h
@@ -1,7 +1,8 @@
 #ifndef HELP_H
 #define HELP_H
 
-struct string_list;
+#include "string-list.h"
+#include "strbuf.h"
 
 struct cmdnames {
        int alloc;
@@ -21,6 +22,7 @@ static inline void mput_char(char c, unsigned int num)
 extern void list_common_cmds_help(void);
 extern void list_all_cmds_help(void);
 extern void list_common_guides_help(void);
+extern void list_config_help(int for_human);
 
 extern void list_all_main_cmds(struct string_list *list);
 extern void list_all_other_cmds(struct string_list *list);
@@ -42,4 +44,45 @@ extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, stru
  * ref to the command, to give suggested "correct" refs.
  */
 extern void help_unknown_ref(const char *ref, const char *cmd, const char *error);
+
+static inline void list_config_item(struct string_list *list,
+                                   const char *prefix,
+                                   const char *str)
+{
+       string_list_append_nodup(list, xstrfmt("%s.%s", prefix, str));
+}
+
+#define define_list_config_array(array)                                        \
+void list_config_##array(struct string_list *list, const char *prefix) \
+{                                                                      \
+       int i;                                                          \
+       for (i = 0; i < ARRAY_SIZE(array); i++)                         \
+               if (array[i])                                           \
+                       list_config_item(list, prefix, array[i]);       \
+}                                                                      \
+struct string_list
+
+#define define_list_config_array_extra(array, values)                  \
+void list_config_##array(struct string_list *list, const char *prefix) \
+{                                                                      \
+       int i;                                                          \
+       static const char *extra[] = values;                            \
+       for (i = 0; i < ARRAY_SIZE(extra); i++)                         \
+               list_config_item(list, prefix, extra[i]);               \
+       for (i = 0; i < ARRAY_SIZE(array); i++)                         \
+               if (array[i])                                           \
+                       list_config_item(list, prefix, array[i]);       \
+}                                                                      \
+struct string_list
+
+/* These are actually scattered over many C files */
+void list_config_advices(struct string_list *list, const char *prefix);
+void list_config_color_branch_slots(struct string_list *list, const char *prefix);
+void list_config_color_decorate_slots(struct string_list *list, const char *prefix);
+void list_config_color_diff_slots(struct string_list *list, const char *prefix);
+void list_config_color_grep_slots(struct string_list *list, const char *prefix);
+void list_config_color_interactive_slots(struct string_list *list, const char *prefix);
+void list_config_color_status_slots(struct string_list *list, const char *prefix);
+void list_config_fsck_msg_ids(struct string_list *list, const char *prefix);
+
 #endif /* HELP_H */
index 6a3cc985c45e751dd0c3f37880cf0bfd205ef088..c0e2bd6a06a27b6056364c43b003d74539fd6cad 100644 (file)
@@ -146,6 +146,8 @@ void partial_clone_get_default_filter_spec(
        /*
         * Parse default value, but silently ignore it if it is invalid.
         */
+       if (!core_partial_clone_filter_default)
+               return;
        gently_parse_list_objects_filter(filter_options,
                                         core_partial_clone_filter_default,
                                         NULL);
index 5b14d2711ad3791169f5d8cbc03836b4254bd55c..a0ba78b20cc99bfcd2c41abd4f312b5649a2b9cd 100644 (file)
@@ -11,6 +11,7 @@
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "oidset.h"
+#include "object-store.h"
 
 /* Remember to update object flag allocation in object.h */
 /*
index 3eec510357337f5e33eea08a05faa22373d98c07..3e5e1992eb0cd1c535ed42e9729941403ca89054 100644 (file)
@@ -10,6 +10,7 @@
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "packfile.h"
+#include "object-store.h"
 
 static void process_blob(struct rev_info *revs,
                         struct blob *blob,
index 4aef85331e0b696d0373cf1bdb0743a7e6b58c36..4a3907fea02d50e1074ca1b785780a0753e77468 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "diff.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tag.h"
 #include "graph.h"
@@ -12,6 +13,7 @@
 #include "gpg-interface.h"
 #include "sequencer.h"
 #include "line-log.h"
+#include "help.h"
 
 static struct decoration name_decoration = { "object names" };
 static int decoration_loaded;
@@ -27,6 +29,15 @@ static char decoration_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_BOLD_BLUE,    /* GRAFTED */
 };
 
+static const char *color_decorate_slots[] = {
+       [DECORATION_REF_LOCAL]  = "branch",
+       [DECORATION_REF_REMOTE] = "remoteBranch",
+       [DECORATION_REF_TAG]    = "tag",
+       [DECORATION_REF_STASH]  = "stash",
+       [DECORATION_REF_HEAD]   = "HEAD",
+       [DECORATION_GRAFTED]    = "grafted",
+};
+
 static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
 {
        if (want_color(decorate_use_color))
@@ -34,34 +45,11 @@ static const char *decorate_get_color(int decorate_use_color, enum decoration_ty
        return "";
 }
 
-static int parse_decorate_color_slot(const char *slot)
-{
-       /*
-        * We're comparing with 'ignore-case' on
-        * (because config.c sets them all tolower),
-        * but let's match the letters in the literal
-        * string values here with how they are
-        * documented in Documentation/config.txt, for
-        * consistency.
-        *
-        * We love being consistent, don't we?
-        */
-       if (!strcasecmp(slot, "branch"))
-               return DECORATION_REF_LOCAL;
-       if (!strcasecmp(slot, "remoteBranch"))
-               return DECORATION_REF_REMOTE;
-       if (!strcasecmp(slot, "tag"))
-               return DECORATION_REF_TAG;
-       if (!strcasecmp(slot, "stash"))
-               return DECORATION_REF_STASH;
-       if (!strcasecmp(slot, "HEAD"))
-               return DECORATION_REF_HEAD;
-       return -1;
-}
+define_list_config_array(color_decorate_slots);
 
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value)
 {
-       int slot = parse_decorate_color_slot(slot_name);
+       int slot = LOOKUP_CONFIG(color_decorate_slots, slot_name);
        if (slot < 0)
                return 0;
        if (!value)
@@ -295,8 +283,12 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       if (opt->show_source && commit->util)
-               fprintf(opt->diffopt.file, "\t%s", (char *) commit->util);
+       if (opt->sources) {
+               char **slot = revision_sources_peek(opt->sources, commit);
+
+               if (slot && *slot)
+                       fprintf(opt->diffopt.file, "\t%s", *slot);
+       }
        if (!opt->show_decorations)
                return;
        format_decorations(&sb, commit, opt->diffopt.use_color);
index 13f0d2884e25edef3fde5bdf72b89a770111e472..962fd86d6d7067e84bc0dbf499cc0c47b6ad3c11 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "string-list.h"
 #include "mailmap.h"
+#include "object-store.h"
 
 #define DEBUG_MAILMAP 0
 #if DEBUG_MAILMAP
index 72cc2baa3f96b2cbfa296335c7f0ff1094b6e96c..4cdeff53e1e8533b0dba168c19187353901253ea 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "tree.h"
 #include "tree-walk.h"
+#include "object-store.h"
 
 static int score_missing(unsigned mode, const char *path)
 {
index fa49c17287f4120b4bbb75acc6b92b3d339710cd..fabb8c19ce983962b45c415f66c11236fa906bfe 100644 (file)
@@ -4,6 +4,7 @@
 #include "ll-merge.h"
 #include "blob.h"
 #include "merge-blobs.h"
+#include "object-store.h"
 
 static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
 {
index f110e1c5ecbd6254b25785ecb49f29cb7bb75ed2..113c1d69625935a2f3c476af0ec0ca92201417e7 100644 (file)
@@ -8,6 +8,7 @@
 #include "advice.h"
 #include "lockfile.h"
 #include "cache-tree.h"
+#include "object-store.h"
 #include "commit.h"
 #include "blob.h"
 #include "builtin.h"
@@ -15,6 +16,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "tag.h"
+#include "alloc.h"
 #include "unpack-trees.h"
 #include "string-list.h"
 #include "xdiff-interface.h"
@@ -160,7 +162,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
 
 static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
 {
-       struct commit *commit = alloc_commit_node();
+       struct commit *commit = alloc_commit_node(the_repository);
 
        set_merge_remote_desc(commit, comment, (struct object *)commit);
        commit->maybe_tree = tree;
@@ -181,7 +183,7 @@ static int oid_eq(const struct object_id *a, const struct object_id *b)
 
 enum rename_type {
        RENAME_NORMAL = 0,
-       RENAME_DIR,
+       RENAME_VIA_DIR,
        RENAME_DELETE,
        RENAME_ONE_FILE_TO_ONE,
        RENAME_ONE_FILE_TO_TWO,
@@ -286,10 +288,12 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)
 
 static void output_commit_title(struct merge_options *o, struct commit *commit)
 {
+       struct merge_remote_desc *desc;
+
        strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
-       if (commit->util)
-               strbuf_addf(&o->obuf, "virtual %s\n",
-                       merge_remote_util(commit)->name);
+       desc = merge_remote_util(commit);
+       if (desc)
+               strbuf_addf(&o->obuf, "virtual %s\n", desc->name);
        else {
                strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
                                         DEFAULT_ABBREV);
@@ -309,8 +313,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
 }
 
 static int add_cacheinfo(struct merge_options *o,
-               unsigned int mode, const struct object_id *oid,
-               const char *path, int stage, int refresh, int options)
+                        unsigned int mode, const struct object_id *oid,
+                        const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
        int ret;
@@ -417,8 +421,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 }
 
 static int save_files_dirs(const struct object_id *oid,
-               struct strbuf *base, const char *path,
-               unsigned int mode, int stage, void *context)
+                          struct strbuf *base, const char *path,
+                          unsigned int mode, int stage, void *context)
 {
        struct path_hashmap_entry *entry;
        int baselen = base->len;
@@ -539,7 +543,7 @@ static void record_df_conflict_files(struct merge_options *o,
                                     struct string_list *entries)
 {
        /* If there is a D/F conflict and the file for such a conflict
-        * currently exist in the working tree, we want to allow it to be
+        * currently exists in the working tree, we want to allow it to be
         * removed to make room for the corresponding directory if needed.
         * The files underneath the directories of such D/F conflicts will
         * be processed before the corresponding file involved in the D/F
@@ -913,7 +917,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)
         */
        if (would_lose_untracked(path))
                return err(o, _("refusing to lose untracked file at '%s'"),
-                            path);
+                          path);
 
        /* Successful unlink is good.. */
        if (!unlink(path))
@@ -992,16 +996,16 @@ static int update_file_flags(struct merge_options *o,
                        unlink(path);
                        if (symlink(lnk, path))
                                ret = err(o, _("failed to symlink '%s': %s"),
-                                       path, strerror(errno));
+                                         path, strerror(errno));
                        free(lnk);
                } else
                        ret = err(o,
                                  _("do not know what to do with %06o %s '%s'"),
                                  mode, oid_to_hex(oid), path);
- free_buf:
      free_buf:
                free(buf);
        }
- update_index:
+update_index:
        if (!ret && update_cache)
                if (add_cacheinfo(o, mode, oid, path, 0, update_wd,
                                  ADD_CACHE_OK_TO_ADD))
@@ -1090,7 +1094,7 @@ static int merge_3way(struct merge_options *o,
 }
 
 static int find_first_merges(struct object_array *result, const char *path,
-               struct commit *a, struct commit *b)
+                            struct commit *a, struct commit *b)
 {
        int i, j;
        struct object_array merges = OBJECT_ARRAY_INIT;
@@ -1108,7 +1112,7 @@ static int find_first_merges(struct object_array *result, const char *path,
 
        /* get all revisions that merge commit a */
        xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
-                       oid_to_hex(&a->object.oid));
+                 oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
        /* FIXME: can't handle linked worktrees in submodules yet */
@@ -1250,12 +1254,12 @@ static int merge_submodule(struct merge_options *o,
                output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
                print_commit((struct commit *) merges.objects[0].item);
                output(o, 2, _(
-                       "If this is correct simply add it to the index "
-                       "for example\n"
-                       "by using:\n\n"
-                       "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
-                       "which will accept this suggestion.\n"),
-                       oid_to_hex(&merges.objects[0].item->oid), path);
+                      "If this is correct simply add it to the index "
+                      "for example\n"
+                      "by using:\n\n"
+                      "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                      "which will accept this suggestion.\n"),
+                      oid_to_hex(&merges.objects[0].item->oid), path);
                break;
 
        default:
@@ -1332,10 +1336,10 @@ static int merge_file_1(struct merge_options *o,
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
                        result->clean = merge_submodule(o, &result->oid,
-                                                      one->path,
-                                                      &one->oid,
-                                                      &a->oid,
-                                                      &b->oid);
+                                                       one->path,
+                                                       &one->oid,
+                                                       &a->oid,
+                                                       &b->oid);
                } else if (S_ISLNK(a->mode)) {
                        switch (o->recursive_variant) {
                        case MERGE_RECURSIVE_NORMAL:
@@ -1410,11 +1414,17 @@ static int merge_file_one(struct merge_options *o,
        return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi);
 }
 
-static int conflict_rename_dir(struct merge_options *o,
-                              struct diff_filepair *pair,
-                              const char *rename_branch,
-                              const char *other_branch)
+static int handle_rename_via_dir(struct merge_options *o,
+                                struct diff_filepair *pair,
+                                const char *rename_branch,
+                                const char *other_branch)
 {
+       /*
+        * Handle file adds that need to be renamed due to directory rename
+        * detection.  This differs from handle_rename_normal, because
+        * there is no content merge to do; just move the file into the
+        * desired final location.
+        */
        const struct diff_filespec *dest = pair->two;
 
        if (!o->call_depth && would_lose_untracked(dest->path)) {
@@ -1443,13 +1453,13 @@ static int conflict_rename_dir(struct merge_options *o,
 }
 
 static int handle_change_delete(struct merge_options *o,
-                                const char *path, const char *old_path,
-                                const struct object_id *o_oid, int o_mode,
-                                const struct object_id *changed_oid,
-                                int changed_mode,
-                                const char *change_branch,
-                                const char *delete_branch,
-                                const char *change, const char *change_past)
+                               const char *path, const char *old_path,
+                               const struct object_id *o_oid, int o_mode,
+                               const struct object_id *changed_oid,
+                               int changed_mode,
+                               const char *change_branch,
+                               const char *delete_branch,
+                               const char *change, const char *change_past)
 {
        char *alt_path = NULL;
        const char *update_path = path;
@@ -1470,6 +1480,21 @@ static int handle_change_delete(struct merge_options *o,
                if (!ret)
                        ret = update_file(o, 0, o_oid, o_mode, update_path);
        } else {
+               /*
+                * Despite the four nearly duplicate messages and argument
+                * lists below and the ugliness of the nested if-statements,
+                * having complete messages makes the job easier for
+                * translators.
+                *
+                * The slight variance among the cases is due to the fact
+                * that:
+                *   1) directory/file conflicts (in effect if
+                *      !alt_path) could cause us to need to write the
+                *      file to a different path.
+                *   2) renames (in effect if !old_path) could mean that
+                *      there are two names for the path that the user
+                *      may know the file by.
+                */
                if (!alt_path) {
                        if (!old_path) {
                                output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
@@ -1509,10 +1534,10 @@ static int handle_change_delete(struct merge_options *o,
        return ret;
 }
 
-static int conflict_rename_delete(struct merge_options *o,
-                                  struct diff_filepair *pair,
-                                  const char *rename_branch,
-                                  const char *delete_branch)
+static int handle_rename_delete(struct merge_options *o,
+                               struct diff_filepair *pair,
+                               const char *rename_branch,
+                               const char *delete_branch)
 {
        const struct diff_filespec *orig = pair->one;
        const struct diff_filespec *dest = pair->two;
@@ -1614,8 +1639,8 @@ static int handle_file(struct merge_options *o,
        return ret;
 }
 
-static int conflict_rename_rename_1to2(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+static int handle_rename_rename_1to2(struct merge_options *o,
+                                    struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
        struct diff_filespec *one = ci->pair1->one;
@@ -1676,8 +1701,8 @@ static int conflict_rename_rename_1to2(struct merge_options *o,
        return 0;
 }
 
-static int conflict_rename_rename_2to1(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+static int handle_rename_rename_2to1(struct merge_options *o,
+                                    struct rename_conflict_info *ci)
 {
        /* Two files, a & b, were renamed to the same thing, c. */
        struct diff_filespec *a = ci->pair1->one;
@@ -2419,7 +2444,7 @@ static void apply_directory_rename_modifications(struct merge_options *o,
         * "NOTE" in update_stages(), doing so will modify the current
         * in-memory index which will break calls to would_lose_untracked()
         * that we need to make.  Instead, we need to just make sure that
-        * the various conflict_rename_*() functions update the index
+        * the various handle_rename_*() functions update the index
         * explicitly rather than relying on unpack_trees() to have done it.
         */
        get_tree_entry(&tree->object.oid,
@@ -2692,7 +2717,7 @@ static int process_renames(struct merge_options *o,
 
                        if (oid_eq(&src_other.oid, &null_oid) &&
                            ren1->add_turned_into_rename) {
-                               setup_rename_conflict_info(RENAME_DIR,
+                               setup_rename_conflict_info(RENAME_VIA_DIR,
                                                           ren1->pair,
                                                           NULL,
                                                           branch1,
@@ -2823,12 +2848,12 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
        free(pairs);
 }
 
-static int handle_renames(struct merge_options *o,
-                         struct tree *common,
-                         struct tree *head,
-                         struct tree *merge,
-                         struct string_list *entries,
-                         struct rename_info *ri)
+static int detect_and_process_renames(struct merge_options *o,
+                                     struct tree *common,
+                                     struct tree *head,
+                                     struct tree *merge,
+                                     struct string_list *entries,
+                                     struct rename_info *ri)
 {
        struct diff_queue_struct *head_pairs, *merge_pairs;
        struct hashmap *dir_re_head, *dir_re_merge;
@@ -2904,7 +2929,8 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
 }
 
 static int read_oid_strbuf(struct merge_options *o,
-       const struct object_id *oid, struct strbuf *dst)
+                          const struct object_id *oid,
+                          struct strbuf *dst)
 {
        void *buf;
        enum object_type type;
@@ -2957,10 +2983,10 @@ static int blob_unchanged(struct merge_options *opt,
 }
 
 static int handle_modify_delete(struct merge_options *o,
-                                const char *path,
-                                struct object_id *o_oid, int o_mode,
-                                struct object_id *a_oid, int a_mode,
-                                struct object_id *b_oid, int b_mode)
+                               const char *path,
+                               struct object_id *o_oid, int o_mode,
+                               struct object_id *a_oid, int a_mode,
+                               struct object_id *b_oid, int b_mode)
 {
        const char *modify_branch, *delete_branch;
        struct object_id *changed_oid;
@@ -3098,12 +3124,12 @@ static int merge_content(struct merge_options *o,
        return !is_dirty && mfi.clean;
 }
 
-static int conflict_rename_normal(struct merge_options *o,
-                                 const char *path,
-                                 struct object_id *o_oid, unsigned int o_mode,
-                                 struct object_id *a_oid, unsigned int a_mode,
-                                 struct object_id *b_oid, unsigned int b_mode,
-                                 struct rename_conflict_info *ci)
+static int handle_rename_normal(struct merge_options *o,
+                               const char *path,
+                               struct object_id *o_oid, unsigned int o_mode,
+                               struct object_id *a_oid, unsigned int a_mode,
+                               struct object_id *b_oid, unsigned int b_mode,
+                               struct rename_conflict_info *ci)
 {
        /* Merge the content and write it out */
        return merge_content(o, path, was_dirty(o, path),
@@ -3130,37 +3156,37 @@ static int process_entry(struct merge_options *o,
                switch (conflict_info->rename_type) {
                case RENAME_NORMAL:
                case RENAME_ONE_FILE_TO_ONE:
-                       clean_merge = conflict_rename_normal(o,
-                                                            path,
-                                                            o_oid, o_mode,
-                                                            a_oid, a_mode,
-                                                            b_oid, b_mode,
-                                                            conflict_info);
+                       clean_merge = handle_rename_normal(o,
+                                                          path,
+                                                          o_oid, o_mode,
+                                                          a_oid, a_mode,
+                                                          b_oid, b_mode,
+                                                          conflict_info);
                        break;
-               case RENAME_DIR:
+               case RENAME_VIA_DIR:
                        clean_merge = 1;
-                       if (conflict_rename_dir(o,
-                                               conflict_info->pair1,
-                                               conflict_info->branch1,
-                                               conflict_info->branch2))
+                       if (handle_rename_via_dir(o,
+                                                 conflict_info->pair1,
+                                                 conflict_info->branch1,
+                                                 conflict_info->branch2))
                                clean_merge = -1;
                        break;
                case RENAME_DELETE:
                        clean_merge = 0;
-                       if (conflict_rename_delete(o,
-                                                  conflict_info->pair1,
-                                                  conflict_info->branch1,
-                                                  conflict_info->branch2))
+                       if (handle_rename_delete(o,
+                                                conflict_info->pair1,
+                                                conflict_info->branch1,
+                                                conflict_info->branch2))
                                clean_merge = -1;
                        break;
                case RENAME_ONE_FILE_TO_TWO:
                        clean_merge = 0;
-                       if (conflict_rename_rename_1to2(o, conflict_info))
+                       if (handle_rename_rename_1to2(o, conflict_info))
                                clean_merge = -1;
                        break;
                case RENAME_TWO_FILES_TO_ONE:
                        clean_merge = 0;
-                       if (conflict_rename_rename_2to1(o, conflict_info))
+                       if (handle_rename_rename_2to1(o, conflict_info))
                                clean_merge = -1;
                        break;
                default:
@@ -3300,8 +3326,8 @@ int merge_trees(struct merge_options *o,
                get_files_dirs(o, merge);
 
                entries = get_unmerged();
-               clean = handle_renames(o, common, head, merge, entries,
-                                      &re_info);
+               clean = detect_and_process_renames(o, common, head, merge,
+                                                  entries, &re_info);
                record_df_conflict_files(o, entries);
                if (clean < 0)
                        goto cleanup;
@@ -3325,7 +3351,7 @@ int merge_trees(struct merge_options *o,
                                    entries->items[i].string);
                }
 
-cleanup:
+       cleanup:
                final_cleanup_renames(&re_info);
 
                string_list_clear(entries, 1);
@@ -3493,14 +3519,14 @@ int merge_recursive_generic(struct merge_options *o,
                        struct commit *base;
                        if (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))
                                return err(o, _("Could not parse object '%s'"),
-                                       oid_to_hex(base_list[i]));
+                                          oid_to_hex(base_list[i]));
                        commit_list_insert(base, &ca);
                }
        }
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
-                       result);
+                               result);
        if (clean < 0) {
                rollback_lock_file(&lock);
                return clean;
index e61988e503b0b2097afeb6716123d88429c0717d..d5770031776283171d702886dc1ebb301bb6e6b1 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "notes-cache.h"
+#include "object-store.h"
 #include "commit.h"
 #include "refs.h"
 
index d613e06574aec31e0ecaf083b489b0465e5c27f1..9cc2ee16a8c04c43dabebd61b0b7d41627af40ee 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "commit.h"
 #include "refs.h"
+#include "object-store.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "xdiff-interface.h"
diff --git a/notes.c b/notes.c
index a386d450c4c812ef30d0fc661fe2c03e1d062a83..32d3dbcc1e74ce344da89b9aa2b9ca09f6e62d2b 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "notes.h"
+#include "object-store.h"
 #include "blob.h"
 #include "tree.h"
 #include "utf8.h"
index d683112fd7bbab5df7e8cd5656b497cb8fd2bda1..a3db17bbf5a70c9920181e26eeba5c348ad87671 100644 (file)
@@ -139,4 +139,121 @@ void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned cha
 
 void *map_sha1_file(struct repository *r, const unsigned char *sha1, unsigned long *size);
 
+extern void *read_object_file_extended(const struct object_id *oid,
+                                      enum object_type *type,
+                                      unsigned long *size, int lookup_replace);
+static inline void *read_object_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
+{
+       return read_object_file_extended(oid, type, size, 1);
+}
+
+/* Read and unpack an object file into memory, write memory to an object file */
+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
+
+extern int hash_object_file(const void *buf, unsigned long len,
+                           const char *type, struct object_id *oid);
+
+extern int write_object_file(const void *buf, unsigned long len,
+                            const char *type, struct object_id *oid);
+
+extern int hash_object_file_literally(const void *buf, unsigned long len,
+                                     const char *type, struct object_id *oid,
+                                     unsigned flags);
+
+extern int pretend_object_file(void *, unsigned long, enum object_type,
+                              struct object_id *oid);
+
+extern int force_object_loose(const struct object_id *oid, time_t mtime);
+
+/*
+ * Open the loose object at path, check its hash, and return the contents,
+ * type, and size. If the object is a blob, then "contents" may return NULL,
+ * to allow streaming of large blobs.
+ *
+ * Returns 0 on success, negative on error (details may be written to stderr).
+ */
+int read_loose_object(const char *path,
+                     const struct object_id *expected_oid,
+                     enum object_type *type,
+                     unsigned long *size,
+                     void **contents);
+
+/*
+ * Convenience for sha1_object_info_extended() with a NULL struct
+ * object_info. OBJECT_INFO_SKIP_CACHED is automatically set; pass
+ * nonzero flags to also set other flags.
+ */
+extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
+static inline int has_sha1_file(const unsigned char *sha1)
+{
+       return has_sha1_file_with_flags(sha1, 0);
+}
+
+/* Same as the above, except for struct object_id. */
+extern int has_object_file(const struct object_id *oid);
+extern int has_object_file_with_flags(const struct object_id *oid, int flags);
+
+/*
+ * Return true iff an alternate object database has a loose object
+ * with the specified name.  This function does not respect replace
+ * references.
+ */
+extern int has_loose_object_nonlocal(const struct object_id *);
+
+extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
+
+struct object_info {
+       /* Request */
+       enum object_type *typep;
+       unsigned long *sizep;
+       off_t *disk_sizep;
+       unsigned char *delta_base_sha1;
+       struct strbuf *type_name;
+       void **contentp;
+
+       /* Response */
+       enum {
+               OI_CACHED,
+               OI_LOOSE,
+               OI_PACKED,
+               OI_DBCACHED
+       } whence;
+       union {
+               /*
+                * struct {
+                *      ... Nothing to expose in this case
+                * } cached;
+                * struct {
+                *      ... Nothing to expose in this case
+                * } loose;
+                */
+               struct {
+                       struct packed_git *pack;
+                       off_t offset;
+                       unsigned int is_delta;
+               } packed;
+       } u;
+};
+
+/*
+ * Initializer for a "struct object_info" that wants no items. You may
+ * also memset() the memory to all-zeroes.
+ */
+#define OBJECT_INFO_INIT {NULL}
+
+/* Invoke lookup_replace_object() on the given hash */
+#define OBJECT_INFO_LOOKUP_REPLACE 1
+/* Allow reading from a loose object file of unknown/bogus type */
+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
+/* Do not check cached storage */
+#define OBJECT_INFO_SKIP_CACHED 4
+/* Do not retry packed storage after checking packed and loose storage */
+#define OBJECT_INFO_QUICK 8
+/* Do not check loose object */
+#define OBJECT_INFO_IGNORE_LOOSE 16
+
+int oid_object_info_extended(struct repository *r,
+                            const struct object_id *,
+                            struct object_info *, unsigned flags);
+
 #endif /* OBJECT_STORE_H */
index f7f4de3aaf6d42a6dde5811b37aa8239ac183754..9b0f819fae38bcc2fe6b15bfe8e5b9b17f7385bc 100644 (file)
--- a/object.c
+++ b/object.c
@@ -1,24 +1,23 @@
 #include "cache.h"
 #include "object.h"
 #include "replace-object.h"
+#include "object-store.h"
 #include "blob.h"
 #include "tree.h"
 #include "commit.h"
 #include "tag.h"
+#include "alloc.h"
 #include "object-store.h"
 #include "packfile.h"
 
-static struct object **obj_hash;
-static int nr_objs, obj_hash_size;
-
 unsigned int get_max_object_index(void)
 {
-       return obj_hash_size;
+       return the_repository->parsed_objects->obj_hash_size;
 }
 
 struct object *get_indexed_object(unsigned int idx)
 {
-       return obj_hash[idx];
+       return the_repository->parsed_objects->obj_hash[idx];
 }
 
 static const char *object_type_strings[] = {
@@ -90,15 +89,16 @@ struct object *lookup_object(const unsigned char *sha1)
        unsigned int i, first;
        struct object *obj;
 
-       if (!obj_hash)
+       if (!the_repository->parsed_objects->obj_hash)
                return NULL;
 
-       first = i = hash_obj(sha1, obj_hash_size);
-       while ((obj = obj_hash[i]) != NULL) {
+       first = i = hash_obj(sha1,
+                            the_repository->parsed_objects->obj_hash_size);
+       while ((obj = the_repository->parsed_objects->obj_hash[i]) != NULL) {
                if (!hashcmp(sha1, obj->oid.hash))
                        break;
                i++;
-               if (i == obj_hash_size)
+               if (i == the_repository->parsed_objects->obj_hash_size)
                        i = 0;
        }
        if (obj && i != first) {
@@ -107,7 +107,8 @@ struct object *lookup_object(const unsigned char *sha1)
                 * that we do not need to walk the hash table the next
                 * time we look for it.
                 */
-               SWAP(obj_hash[i], obj_hash[first]);
+               SWAP(the_repository->parsed_objects->obj_hash[i],
+                    the_repository->parsed_objects->obj_hash[first]);
        }
        return obj;
 }
@@ -117,29 +118,30 @@ struct object *lookup_object(const unsigned char *sha1)
  * power of 2 (but at least 32).  Copy the existing values to the new
  * hash map.
  */
-static void grow_object_hash(void)
+static void grow_object_hash(struct repository *r)
 {
        int i;
        /*
         * Note that this size must always be power-of-2 to match hash_obj
         * above.
         */
-       int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
+       int new_hash_size = r->parsed_objects->obj_hash_size < 32 ? 32 : 2 * r->parsed_objects->obj_hash_size;
        struct object **new_hash;
 
        new_hash = xcalloc(new_hash_size, sizeof(struct object *));
-       for (i = 0; i < obj_hash_size; i++) {
-               struct object *obj = obj_hash[i];
+       for (i = 0; i < r->parsed_objects->obj_hash_size; i++) {
+               struct object *obj = r->parsed_objects->obj_hash[i];
+
                if (!obj)
                        continue;
                insert_obj_hash(obj, new_hash, new_hash_size);
        }
-       free(obj_hash);
-       obj_hash = new_hash;
-       obj_hash_size = new_hash_size;
+       free(r->parsed_objects->obj_hash);
+       r->parsed_objects->obj_hash = new_hash;
+       r->parsed_objects->obj_hash_size = new_hash_size;
 }
 
-void *create_object(const unsigned char *sha1, void *o)
+void *create_object(struct repository *r, const unsigned char *sha1, void *o)
 {
        struct object *obj = o;
 
@@ -147,11 +149,12 @@ void *create_object(const unsigned char *sha1, void *o)
        obj->flags = 0;
        hashcpy(obj->oid.hash, sha1);
 
-       if (obj_hash_size - 1 <= nr_objs * 2)
-               grow_object_hash();
+       if (r->parsed_objects->obj_hash_size - 1 <= r->parsed_objects->nr_objs * 2)
+               grow_object_hash(r);
 
-       insert_obj_hash(obj, obj_hash, obj_hash_size);
-       nr_objs++;
+       insert_obj_hash(obj, r->parsed_objects->obj_hash,
+                       r->parsed_objects->obj_hash_size);
+       r->parsed_objects->nr_objs++;
        return obj;
 }
 
@@ -161,7 +164,7 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet)
                return obj;
        else if (obj->type == OBJ_NONE) {
                if (type == OBJ_COMMIT)
-                       ((struct commit *)obj)->index = alloc_commit_index();
+                       ((struct commit *)obj)->index = alloc_commit_index(the_repository);
                obj->type = type;
                return obj;
        }
@@ -178,7 +181,8 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
        if (!obj)
-               obj = create_object(sha1, alloc_object_node());
+               obj = create_object(the_repository, sha1,
+                                   alloc_object_node(the_repository));
        return obj;
 }
 
@@ -210,7 +214,7 @@ struct object *parse_object_buffer(const struct object_id *oid, enum object_type
        } else if (type == OBJ_COMMIT) {
                struct commit *commit = lookup_commit(oid);
                if (commit) {
-                       if (parse_commit_buffer(commit, buffer, size))
+                       if (parse_commit_buffer(commit, buffer, size, 1))
                                return NULL;
                        if (!get_cached_commit_buffer(commit, NULL)) {
                                set_commit_buffer(commit, buffer, size);
@@ -431,8 +435,8 @@ void clear_object_flags(unsigned flags)
 {
        int i;
 
-       for (i=0; i < obj_hash_size; i++) {
-               struct object *obj = obj_hash[i];
+       for (i=0; i < the_repository->parsed_objects->obj_hash_size; i++) {
+               struct object *obj = the_repository->parsed_objects->obj_hash[i];
                if (obj)
                        obj->flags &= ~flags;
        }
@@ -442,13 +446,30 @@ void clear_commit_marks_all(unsigned int flags)
 {
        int i;
 
-       for (i = 0; i < obj_hash_size; i++) {
-               struct object *obj = obj_hash[i];
+       for (i = 0; i < the_repository->parsed_objects->obj_hash_size; i++) {
+               struct object *obj = the_repository->parsed_objects->obj_hash[i];
                if (obj && obj->type == OBJ_COMMIT)
                        obj->flags &= ~flags;
        }
 }
 
+struct parsed_object_pool *parsed_object_pool_new(void)
+{
+       struct parsed_object_pool *o = xmalloc(sizeof(*o));
+       memset(o, 0, sizeof(*o));
+
+       o->blob_state = allocate_alloc_state();
+       o->tree_state = allocate_alloc_state();
+       o->commit_state = allocate_alloc_state();
+       o->tag_state = allocate_alloc_state();
+       o->object_state = allocate_alloc_state();
+
+       o->is_shallow = -1;
+       o->shallow_stat = xcalloc(1, sizeof(*o->shallow_stat));
+
+       return o;
+}
+
 struct raw_object_store *raw_object_store_new(void)
 {
        struct raw_object_store *o = xmalloc(sizeof(*o));
@@ -491,3 +512,43 @@ void raw_object_store_clear(struct raw_object_store *o)
        close_all_packs(o);
        o->packed_git = NULL;
 }
+
+void parsed_object_pool_clear(struct parsed_object_pool *o)
+{
+       /*
+        * As objects are allocated in slabs (see alloc.c), we do
+        * not need to free each object, but each slab instead.
+        *
+        * Before doing so, we need to free any additional memory
+        * the objects may hold.
+        */
+       unsigned i;
+
+       for (i = 0; i < o->obj_hash_size; i++) {
+               struct object *obj = o->obj_hash[i];
+
+               if (!obj)
+                       continue;
+
+               if (obj->type == OBJ_TREE)
+                       free_tree_buffer((struct tree*)obj);
+               else if (obj->type == OBJ_COMMIT)
+                       release_commit_memory((struct commit*)obj);
+               else if (obj->type == OBJ_TAG)
+                       release_tag_memory((struct tag*)obj);
+       }
+
+       FREE_AND_NULL(o->obj_hash);
+       o->obj_hash_size = 0;
+
+       clear_alloc_state(o->blob_state);
+       clear_alloc_state(o->tree_state);
+       clear_alloc_state(o->commit_state);
+       clear_alloc_state(o->tag_state);
+       clear_alloc_state(o->object_state);
+       FREE_AND_NULL(o->blob_state);
+       FREE_AND_NULL(o->tree_state);
+       FREE_AND_NULL(o->commit_state);
+       FREE_AND_NULL(o->tag_state);
+       FREE_AND_NULL(o->object_state);
+}
index 5c13955000cdaec252f29a3b93599560c35deb8a..1b96073601f2cd239e7fe6923a078572ee820bcc 100644 (file)
--- a/object.h
+++ b/object.h
@@ -1,6 +1,32 @@
 #ifndef OBJECT_H
 #define OBJECT_H
 
+struct parsed_object_pool {
+       struct object **obj_hash;
+       int nr_objs, obj_hash_size;
+
+       /* TODO: migrate alloc_states to mem-pool? */
+       struct alloc_state *blob_state;
+       struct alloc_state *tree_state;
+       struct alloc_state *commit_state;
+       struct alloc_state *tag_state;
+       struct alloc_state *object_state;
+       unsigned commit_count;
+
+       /* parent substitutions from .git/info/grafts and .git/shallow */
+       struct commit_graft **grafts;
+       int grafts_alloc, grafts_nr;
+
+       int is_shallow;
+       struct stat_validity *shallow_stat;
+       char *alternate_shallow_file;
+
+       int commit_graft_prepared;
+};
+
+struct parsed_object_pool *parsed_object_pool_new(void);
+void parsed_object_pool_clear(struct parsed_object_pool *o);
+
 struct object_list {
        struct object *item;
        struct object_list *next;
@@ -42,6 +68,7 @@ struct object_array {
  * builtin/index-pack.c:                                     2021
  * builtin/pack-objects.c:                                   20
  * builtin/reflog.c:                   10--12
+ * builtin/show-branch.c:    0-------------------------------------------26
  * builtin/unpack-objects.c:                                 2021
  */
 #define FLAG_BITS  27
@@ -84,7 +111,7 @@ extern struct object *get_indexed_object(unsigned int);
  */
 struct object *lookup_object(const unsigned char *sha1);
 
-extern void *create_object(const unsigned char *sha1, void *obj);
+extern void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
 
 void *object_as_type(struct object *obj, enum object_type type, int quiet);
 
index 7b2dc3e7dc768a2d2405a9ae3b1d13f86069a93d..d977e9bacb19ed766dd0a501b6fbcae800bb4ae0 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tag.h"
 #include "diff.h"
@@ -360,11 +361,17 @@ static int date_compare(const void *_a, const void *_b)
 
 void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)
 {
-       if (prepare_bitmap_git() < 0)
+       struct bitmap_index *bitmap_git;
+       if (!(bitmap_git = prepare_bitmap_git()))
                return;
 
        writer.reused = kh_init_sha1();
-       rebuild_existing_bitmaps(to_pack, writer.reused, writer.show_progress);
+       rebuild_existing_bitmaps(bitmap_git, to_pack, writer.reused,
+                                writer.show_progress);
+       /*
+        * NEEDSWORK: rebuild_existing_bitmaps() makes writer.reused reference
+        * some bitmaps in bitmap_git, so we can't free the latter.
+        */
 }
 
 static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)
index 18f8b22aeb422bb81999e1e564006daf8b64f637..f0a1937a1cc5fbb13fc705df8d193a43a0648198 100644 (file)
@@ -25,14 +25,14 @@ struct stored_bitmap {
 };
 
 /*
- * The currently active bitmap index. By design, repositories only have
+ * The active bitmap index for a repository. By design, repositories only have
  * a single bitmap index available (the index for the biggest packfile in
  * the repository), since bitmap indexes need full closure.
  *
  * If there is more than one bitmap index available (e.g. because of alternates),
  * the active bitmap index is the largest one.
  */
-static struct bitmap_index {
+struct bitmap_index {
        /* Packfile to which this bitmap index belongs to */
        struct packed_git *pack;
 
@@ -66,7 +66,7 @@ static struct bitmap_index {
        /* Number of bitmapped commits */
        uint32_t entry_count;
 
-       /* Name-hash cache (or NULL if not present). */
+       /* If not NULL, this is a name-hash cache pointing into map. */
        uint32_t *hashes;
 
        /*
@@ -90,8 +90,7 @@ static struct bitmap_index {
        unsigned int version;
 
        unsigned loaded : 1;
-
-} bitmap_git;
+};
 
 static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
 {
@@ -259,7 +258,7 @@ static char *pack_bitmap_filename(struct packed_git *p)
        return xstrfmt("%.*s.bitmap", (int)len, p->pack_name);
 }
 
-static int open_pack_bitmap_1(struct packed_git *packfile)
+static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git *packfile)
 {
        int fd;
        struct stat st;
@@ -280,117 +279,122 @@ static int open_pack_bitmap_1(struct packed_git *packfile)
                return -1;
        }
 
-       if (bitmap_git.pack) {
+       if (bitmap_git->pack) {
                warning("ignoring extra bitmap file: %s", packfile->pack_name);
                close(fd);
                return -1;
        }
 
-       bitmap_git.pack = packfile;
-       bitmap_git.map_size = xsize_t(st.st_size);
-       bitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       bitmap_git.map_pos = 0;
+       bitmap_git->pack = packfile;
+       bitmap_git->map_size = xsize_t(st.st_size);
+       bitmap_git->map = xmmap(NULL, bitmap_git->map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       bitmap_git->map_pos = 0;
        close(fd);
 
-       if (load_bitmap_header(&bitmap_git) < 0) {
-               munmap(bitmap_git.map, bitmap_git.map_size);
-               bitmap_git.map = NULL;
-               bitmap_git.map_size = 0;
+       if (load_bitmap_header(bitmap_git) < 0) {
+               munmap(bitmap_git->map, bitmap_git->map_size);
+               bitmap_git->map = NULL;
+               bitmap_git->map_size = 0;
                return -1;
        }
 
        return 0;
 }
 
-static int load_pack_bitmap(void)
+static int load_pack_bitmap(struct bitmap_index *bitmap_git)
 {
-       assert(bitmap_git.map && !bitmap_git.loaded);
+       assert(bitmap_git->map && !bitmap_git->loaded);
 
-       bitmap_git.bitmaps = kh_init_sha1();
-       bitmap_git.ext_index.positions = kh_init_sha1_pos();
-       load_pack_revindex(bitmap_git.pack);
+       bitmap_git->bitmaps = kh_init_sha1();
+       bitmap_git->ext_index.positions = kh_init_sha1_pos();
+       load_pack_revindex(bitmap_git->pack);
 
-       if (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.tags = read_bitmap_1(&bitmap_git)))
+       if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->trees = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->blobs = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->tags = read_bitmap_1(bitmap_git)))
                goto failed;
 
-       if (load_bitmap_entries_v1(&bitmap_git) < 0)
+       if (load_bitmap_entries_v1(bitmap_git) < 0)
                goto failed;
 
-       bitmap_git.loaded = 1;
+       bitmap_git->loaded = 1;
        return 0;
 
 failed:
-       munmap(bitmap_git.map, bitmap_git.map_size);
-       bitmap_git.map = NULL;
-       bitmap_git.map_size = 0;
+       munmap(bitmap_git->map, bitmap_git->map_size);
+       bitmap_git->map = NULL;
+       bitmap_git->map_size = 0;
        return -1;
 }
 
-static int open_pack_bitmap(void)
+static int open_pack_bitmap(struct bitmap_index *bitmap_git)
 {
        struct packed_git *p;
        int ret = -1;
 
-       assert(!bitmap_git.map && !bitmap_git.loaded);
+       assert(!bitmap_git->map && !bitmap_git->loaded);
 
        for (p = get_packed_git(the_repository); p; p = p->next) {
-               if (open_pack_bitmap_1(p) == 0)
+               if (open_pack_bitmap_1(bitmap_git, p) == 0)
                        ret = 0;
        }
 
        return ret;
 }
 
-int prepare_bitmap_git(void)
+struct bitmap_index *prepare_bitmap_git(void)
 {
-       if (bitmap_git.loaded)
-               return 0;
+       struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-       if (!open_pack_bitmap())
-               return load_pack_bitmap();
+       if (!open_pack_bitmap(bitmap_git) && !load_pack_bitmap(bitmap_git))
+               return bitmap_git;
 
-       return -1;
+       free_bitmap_index(bitmap_git);
+       return NULL;
 }
 
 struct include_data {
+       struct bitmap_index *bitmap_git;
        struct bitmap *base;
        struct bitmap *seen;
 };
 
-static inline int bitmap_position_extended(const unsigned char *sha1)
+static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
+                                          const unsigned char *sha1)
 {
-       khash_sha1_pos *positions = bitmap_git.ext_index.positions;
+       khash_sha1_pos *positions = bitmap_git->ext_index.positions;
        khiter_t pos = kh_get_sha1_pos(positions, sha1);
 
        if (pos < kh_end(positions)) {
                int bitmap_pos = kh_value(positions, pos);
-               return bitmap_pos + bitmap_git.pack->num_objects;
+               return bitmap_pos + bitmap_git->pack->num_objects;
        }
 
        return -1;
 }
 
-static inline int bitmap_position_packfile(const unsigned char *sha1)
+static inline int bitmap_position_packfile(struct bitmap_index *bitmap_git,
+                                          const unsigned char *sha1)
 {
-       off_t offset = find_pack_entry_one(sha1, bitmap_git.pack);
+       off_t offset = find_pack_entry_one(sha1, bitmap_git->pack);
        if (!offset)
                return -1;
 
-       return find_revindex_position(bitmap_git.pack, offset);
+       return find_revindex_position(bitmap_git->pack, offset);
 }
 
-static int bitmap_position(const unsigned char *sha1)
+static int bitmap_position(struct bitmap_index *bitmap_git,
+                          const unsigned char *sha1)
 {
-       int pos = bitmap_position_packfile(sha1);
-       return (pos >= 0) ? pos : bitmap_position_extended(sha1);
+       int pos = bitmap_position_packfile(bitmap_git, sha1);
+       return (pos >= 0) ? pos : bitmap_position_extended(bitmap_git, sha1);
 }
 
-static int ext_index_add_object(struct object *object, const char *name)
+static int ext_index_add_object(struct bitmap_index *bitmap_git,
+                               struct object *object, const char *name)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct eindex *eindex = &bitmap_git->ext_index;
 
        khiter_t hash_pos;
        int hash_ret;
@@ -413,27 +417,34 @@ static int ext_index_add_object(struct object *object, const char *name)
                bitmap_pos = kh_value(eindex->positions, hash_pos);
        }
 
-       return bitmap_pos + bitmap_git.pack->num_objects;
+       return bitmap_pos + bitmap_git->pack->num_objects;
 }
 
-static void show_object(struct object *object, const char *name, void *data)
+struct bitmap_show_data {
+       struct bitmap_index *bitmap_git;
+       struct bitmap *base;
+};
+
+static void show_object(struct object *object, const char *name, void *data_)
 {
-       struct bitmap *base = data;
+       struct bitmap_show_data *data = data_;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->oid.hash);
+       bitmap_pos = bitmap_position(data->bitmap_git, object->oid.hash);
 
        if (bitmap_pos < 0)
-               bitmap_pos = ext_index_add_object(object, name);
+               bitmap_pos = ext_index_add_object(data->bitmap_git, object,
+                                                 name);
 
-       bitmap_set(base, bitmap_pos);
+       bitmap_set(data->base, bitmap_pos);
 }
 
 static void show_commit(struct commit *commit, void *data)
 {
 }
 
-static int add_to_include_set(struct include_data *data,
+static int add_to_include_set(struct bitmap_index *bitmap_git,
+                             struct include_data *data,
                              const unsigned char *sha1,
                              int bitmap_pos)
 {
@@ -445,9 +456,9 @@ static int add_to_include_set(struct include_data *data,
        if (bitmap_get(data->base, bitmap_pos))
                return 0;
 
-       hash_pos = kh_get_sha1(bitmap_git.bitmaps, sha1);
-       if (hash_pos < kh_end(bitmap_git.bitmaps)) {
-               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, hash_pos);
+       hash_pos = kh_get_sha1(bitmap_git->bitmaps, sha1);
+       if (hash_pos < kh_end(bitmap_git->bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, hash_pos);
                bitmap_or_ewah(data->base, lookup_stored_bitmap(st));
                return 0;
        }
@@ -461,11 +472,14 @@ static int should_include(struct commit *commit, void *_data)
        struct include_data *data = _data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.oid.hash);
+       bitmap_pos = bitmap_position(data->bitmap_git, commit->object.oid.hash);
        if (bitmap_pos < 0)
-               bitmap_pos = ext_index_add_object((struct object *)commit, NULL);
+               bitmap_pos = ext_index_add_object(data->bitmap_git,
+                                                 (struct object *)commit,
+                                                 NULL);
 
-       if (!add_to_include_set(data, commit->object.oid.hash, bitmap_pos)) {
+       if (!add_to_include_set(data->bitmap_git, data, commit->object.oid.hash,
+                               bitmap_pos)) {
                struct commit_list *parent = commit->parents;
 
                while (parent) {
@@ -479,7 +493,8 @@ static int should_include(struct commit *commit, void *_data)
        return 1;
 }
 
-static struct bitmap *find_objects(struct rev_info *revs,
+static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
+                                  struct rev_info *revs,
                                   struct object_list *roots,
                                   struct bitmap *seen)
 {
@@ -501,10 +516,10 @@ static struct bitmap *find_objects(struct rev_info *revs,
                roots = roots->next;
 
                if (object->type == OBJ_COMMIT) {
-                       khiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->oid.hash);
+                       khiter_t pos = kh_get_sha1(bitmap_git->bitmaps, object->oid.hash);
 
-                       if (pos < kh_end(bitmap_git.bitmaps)) {
-                               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+                       if (pos < kh_end(bitmap_git->bitmaps)) {
+                               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
                                struct ewah_bitmap *or_with = lookup_stored_bitmap(st);
 
                                if (base == NULL)
@@ -543,7 +558,7 @@ static struct bitmap *find_objects(struct rev_info *revs,
                int pos;
 
                roots = roots->next;
-               pos = bitmap_position(object->oid.hash);
+               pos = bitmap_position(bitmap_git, object->oid.hash);
 
                if (pos < 0 || base == NULL || !bitmap_get(base, pos)) {
                        object->flags &= ~UNINTERESTING;
@@ -556,10 +571,12 @@ static struct bitmap *find_objects(struct rev_info *revs,
 
        if (needs_walk) {
                struct include_data incdata;
+               struct bitmap_show_data show_data;
 
                if (base == NULL)
                        base = bitmap_new();
 
+               incdata.bitmap_git = bitmap_git;
                incdata.base = base;
                incdata.seen = seen;
 
@@ -569,22 +586,27 @@ static struct bitmap *find_objects(struct rev_info *revs,
                if (prepare_revision_walk(revs))
                        die("revision walk setup failed");
 
-               traverse_commit_list(revs, show_commit, show_object, base);
+               show_data.bitmap_git = bitmap_git;
+               show_data.base = base;
+
+               traverse_commit_list(revs, show_commit, show_object,
+                                    &show_data);
        }
 
        return base;
 }
 
-static void show_extended_objects(struct bitmap *objects,
+static void show_extended_objects(struct bitmap_index *bitmap_git,
                                  show_reachable_fn show_reach)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct bitmap *objects = bitmap_git->result;
+       struct eindex *eindex = &bitmap_git->ext_index;
        uint32_t i;
 
        for (i = 0; i < eindex->count; ++i) {
                struct object *obj;
 
-               if (!bitmap_get(objects, bitmap_git.pack->num_objects + i))
+               if (!bitmap_get(objects, bitmap_git->pack->num_objects + i))
                        continue;
 
                obj = eindex->objects[i];
@@ -593,7 +615,7 @@ static void show_extended_objects(struct bitmap *objects,
 }
 
 static void show_objects_for_type(
-       struct bitmap *objects,
+       struct bitmap_index *bitmap_git,
        struct ewah_bitmap *type_filter,
        enum object_type object_type,
        show_reachable_fn show_reach)
@@ -604,7 +626,9 @@ static void show_objects_for_type(
        struct ewah_iterator it;
        eword_t filter;
 
-       if (bitmap_git.reuse_objects == bitmap_git.pack->num_objects)
+       struct bitmap *objects = bitmap_git->result;
+
+       if (bitmap_git->reuse_objects == bitmap_git->pack->num_objects)
                return;
 
        ewah_iterator_init(&it, type_filter);
@@ -622,16 +646,16 @@ static void show_objects_for_type(
 
                        offset += ewah_bit_ctz64(word >> offset);
 
-                       if (pos + offset < bitmap_git.reuse_objects)
+                       if (pos + offset < bitmap_git->reuse_objects)
                                continue;
 
-                       entry = &bitmap_git.pack->revindex[pos + offset];
-                       nth_packed_object_oid(&oid, bitmap_git.pack, entry->nr);
+                       entry = &bitmap_git->pack->revindex[pos + offset];
+                       nth_packed_object_oid(&oid, bitmap_git->pack, entry->nr);
 
-                       if (bitmap_git.hashes)
-                               hash = get_be32(bitmap_git.hashes + entry->nr);
+                       if (bitmap_git->hashes)
+                               hash = get_be32(bitmap_git->hashes + entry->nr);
 
-                       show_reach(&oid, object_type, 0, hash, bitmap_git.pack, entry->offset);
+                       show_reach(&oid, object_type, 0, hash, bitmap_git->pack, entry->offset);
                }
 
                pos += BITS_IN_EWORD;
@@ -639,20 +663,21 @@ static void show_objects_for_type(
        }
 }
 
-static int in_bitmapped_pack(struct object_list *roots)
+static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
+                            struct object_list *roots)
 {
        while (roots) {
                struct object *object = roots->item;
                roots = roots->next;
 
-               if (find_pack_entry_one(object->oid.hash, bitmap_git.pack) > 0)
+               if (find_pack_entry_one(object->oid.hash, bitmap_git->pack) > 0)
                        return 1;
        }
 
        return 0;
 }
 
-int prepare_bitmap_walk(struct rev_info *revs)
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
 {
        unsigned int i;
 
@@ -662,12 +687,11 @@ int prepare_bitmap_walk(struct rev_info *revs)
        struct bitmap *wants_bitmap = NULL;
        struct bitmap *haves_bitmap = NULL;
 
-       if (!bitmap_git.loaded) {
-               /* try to open a bitmapped pack, but don't parse it yet
-                * because we may not need to use it */
-               if (open_pack_bitmap() < 0)
-                       return -1;
-       }
+       struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
+       /* try to open a bitmapped pack, but don't parse it yet
+        * because we may not need to use it */
+       if (open_pack_bitmap(bitmap_git) < 0)
+               goto cleanup;
 
        for (i = 0; i < revs->pending.nr; ++i) {
                struct object *object = revs->pending.objects[i].item;
@@ -699,26 +723,26 @@ int prepare_bitmap_walk(struct rev_info *revs)
         * in the packfile that has a bitmap, we don't have anything to
         * optimize here
         */
-       if (haves && !in_bitmapped_pack(haves))
-               return -1;
+       if (haves && !in_bitmapped_pack(bitmap_git, haves))
+               goto cleanup;
 
        /* if we don't want anything, we're done here */
        if (!wants)
-               return -1;
+               goto cleanup;
 
        /*
         * now we're going to use bitmaps, so load the actual bitmap entries
         * from disk. this is the point of no return; after this the rev_list
         * becomes invalidated and we must perform the revwalk through bitmaps
         */
-       if (!bitmap_git.loaded && load_pack_bitmap() < 0)
-               return -1;
+       if (!bitmap_git->loaded && load_pack_bitmap(bitmap_git) < 0)
+               goto cleanup;
 
        object_array_clear(&revs->pending);
 
        if (haves) {
                revs->ignore_missing_links = 1;
-               haves_bitmap = find_objects(revs, haves, NULL);
+               haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
                reset_revision_walk();
                revs->ignore_missing_links = 0;
 
@@ -726,7 +750,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
                        BUG("failed to perform bitmap walk");
        }
 
-       wants_bitmap = find_objects(revs, wants, haves_bitmap);
+       wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
 
        if (!wants_bitmap)
                BUG("failed to perform bitmap walk");
@@ -734,13 +758,18 @@ int prepare_bitmap_walk(struct rev_info *revs)
        if (haves_bitmap)
                bitmap_and_not(wants_bitmap, haves_bitmap);
 
-       bitmap_git.result = wants_bitmap;
+       bitmap_git->result = wants_bitmap;
 
        bitmap_free(haves_bitmap);
-       return 0;
+       return bitmap_git;
+
+cleanup:
+       free_bitmap_index(bitmap_git);
+       return NULL;
 }
 
-int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
+int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                      struct packed_git **packfile,
                                       uint32_t *entries,
                                       off_t *up_to)
 {
@@ -750,7 +779,7 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
         */
        static const double REUSE_PERCENT = 0.9;
 
-       struct bitmap *result = bitmap_git.result;
+       struct bitmap *result = bitmap_git->result;
        uint32_t reuse_threshold;
        uint32_t i, reuse_objects = 0;
 
@@ -770,8 +799,8 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
                const unsigned char *sha1;
                struct revindex_entry *entry;
 
-               entry = &bitmap_git.reverse_index->revindex[reuse_objects];
-               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+               entry = &bitmap_git->reverse_index->revindex[reuse_objects];
+               sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
 
                fprintf(stderr, "Failed to reuse at %d (%016llx)\n",
                        reuse_objects, result->words[i]);
@@ -782,48 +811,50 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
        if (!reuse_objects)
                return -1;
 
-       if (reuse_objects >= bitmap_git.pack->num_objects) {
-               bitmap_git.reuse_objects = *entries = bitmap_git.pack->num_objects;
+       if (reuse_objects >= bitmap_git->pack->num_objects) {
+               bitmap_git->reuse_objects = *entries = bitmap_git->pack->num_objects;
                *up_to = -1; /* reuse the full pack */
-               *packfile = bitmap_git.pack;
+               *packfile = bitmap_git->pack;
                return 0;
        }
 
-       reuse_threshold = bitmap_popcount(bitmap_git.result) * REUSE_PERCENT;
+       reuse_threshold = bitmap_popcount(bitmap_git->result) * REUSE_PERCENT;
 
        if (reuse_objects < reuse_threshold)
                return -1;
 
-       bitmap_git.reuse_objects = *entries = reuse_objects;
-       *up_to = bitmap_git.pack->revindex[reuse_objects].offset;
-       *packfile = bitmap_git.pack;
+       bitmap_git->reuse_objects = *entries = reuse_objects;
+       *up_to = bitmap_git->pack->revindex[reuse_objects].offset;
+       *packfile = bitmap_git->pack;
 
        return 0;
 }
 
-void traverse_bitmap_commit_list(show_reachable_fn show_reachable)
+void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
+                                show_reachable_fn show_reachable)
 {
-       assert(bitmap_git.result);
+       assert(bitmap_git->result);
 
-       show_objects_for_type(bitmap_git.result, bitmap_git.commits,
+       show_objects_for_type(bitmap_git, bitmap_git->commits,
                OBJ_COMMIT, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.trees,
+       show_objects_for_type(bitmap_git, bitmap_git->trees,
                OBJ_TREE, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.blobs,
+       show_objects_for_type(bitmap_git, bitmap_git->blobs,
                OBJ_BLOB, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.tags,
+       show_objects_for_type(bitmap_git, bitmap_git->tags,
                OBJ_TAG, show_reachable);
 
-       show_extended_objects(bitmap_git.result, show_reachable);
+       show_extended_objects(bitmap_git, show_reachable);
 
-       bitmap_free(bitmap_git.result);
-       bitmap_git.result = NULL;
+       bitmap_free(bitmap_git->result);
+       bitmap_git->result = NULL;
 }
 
-static uint32_t count_object_type(struct bitmap *objects,
+static uint32_t count_object_type(struct bitmap_index *bitmap_git,
                                  enum object_type type)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct bitmap *objects = bitmap_git->result;
+       struct eindex *eindex = &bitmap_git->ext_index;
 
        uint32_t i = 0, count = 0;
        struct ewah_iterator it;
@@ -831,19 +862,19 @@ static uint32_t count_object_type(struct bitmap *objects,
 
        switch (type) {
        case OBJ_COMMIT:
-               ewah_iterator_init(&it, bitmap_git.commits);
+               ewah_iterator_init(&it, bitmap_git->commits);
                break;
 
        case OBJ_TREE:
-               ewah_iterator_init(&it, bitmap_git.trees);
+               ewah_iterator_init(&it, bitmap_git->trees);
                break;
 
        case OBJ_BLOB:
-               ewah_iterator_init(&it, bitmap_git.blobs);
+               ewah_iterator_init(&it, bitmap_git->blobs);
                break;
 
        case OBJ_TAG:
-               ewah_iterator_init(&it, bitmap_git.tags);
+               ewah_iterator_init(&it, bitmap_git->tags);
                break;
 
        default:
@@ -857,32 +888,34 @@ static uint32_t count_object_type(struct bitmap *objects,
 
        for (i = 0; i < eindex->count; ++i) {
                if (eindex->objects[i]->type == type &&
-                       bitmap_get(objects, bitmap_git.pack->num_objects + i))
+                       bitmap_get(objects, bitmap_git->pack->num_objects + i))
                        count++;
        }
 
        return count;
 }
 
-void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees,
+void count_bitmap_commit_list(struct bitmap_index *bitmap_git,
+                             uint32_t *commits, uint32_t *trees,
                              uint32_t *blobs, uint32_t *tags)
 {
-       assert(bitmap_git.result);
+       assert(bitmap_git->result);
 
        if (commits)
-               *commits = count_object_type(bitmap_git.result, OBJ_COMMIT);
+               *commits = count_object_type(bitmap_git, OBJ_COMMIT);
 
        if (trees)
-               *trees = count_object_type(bitmap_git.result, OBJ_TREE);
+               *trees = count_object_type(bitmap_git, OBJ_TREE);
 
        if (blobs)
-               *blobs = count_object_type(bitmap_git.result, OBJ_BLOB);
+               *blobs = count_object_type(bitmap_git, OBJ_BLOB);
 
        if (tags)
-               *tags = count_object_type(bitmap_git.result, OBJ_TAG);
+               *tags = count_object_type(bitmap_git, OBJ_TAG);
 }
 
 struct bitmap_test_data {
+       struct bitmap_index *bitmap_git;
        struct bitmap *base;
        struct progress *prg;
        size_t seen;
@@ -894,7 +927,7 @@ static void test_show_object(struct object *object, const char *name,
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->oid.hash);
+       bitmap_pos = bitmap_position(tdata->bitmap_git, object->oid.hash);
        if (bitmap_pos < 0)
                die("Object not in bitmap: %s\n", oid_to_hex(&object->oid));
 
@@ -907,7 +940,8 @@ static void test_show_commit(struct commit *commit, void *data)
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.oid.hash);
+       bitmap_pos = bitmap_position(tdata->bitmap_git,
+                                    commit->object.oid.hash);
        if (bitmap_pos < 0)
                die("Object not in bitmap: %s\n", oid_to_hex(&commit->object.oid));
 
@@ -922,21 +956,22 @@ void test_bitmap_walk(struct rev_info *revs)
        khiter_t pos;
        size_t result_popcnt;
        struct bitmap_test_data tdata;
+       struct bitmap_index *bitmap_git;
 
-       if (prepare_bitmap_git())
+       if (!(bitmap_git = prepare_bitmap_git()))
                die("failed to load bitmap indexes");
 
        if (revs->pending.nr != 1)
                die("you must specify exactly one commit to test");
 
        fprintf(stderr, "Bitmap v%d test (%d entries loaded)\n",
-               bitmap_git.version, bitmap_git.entry_count);
+               bitmap_git->version, bitmap_git->entry_count);
 
        root = revs->pending.objects[0].item;
-       pos = kh_get_sha1(bitmap_git.bitmaps, root->oid.hash);
+       pos = kh_get_sha1(bitmap_git->bitmaps, root->oid.hash);
 
-       if (pos < kh_end(bitmap_git.bitmaps)) {
-               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+       if (pos < kh_end(bitmap_git->bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
                struct ewah_bitmap *bm = lookup_stored_bitmap(st);
 
                fprintf(stderr, "Found bitmap for %s. %d bits / %08x checksum\n",
@@ -957,6 +992,7 @@ void test_bitmap_walk(struct rev_info *revs)
        if (prepare_revision_walk(revs))
                die("revision walk setup failed");
 
+       tdata.bitmap_git = bitmap_git;
        tdata.base = bitmap_new();
        tdata.prg = start_progress("Verifying bitmap entries", result_popcnt);
        tdata.seen = 0;
@@ -970,7 +1006,7 @@ void test_bitmap_walk(struct rev_info *revs)
        else
                fprintf(stderr, "Mismatch!\n");
 
-       bitmap_free(result);
+       free_bitmap_index(bitmap_git);
 }
 
 static int rebuild_bitmap(uint32_t *reposition,
@@ -1004,7 +1040,8 @@ static int rebuild_bitmap(uint32_t *reposition,
        return 0;
 }
 
-int rebuild_existing_bitmaps(struct packing_data *mapping,
+int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git,
+                            struct packing_data *mapping,
                             khash_sha1 *reused_bitmaps,
                             int show_progress)
 {
@@ -1017,10 +1054,7 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        khiter_t hash_pos;
        int hash_ret;
 
-       if (prepare_bitmap_git() < 0)
-               return -1;
-
-       num_objects = bitmap_git.pack->num_objects;
+       num_objects = bitmap_git->pack->num_objects;
        reposition = xcalloc(num_objects, sizeof(uint32_t));
 
        for (i = 0; i < num_objects; ++i) {
@@ -1028,8 +1062,8 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
                struct revindex_entry *entry;
                struct object_entry *oe;
 
-               entry = &bitmap_git.pack->revindex[i];
-               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+               entry = &bitmap_git->pack->revindex[i];
+               sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
                oe = packlist_find(mapping, sha1, NULL);
 
                if (oe)
@@ -1042,7 +1076,7 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        if (show_progress)
                progress = start_progress("Reusing bitmaps", 0);
 
-       kh_foreach_value(bitmap_git.bitmaps, stored, {
+       kh_foreach_value(bitmap_git->bitmaps, stored, {
                if (stored->flags & BITMAP_FLAG_REUSE) {
                        if (!rebuild_bitmap(reposition,
                                            lookup_stored_bitmap(stored),
@@ -1064,3 +1098,21 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        bitmap_free(rebuild);
        return 0;
 }
+
+void free_bitmap_index(struct bitmap_index *b)
+{
+       if (!b)
+               return;
+
+       if (b->map)
+               munmap(b->map, b->map_size);
+       ewah_pool_free(b->commits);
+       ewah_pool_free(b->trees);
+       ewah_pool_free(b->blobs);
+       ewah_pool_free(b->tags);
+       kh_destroy_sha1(b->bitmaps);
+       free(b->ext_index.objects);
+       free(b->ext_index.hashes);
+       bitmap_free(b->result);
+       free(b);
+}
index 5ded2f139a6ccdab725ed5e568b1100906f4b736..4555907dee99fbf731b739c584837adbbe8270f1 100644 (file)
@@ -34,13 +34,21 @@ typedef int (*show_reachable_fn)(
        struct packed_git *found_pack,
        off_t found_offset);
 
-int prepare_bitmap_git(void);
-void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);
-void traverse_bitmap_commit_list(show_reachable_fn show_reachable);
+struct bitmap_index;
+
+struct bitmap_index *prepare_bitmap_git(void);
+void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
+                             uint32_t *trees, uint32_t *blobs, uint32_t *tags);
+void traverse_bitmap_commit_list(struct bitmap_index *,
+                                show_reachable_fn show_reachable);
 void test_bitmap_walk(struct rev_info *revs);
-int prepare_bitmap_walk(struct rev_info *revs);
-int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);
-int rebuild_existing_bitmaps(struct packing_data *mapping, khash_sha1 *reused_bitmaps, int show_progress);
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs);
+int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
+                                      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);
+void free_bitmap_index(struct bitmap_index *);
 
 void bitmap_writer_show_progress(int show);
 void bitmap_writer_set_checksum(unsigned char *sha1);
index e0a38aba9321deae1d64aea7cd87067fde240b10..cc7eaffe1baef8accc1476c92145b7c4bcdcf046 100644 (file)
@@ -3,6 +3,11 @@
 
 #include "oidset.h"
 
+/* in object-store.h */
+struct packed_git;
+struct object_info;
+enum object_type;
+
 /*
  * Generate the filename to be used for a pack file with checksum "sha1" and
  * extension "ext". The result is written into the strbuf "buf", overwriting
index 0f7059a8ab32a624775026d7dc2289245c87c192..7db84227ab34cb33849f4327af58acd925f782c8 100644 (file)
@@ -427,13 +427,59 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
        parse_options_check(options);
 }
 
-/*
- * TODO: we are not completing the --no-XXX form yet because there are
- * many options that do not suppress it properly.
- */
+static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
+{
+       int printed_dashdash = 0;
+
+       for (; opts->type != OPTION_END; opts++) {
+               int has_unset_form = 0;
+               const char *name;
+
+               if (!opts->long_name)
+                       continue;
+               if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
+                       continue;
+               if (opts->flags & PARSE_OPT_NONEG)
+                       continue;
+
+               switch (opts->type) {
+               case OPTION_STRING:
+               case OPTION_FILENAME:
+               case OPTION_INTEGER:
+               case OPTION_MAGNITUDE:
+               case OPTION_CALLBACK:
+               case OPTION_BIT:
+               case OPTION_NEGBIT:
+               case OPTION_COUNTUP:
+               case OPTION_SET_INT:
+                       has_unset_form = 1;
+                       break;
+               default:
+                       break;
+               }
+               if (!has_unset_form)
+                       continue;
+
+               if (skip_prefix(opts->long_name, "no-", &name)) {
+                       if (nr_noopts < 0)
+                               printf(" --%s", name);
+               } else if (nr_noopts >= 0) {
+                       if (nr_noopts && !printed_dashdash) {
+                               printf(" --");
+                               printed_dashdash = 1;
+                       }
+                       printf(" --no-%s", opts->long_name);
+                       nr_noopts++;
+               }
+       }
+}
+
 static int show_gitcomp(struct parse_opt_ctx_t *ctx,
                        const struct option *opts)
 {
+       const struct option *original_opts = opts;
+       int nr_noopts = 0;
+
        for (; opts->type != OPTION_END; opts++) {
                const char *suffix = "";
 
@@ -463,8 +509,12 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
                }
                if (opts->flags & PARSE_OPT_COMP_ARG)
                        suffix = "=";
+               if (starts_with(opts->long_name, "no-"))
+                       nr_noopts++;
                printf(" --%s%s", opts->long_name, suffix);
        }
+       show_negated_gitcomp(original_opts, -1);
+       show_negated_gitcomp(original_opts, nr_noopts);
        fputc('\n', stdout);
        exit(0);
 }
diff --git a/path.c b/path.c
index 7f109f661816039768e4d8050bdb3c12384f9f85..34f0f98349a6eccd462614cdf143bb031ca348c8 100644 (file)
--- a/path.c
+++ b/path.c
@@ -1442,12 +1442,12 @@ char *xdg_cache_home(const char *filename)
        return NULL;
 }
 
-GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD")
-GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD")
-GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG")
-GIT_PATH_FUNC(git_path_merge_msg, "MERGE_MSG")
-GIT_PATH_FUNC(git_path_merge_rr, "MERGE_RR")
-GIT_PATH_FUNC(git_path_merge_mode, "MERGE_MODE")
-GIT_PATH_FUNC(git_path_merge_head, "MERGE_HEAD")
-GIT_PATH_FUNC(git_path_fetch_head, "FETCH_HEAD")
-GIT_PATH_FUNC(git_path_shallow, "shallow")
+REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD")
+REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
+REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
+REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
+REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
+REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
+REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
+REPO_GIT_PATH_FUNC(shallow, "shallow")
diff --git a/path.h b/path.h
index 1ccd0373c9db8460c7ad7a3f9c5465f84408fd56..5263f40519a3d4093f196513b27b2f8dc19418a4 100644 (file)
--- a/path.h
+++ b/path.h
@@ -160,14 +160,36 @@ extern void report_linked_checkout_garbage(void);
                return ret; \
        }
 
-const char *git_path_cherry_pick_head(void);
-const char *git_path_revert_head(void);
-const char *git_path_squash_msg(void);
-const char *git_path_merge_msg(void);
-const char *git_path_merge_rr(void);
-const char *git_path_merge_mode(void);
-const char *git_path_merge_head(void);
-const char *git_path_fetch_head(void);
-const char *git_path_shallow(void);
+#define REPO_GIT_PATH_FUNC(var, filename) \
+       const char *git_path_##var(struct repository *r) \
+       { \
+               if (!r->cached_paths.var) \
+                       r->cached_paths.var = git_pathdup(filename); \
+               return r->cached_paths.var; \
+       }
+
+struct path_cache {
+       const char *cherry_pick_head;
+       const char *revert_head;
+       const char *squash_msg;
+       const char *merge_msg;
+       const char *merge_rr;
+       const char *merge_mode;
+       const char *merge_head;
+       const char *fetch_head;
+       const char *shallow;
+};
+
+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+const char *git_path_cherry_pick_head(struct repository *r);
+const char *git_path_revert_head(struct repository *r);
+const char *git_path_squash_msg(struct repository *r);
+const char *git_path_merge_msg(struct repository *r);
+const char *git_path_merge_rr(struct repository *r);
+const char *git_path_merge_mode(struct repository *r);
+const char *git_path_merge_head(struct repository *r);
+const char *git_path_fetch_head(struct repository *r);
+const char *git_path_shallow(struct repository *r);
 
 #endif /* PATH_H */
index 372588260ea00f48ea4d031095901a575ac0e620..e865254bea028485e1731d316727b66753a8fc5e 100644 (file)
@@ -11,6 +11,7 @@
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
+#include "object-store.h"
 #include "tree.h"
 #include "commit.h"
 #include "blob.h"
index 01c1a82075eca737c2e3749e295ca24bbab870b6..0ab893a250f565ef8be1c449542d4e70e7bc10d5 100644 (file)
@@ -3,6 +3,7 @@
 #include "parse-options.h"
 #include "refs.h"
 #include "wildmatch.h"
+#include "object-store.h"
 #include "commit.h"
 #include "remote.h"
 #include "color.h"
@@ -16,6 +17,7 @@
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
+#include "commit-graph.h"
 
 static struct ref_msg {
        const char *gone;
@@ -1662,12 +1664,13 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
 }
 
 /*
- * Test whether the candidate or one of its parents is contained in the list.
+ * Test whether the candidate is contained in the list.
  * Do not recurse to find out, though, but return -1 if inconclusive.
  */
 static enum contains_result contains_test(struct commit *candidate,
                                          const struct commit_list *want,
-                                         struct contains_cache *cache)
+                                         struct contains_cache *cache,
+                                         uint32_t cutoff)
 {
        enum contains_result *cached = contains_cache_at(cache, candidate);
 
@@ -1683,6 +1686,10 @@ static enum contains_result contains_test(struct commit *candidate,
 
        /* Otherwise, we don't know; prepare to recurse */
        parse_commit_or_die(candidate);
+
+       if (candidate->generation < cutoff)
+               return CONTAINS_NO;
+
        return CONTAINS_UNKNOWN;
 }
 
@@ -1698,8 +1705,18 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                                              struct contains_cache *cache)
 {
        struct contains_stack contains_stack = { 0, 0, NULL };
-       enum contains_result result = contains_test(candidate, want, cache);
+       enum contains_result result;
+       uint32_t cutoff = GENERATION_NUMBER_INFINITY;
+       const struct commit_list *p;
+
+       for (p = want; p; p = p->next) {
+               struct commit *c = p->item;
+               load_commit_graph_info(c);
+               if (c->generation < cutoff)
+                       cutoff = c->generation;
+       }
 
+       result = contains_test(candidate, want, cache, cutoff);
        if (result != CONTAINS_UNKNOWN)
                return result;
 
@@ -1717,7 +1734,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                 * If we just popped the stack, parents->item has been marked,
                 * therefore contains_test will return a meaningful yes/no.
                 */
-               else switch (contains_test(parents->item, want, cache)) {
+               else switch (contains_test(parents->item, want, cache, cutoff)) {
                case CONTAINS_YES:
                        *contains_cache_at(cache, commit) = CONTAINS_YES;
                        contains_stack.nr--;
@@ -1731,7 +1748,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                }
        }
        free(contains_stack.contains_stack);
-       return contains_test(candidate, want, cache);
+       return contains_test(candidate, want, cache, cutoff);
 }
 
 static int commit_contains(struct ref_filter *filter, struct commit *commit,
diff --git a/refs.c b/refs.c
index 0eb379f9312fd9f167fea2e0f148c85c47cd2ff0..3b4508a97aa23fa4a16bb64137b01c16b2b4b59d 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -9,6 +9,7 @@
 #include "iterator.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
+#include "object-store.h"
 #include "object.h"
 #include "tag.h"
 #include "submodule.h"
index cec3fb9e00f78c1e0be9f24bd07aa756d56afc00..d447a731da0932e60c918030c7cd07a1843dfd35 100644 (file)
@@ -499,6 +499,7 @@ static int load_contents(struct snapshot *snapshot)
        size = xsize_t(st.st_size);
 
        if (!size) {
+               close(fd);
                return 0;
        } else if (mmap_strategy == MMAP_NONE || size <= SMALL_FILE_SIZE) {
                snapshot->buf = xmalloc(size);
index 78edc48ae8eebcfdbfd87172e7ef4ead6c3fff85..e8010dce0ce27472c2b3269e13b9c1de6a69f3cc 100644 (file)
--- a/refspec.c
+++ b/refspec.c
@@ -124,11 +124,16 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
        return 1;
 }
 
-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
+int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
 {
        memset(item, 0, sizeof(*item));
+       return parse_refspec(item, refspec, fetch);
+}
 
-       if (!parse_refspec(item, refspec, fetch))
+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
+                             int fetch)
+{
+       if (!refspec_item_init(item, refspec, fetch))
                die("Invalid refspec '%s'", refspec);
 }
 
@@ -152,7 +157,7 @@ void refspec_append(struct refspec *rs, const char *refspec)
 {
        struct refspec_item item;
 
-       refspec_item_init(&item, refspec, rs->fetch);
+       refspec_item_init_or_die(&item, refspec, rs->fetch);
 
        ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
        rs->items[rs->nr++] = item;
@@ -191,7 +196,7 @@ void refspec_clear(struct refspec *rs)
 int valid_fetch_refspec(const char *fetch_refspec_str)
 {
        struct refspec_item refspec;
-       int ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);
+       int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
        refspec_item_clear(&refspec);
        return ret;
 }
index 3a9363887c48dbba612ae4de195c30f9d382c00c..9b6e64a824e4be1025f3f0a07fa5a59296698489 100644 (file)
--- a/refspec.h
+++ b/refspec.h
@@ -32,7 +32,10 @@ struct refspec {
        int fetch;
 };
 
-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);
+int refspec_item_init(struct refspec_item *item, const char *refspec,
+                     int fetch);
+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
+                             int fetch);
 void refspec_item_clear(struct refspec_item *item);
 void refspec_init(struct refspec *rs, int fetch);
 void refspec_append(struct refspec *rs, const char *refspec);
index 444d98059f681e21beeb3e66fe8539887cfb74d1..3af708c5b671920cff3d1888eb9c8ae724e0d648 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "refs.h"
 #include "remote.h"
+#include "object-store.h"
 #include "strbuf.h"
 #include "url.h"
 #include "exec-cmd.h"
index abe80c13972c718bd5d738063a2f526f2d12ee87..539285fbdf015141bbacf7a730b9fad0cacbf842 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -3,6 +3,7 @@
 #include "remote.h"
 #include "refs.h"
 #include "refspec.h"
+#include "object-store.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
index 02fe884603df56ea0996121152b7640afb56a48f..5dd148671811b383a64b8f3adab87804cea9db08 100644 (file)
@@ -2,6 +2,7 @@
 #include "repository.h"
 #include "object-store.h"
 #include "config.h"
+#include "object.h"
 #include "submodule-config.h"
 
 /* The main repository */
@@ -14,6 +15,8 @@ void initialize_the_repository(void)
 
        the_repo.index = &the_index;
        the_repo.objects = raw_object_store_new();
+       the_repo.parsed_objects = parsed_object_pool_new();
+
        repo_set_hash_algo(&the_repo, GIT_HASH_SHA1);
 }
 
@@ -143,6 +146,7 @@ int repo_init(struct repository *repo,
        memset(repo, 0, sizeof(*repo));
 
        repo->objects = raw_object_store_new();
+       repo->parsed_objects = parsed_object_pool_new();
 
        if (repo_init_gitdir(repo, gitdir))
                goto error;
@@ -226,6 +230,9 @@ void repo_clear(struct repository *repo)
        raw_object_store_clear(repo->objects);
        FREE_AND_NULL(repo->objects);
 
+       parsed_object_pool_clear(repo->parsed_objects);
+       FREE_AND_NULL(repo->parsed_objects);
+
        if (repo->config) {
                git_configset_clear(repo->config);
                FREE_AND_NULL(repo->config);
index f2646f0c52aa83f6da8950cfd96c4308498cf417..b9413be50cfd0c9d17865220c3db5eb16024187e 100644 (file)
@@ -26,9 +26,23 @@ struct repository {
         */
        struct raw_object_store *objects;
 
+       /*
+        * All objects in this repository that have been parsed. This structure
+        * owns all objects it references, so users of "struct object *"
+        * generally do not need to free them; instead, when a repository is no
+        * longer used, call parsed_object_pool_clear() on this structure, which
+        * is called by the repositories repo_clear on its desconstruction.
+        */
+       struct parsed_object_pool *parsed_objects;
+
        /* The store in which the refs are held. */
        struct ref_store *refs;
 
+       /*
+        * Contains path to often used file names.
+        */
+       struct path_cache cached_paths;
+
        /*
         * Path to the repository's graft file.
         * Cannot be NULL after initialization.
index e0862e27786244b1a98723d09475223cc001dd62..16c8aac6211ac74de77b1f2cad5eddb4d1abcc95 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -9,6 +9,7 @@
 #include "ll-merge.h"
 #include "attr.h"
 #include "pathspec.h"
+#include "object-store.h"
 #include "sha1-lookup.h"
 
 #define RESOLVED 0
@@ -200,7 +201,7 @@ static struct rerere_id *new_rerere_id(unsigned char *sha1)
 static void read_rr(struct string_list *rr)
 {
        struct strbuf buf = STRBUF_INIT;
-       FILE *in = fopen_or_warn(git_path_merge_rr(), "r");
+       FILE *in = fopen_or_warn(git_path_merge_rr(the_repository), "r");
 
        if (!in)
                return;
@@ -895,7 +896,8 @@ int setup_rerere(struct string_list *merge_rr, int flags)
        if (flags & RERERE_READONLY)
                fd = 0;
        else
-               fd = hold_lock_file_for_update(&write_lock, git_path_merge_rr(),
+               fd = hold_lock_file_for_update(&write_lock,
+                                              git_path_merge_rr(the_repository),
                                               LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
@@ -1245,6 +1247,6 @@ void rerere_clear(struct string_list *merge_rr)
                        rmdir(rerere_path(id, NULL));
                }
        }
-       unlink_or_warn(git_path_merge_rr());
+       unlink_or_warn(git_path_merge_rr(the_repository));
        rollback_lock_file(&write_lock);
 }
index 40fd91ff2b16bffc725f8602f45117279e27ba19..72abe235e4a8045b92d40b18c7d1fb574456e38c 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "tag.h"
 #include "blob.h"
 #include "tree.h"
@@ -29,6 +30,8 @@ volatile show_early_output_fn_t show_early_output;
 static const char *term_bad;
 static const char *term_good;
 
+implement_shared_commit_slab(revision_sources, char *);
+
 void show_object_with_name(FILE *out, struct object *obj, const char *name)
 {
        const char *p;
@@ -265,14 +268,19 @@ static struct commit *handle_commit(struct rev_info *revs,
         */
        if (object->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)object;
+
                if (parse_commit(commit) < 0)
                        die("unable to parse commit %s", name);
                if (flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
-               if (revs->show_source && !commit->util)
-                       commit->util = xstrdup(name);
+               if (revs->sources) {
+                       char **slot = revision_sources_at(revs->sources, commit);
+
+                       if (!*slot)
+                               *slot = xstrdup(name);
+               }
                return commit;
        }
 
@@ -824,8 +832,12 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                        }
                        return -1;
                }
-               if (revs->show_source && !p->util)
-                       p->util = commit->util;
+               if (revs->sources) {
+                       char **slot = revision_sources_at(revs->sources, p);
+
+                       if (!*slot)
+                               *slot = *revision_sources_at(revs->sources, commit);
+               }
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
index b8c47b98e22562ef320197b4ddbc8f0c3ee40f98..bf2239f87689d9486c4b888bed5cba6affa21953 100644 (file)
@@ -6,6 +6,7 @@
 #include "notes.h"
 #include "pretty.h"
 #include "diff.h"
+#include "commit-slab-decl.h"
 
 /* Remember to update object flag allocation in object.h */
 #define SEEN           (1u<<0)
@@ -29,6 +30,7 @@ struct rev_info;
 struct log_info;
 struct string_list;
 struct saved_parents;
+define_shared_commit_slab(revision_sources, char *);
 
 struct rev_cmdline_info {
        unsigned int nr;
@@ -111,7 +113,6 @@ struct rev_info {
                        right_only:1,
                        rewrite_parents:1,
                        print_parents:1,
-                       show_source:1,
                        show_decorations:1,
                        reverse:1,
                        reverse_output_stage:1,
@@ -224,6 +225,8 @@ struct rev_info {
 
        struct commit_list *previous_parents;
        const char *break_bar;
+
+       struct revision_sources *sources;
 };
 
 extern int ref_excluded(struct string_list *, const char *path);
index 19025a7aca82a7066b9a2d40d4d50406a9749a5f..e920ca57df4dd0b65d6694ed2532dead8ae83fb6 100644 (file)
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "commit.h"
 #include "refs.h"
+#include "object-store.h"
 #include "pkt-line.h"
 #include "sideband.h"
 #include "run-command.h"
@@ -75,7 +76,7 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
                argv_array_push(&po.args, "-q");
        if (args->progress)
                argv_array_push(&po.args, "--progress");
-       if (is_repository_shallow())
+       if (is_repository_shallow(the_repository))
                argv_array_push(&po.args, "--shallow");
        po.in = -1;
        po.out = args->stateless_rpc ? -1 : fd;
@@ -220,7 +221,7 @@ static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *c
 
 static void advertise_shallow_grafts_buf(struct strbuf *sb)
 {
-       if (!is_repository_shallow())
+       if (!is_repository_shallow(the_repository))
                return;
        for_each_commit_graft(advertise_shallow_grafts_cb, sb);
 }
@@ -537,7 +538,7 @@ int send_pack(struct send_pack_args *args,
        }
 
        if (args->stateless_rpc) {
-               if (!args->dry_run && (cmds_sent || is_repository_shallow())) {
+               if (!args->dry_run && (cmds_sent || is_repository_shallow(the_repository))) {
                        packet_buf_flush(&req_buf);
                        send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
                }
index 4034c0461b5022dad01b25d824cdc4f47ee09d13..39363a950f841afff28032e619a1c10e294bd857 100644 (file)
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "lockfile.h"
 #include "dir.h"
+#include "object-store.h"
 #include "object.h"
 #include "commit.h"
 #include "sequencer.h"
@@ -27,6 +28,7 @@
 #include "worktree.h"
 #include "oidmap.h"
 #include "oidset.h"
+#include "commit-slab.h"
 #include "alias.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -175,6 +177,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)
                        warning(_("invalid commit message cleanup mode '%s'"),
                                  s);
 
+               free((char *)s);
                return status;
        }
 
@@ -355,7 +358,7 @@ static void print_advice(int show_hint, struct replay_opts *opts)
                 * (typically rebase --interactive) wants to take care
                 * of the commit itself so remove CHERRY_PICK_HEAD
                 */
-               unlink(git_path_cherry_pick_head());
+               unlink(git_path_cherry_pick_head(the_repository));
                return;
        }
 
@@ -1322,8 +1325,8 @@ static int do_commit(const char *msg_file, const char *author,
                                    &oid);
                strbuf_release(&sb);
                if (!res) {
-                       unlink(git_path_cherry_pick_head());
-                       unlink(git_path_merge_msg());
+                       unlink(git_path_cherry_pick_head(the_repository));
+                       unlink(git_path_merge_msg(the_repository));
                        if (!is_rebase_i(opts))
                                print_commit_summary(NULL, &oid,
                                                SUMMARY_SHOW_AUTHOR_DATE);
@@ -1611,7 +1614,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                struct replay_opts *opts, int final_fixup)
 {
        unsigned int flags = opts->edit ? EDIT_MSG : 0;
-       const char *msg_file = opts->edit ? NULL : git_path_merge_msg();
+       const char *msg_file = opts->edit ? NULL : git_path_merge_msg(the_repository);
        struct object_id head;
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
@@ -1753,12 +1756,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
-                       const char *dest = git_path_squash_msg();
+                       const char *dest = git_path_squash_msg(the_repository);
                        unlink(dest);
                        if (copy_file(dest, rebase_path_squash_msg(), 0666))
                                return error(_("could not rename '%s' to '%s'"),
                                             rebase_path_squash_msg(), dest);
-                       unlink(git_path_merge_msg());
+                       unlink(git_path_merge_msg(the_repository));
                        msg_file = dest;
                        flags |= EDIT_MSG;
                }
@@ -1773,15 +1776,16 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                res = do_recursive_merge(base, next, base_label, next_label,
                                         &head, &msgbuf, opts);
                if (res < 0)
-                       return res;
+                       goto leave;
+
                res |= write_message(msgbuf.buf, msgbuf.len,
-                                    git_path_merge_msg(), 0);
+                                    git_path_merge_msg(the_repository), 0);
        } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
 
                res = write_message(msgbuf.buf, msgbuf.len,
-                                   git_path_merge_msg(), 0);
+                                   git_path_merge_msg(the_repository), 0);
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
@@ -2391,8 +2395,8 @@ static int rollback_single_pick(void)
 {
        struct object_id head_oid;
 
-       if (!file_exists(git_path_cherry_pick_head()) &&
-           !file_exists(git_path_revert_head()))
+       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
+           !file_exists(git_path_revert_head(the_repository)))
                return error(_("no cherry-pick or revert in progress"));
        if (read_ref_full("HEAD", 0, &head_oid, NULL))
                return error(_("cannot resolve HEAD"));
@@ -2617,10 +2621,11 @@ static int error_failed_squash(struct commit *commit,
        if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
                return error(_("could not copy '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
-       unlink(git_path_merge_msg());
-       if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
+       unlink(git_path_merge_msg(the_repository));
+       if (copy_file(git_path_merge_msg(the_repository), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
-                            rebase_path_message(), git_path_merge_msg());
+                            rebase_path_message(),
+                            git_path_merge_msg(the_repository));
        return error_with_patch(commit, subject, subject_len, opts, 1, 0);
 }
 
@@ -2910,11 +2915,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                write_author_script(message);
                find_commit_subject(message, &body);
                len = strlen(body);
-               ret = write_message(body, len, git_path_merge_msg(), 0);
+               ret = write_message(body, len, git_path_merge_msg(the_repository), 0);
                unuse_commit_buffer(commit, message);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg());
+                                   git_path_merge_msg(the_repository));
                        goto leave_merge;
                }
        } else {
@@ -2935,11 +2940,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                        len = buf.len;
                }
 
-               ret = write_message(p, len, git_path_merge_msg(), 0);
+               ret = write_message(p, len, git_path_merge_msg(the_repository), 0);
                strbuf_release(&buf);
                if (ret) {
                        error_errno(_("could not write '%s'"),
-                                   git_path_merge_msg());
+                                   git_path_merge_msg(the_repository));
                        goto leave_merge;
                }
        }
@@ -2976,8 +2981,8 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
        }
 
        write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
-                     git_path_merge_head(), 0);
-       write_message("no-ff", 5, git_path_merge_mode(), 0);
+                     git_path_merge_head(the_repository), 0);
+       write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
 
        bases = get_merge_bases(head_commit, merge_commit);
        if (bases && !oidcmp(&merge_commit->object.oid,
@@ -3031,7 +3036,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
                 * value (a negative one would indicate that the `merge`
                 * command needs to be rescheduled).
                 */
-               ret = !!run_git_commit(git_path_merge_msg(), opts,
+               ret = !!run_git_commit(git_path_merge_msg(the_repository), opts,
                                     run_commit_flags);
 
 leave_merge:
@@ -3214,10 +3219,27 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        intend_to_amend();
                                return error_failed_squash(item->commit, opts,
                                        item->arg_len, item->arg);
-                       } else if (res && is_rebase_i(opts) && item->commit)
+                       } else if (res && is_rebase_i(opts) && item->commit) {
+                               int to_amend = 0;
+                               struct object_id oid;
+
+                               /*
+                                * If we are rewording and have either
+                                * fast-forwarded already, or are about to
+                                * create a new root commit, we want to amend,
+                                * otherwise we do not.
+                                */
+                               if (item->command == TODO_REWORD &&
+                                   !get_oid("HEAD", &oid) &&
+                                   (!oidcmp(&item->commit->object.oid, &oid) ||
+                                    (opts->have_squash_onto &&
+                                     !oidcmp(&opts->squash_onto, &oid))))
+                                       to_amend = 1;
+
                                return res | error_with_patch(item->commit,
-                                       item->arg, item->arg_len, opts, res,
-                                       item->command == TODO_REWORD);
+                                               item->arg, item->arg_len, opts,
+                                               res, to_amend);
+                       }
                } else if (item->command == TODO_EXEC) {
                        char *end_of_arg = (char *)(item->arg + item->arg_len);
                        int saved = *end_of_arg;
@@ -3398,8 +3420,8 @@ static int continue_single_pick(void)
 {
        const char *argv[] = { "commit", NULL };
 
-       if (!file_exists(git_path_cherry_pick_head()) &&
-           !file_exists(git_path_revert_head()))
+       if (!file_exists(git_path_cherry_pick_head(the_repository)) &&
+           !file_exists(git_path_revert_head(the_repository)))
                return error(_("no cherry-pick or revert in progress"));
        return run_command_v_opt(argv, RUN_GIT_CMD);
 }
@@ -3502,7 +3524,7 @@ static int commit_staged_changes(struct replay_opts *opts,
        }
 
        if (is_clean) {
-               const char *cherry_pick_head = git_path_cherry_pick_head();
+               const char *cherry_pick_head = git_path_cherry_pick_head(the_repository);
 
                if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
@@ -3552,8 +3574,8 @@ int sequencer_continue(struct replay_opts *opts)
 
        if (!is_rebase_i(opts)) {
                /* Verify that the conflict has been resolved */
-               if (file_exists(git_path_cherry_pick_head()) ||
-                   file_exists(git_path_revert_head())) {
+               if (file_exists(git_path_cherry_pick_head(the_repository)) ||
+                   file_exists(git_path_revert_head(the_repository))) {
                        res = continue_single_pick();
                        if (res)
                                goto release_todo_list;
@@ -4238,6 +4260,7 @@ static enum check_level get_missing_commit_check_level(void)
        return CHECK_IGNORE;
 }
 
+define_commit_slab(commit_seen, unsigned char);
 /*
  * Check if the user dropped some commits by mistake
  * Behaviour determined by rebase.missingCommitsCheck.
@@ -4251,6 +4274,9 @@ int check_todo_list(void)
        struct todo_list todo_list = TODO_LIST_INIT;
        struct strbuf missing = STRBUF_INIT;
        int advise_to_edit_todo = 0, res = 0, i;
+       struct commit_seen commit_seen;
+
+       init_commit_seen(&commit_seen);
 
        strbuf_addstr(&todo_file, rebase_path_todo());
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
@@ -4267,7 +4293,7 @@ int check_todo_list(void)
        for (i = 0; i < todo_list.nr; i++) {
                struct commit *commit = todo_list.items[i].commit;
                if (commit)
-                       commit->util = (void *)1;
+                       *commit_seen_at(&commit_seen, commit) = 1;
        }
 
        todo_list_release(&todo_list);
@@ -4283,11 +4309,11 @@ int check_todo_list(void)
        for (i = todo_list.nr - 1; i >= 0; i--) {
                struct todo_item *item = todo_list.items + i;
                struct commit *commit = item->commit;
-               if (commit && !commit->util) {
+               if (commit && !*commit_seen_at(&commit_seen, commit)) {
                        strbuf_addf(&missing, " - %s %.*s\n",
                                    short_commit_name(commit),
                                    item->arg_len, item->arg);
-                       commit->util = (void *)1;
+                       *commit_seen_at(&commit_seen, commit) = 1;
                }
        }
 
@@ -4313,6 +4339,7 @@ int check_todo_list(void)
                "The possible behaviours are: ignore, warn, error.\n\n"));
 
 leave_check:
+       clear_commit_seen(&commit_seen);
        strbuf_release(&todo_file);
        todo_list_release(&todo_list);
 
@@ -4433,6 +4460,8 @@ static int subject2item_cmp(const void *fndata,
        return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject);
 }
 
+define_commit_slab(commit_todo_item, struct todo_item *);
+
 /*
  * Rearrange the todo list that has both "pick commit-id msg" and "pick
  * commit-id fixup!/squash! msg" in it so that the latter is put immediately
@@ -4449,6 +4478,7 @@ int rearrange_squash(void)
        struct hashmap subject2item;
        int res = 0, rearranged = 0, *next, *tail, i;
        char **subjects;
+       struct commit_todo_item commit_todo;
 
        if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
                return -1;
@@ -4457,6 +4487,7 @@ int rearrange_squash(void)
                return -1;
        }
 
+       init_commit_todo_item(&commit_todo);
        /*
         * The hashmap maps onelines to the respective todo list index.
         *
@@ -4487,10 +4518,11 @@ int rearrange_squash(void)
 
                if (is_fixup(item->command)) {
                        todo_list_release(&todo_list);
+                       clear_commit_todo_item(&commit_todo);
                        return error(_("the script was already rearranged."));
                }
 
-               item->commit->util = item;
+               *commit_todo_item_at(&commit_todo, item->commit) = item;
 
                parse_commit(item->commit);
                commit_buffer = get_commit_buffer(item->commit, NULL);
@@ -4517,9 +4549,9 @@ int rearrange_squash(void)
                        else if (!strchr(p, ' ') &&
                                 (commit2 =
                                  lookup_commit_reference_by_name(p)) &&
-                                commit2->util)
+                                *commit_todo_item_at(&commit_todo, commit2))
                                /* found by commit name */
-                               i2 = (struct todo_item *)commit2->util
+                               i2 = *commit_todo_item_at(&commit_todo, commit2)
                                        - todo_list.items;
                        else {
                                /* copy can be a prefix of the commit subject */
@@ -4596,5 +4628,6 @@ int rearrange_squash(void)
        hashmap_free(&subject2item, 1);
        todo_list_release(&todo_list);
 
+       clear_commit_todo_item(&commit_todo);
        return res;
 }
index 695e5c62764cf4befe09467f1a658b25c01baba1..de4839e634c02cb7497f05a3a4597b9fa5123e32 100644 (file)
@@ -1801,7 +1801,7 @@ static void check_commit(const void *buf, size_t size)
 {
        struct commit c;
        memset(&c, 0, sizeof(c));
-       if (parse_commit_buffer(&c, buf, size))
+       if (parse_commit_buffer(&c, buf, size, 0))
                die("corrupt commit");
 }
 
index 9ff83cabcd3be2614f7ed770884ac237294b05e4..e53067cdede4865e144713502055a04cf230c926 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -1,6 +1,8 @@
 #include "cache.h"
+#include "repository.h"
 #include "tempfile.h"
 #include "lockfile.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
 #include "commit-slab.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "commit-slab.h"
+#include "repository.h"
 
-static int is_shallow = -1;
-static struct stat_validity shallow_stat;
-static char *alternate_shallow_file;
-
-void set_alternate_shallow_file(const char *path, int override)
+void set_alternate_shallow_file(struct repository *r, const char *path, int override)
 {
-       if (is_shallow != -1)
+       if (r->parsed_objects->is_shallow != -1)
                BUG("is_repository_shallow must not be called before set_alternate_shallow_file");
-       if (alternate_shallow_file && !override)
+       if (r->parsed_objects->alternate_shallow_file && !override)
                return;
-       free(alternate_shallow_file);
-       alternate_shallow_file = xstrdup_or_null(path);
+       free(r->parsed_objects->alternate_shallow_file);
+       r->parsed_objects->alternate_shallow_file = xstrdup_or_null(path);
 }
 
-int register_shallow(const struct object_id *oid)
+int register_shallow(struct repository *r, const struct object_id *oid)
 {
        struct commit_graft *graft =
                xmalloc(sizeof(struct commit_graft));
@@ -37,43 +37,48 @@ int register_shallow(const struct object_id *oid)
        graft->nr_parent = -1;
        if (commit && commit->object.parsed)
                commit->parents = NULL;
-       return register_commit_graft(graft, 0);
+       return register_commit_graft(r, graft, 0);
 }
 
-int is_repository_shallow(void)
+int is_repository_shallow(struct repository *r)
 {
        FILE *fp;
        char buf[1024];
-       const char *path = alternate_shallow_file;
+       const char *path = r->parsed_objects->alternate_shallow_file;
 
-       if (is_shallow >= 0)
-               return is_shallow;
+       if (r->parsed_objects->is_shallow >= 0)
+               return r->parsed_objects->is_shallow;
 
        if (!path)
-               path = git_path_shallow();
+               path = git_path_shallow(r);
        /*
         * fetch-pack sets '--shallow-file ""' as an indicator that no
         * shallow file should be used. We could just open it and it
         * will likely fail. But let's do an explicit check instead.
         */
        if (!*path || (fp = fopen(path, "r")) == NULL) {
-               stat_validity_clear(&shallow_stat);
-               is_shallow = 0;
-               return is_shallow;
+               stat_validity_clear(r->parsed_objects->shallow_stat);
+               r->parsed_objects->is_shallow = 0;
+               return r->parsed_objects->is_shallow;
        }
-       stat_validity_update(&shallow_stat, fileno(fp));
-       is_shallow = 1;
+       stat_validity_update(r->parsed_objects->shallow_stat, fileno(fp));
+       r->parsed_objects->is_shallow = 1;
 
        while (fgets(buf, sizeof(buf), fp)) {
                struct object_id oid;
                if (get_oid_hex(buf, &oid))
                        die("bad shallow line: %s", buf);
-               register_shallow(&oid);
+               register_shallow(r, &oid);
        }
        fclose(fp);
-       return is_shallow;
+       return r->parsed_objects->is_shallow;
 }
 
+/*
+ * TODO: use "int" elemtype instead of "int *" when/if commit-slab
+ * supports a "valid" flag.
+ */
+define_commit_slab(commit_depth, int *);
 struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                int shallow_flag, int not_shallow_flag)
 {
@@ -82,32 +87,36 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
        struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
        struct commit_graft *graft;
+       struct commit_depth depths;
 
+       init_commit_depth(&depths);
        while (commit || i < heads->nr || stack.nr) {
                struct commit_list *p;
                if (!commit) {
                        if (i < heads->nr) {
+                               int **depth_slot;
                                commit = (struct commit *)
                                        deref_tag(heads->objects[i++].item, NULL, 0);
                                if (!commit || commit->object.type != OBJ_COMMIT) {
                                        commit = NULL;
                                        continue;
                                }
-                               if (!commit->util)
-                                       commit->util = xmalloc(sizeof(int));
-                               *(int *)commit->util = 0;
+                               depth_slot = commit_depth_at(&depths, commit);
+                               if (!*depth_slot)
+                                       *depth_slot = xmalloc(sizeof(int));
+                               **depth_slot = 0;
                                cur_depth = 0;
                        } else {
                                commit = (struct commit *)
                                        object_array_pop(&stack);
-                               cur_depth = *(int *)commit->util;
+                               cur_depth = **commit_depth_at(&depths, commit);
                        }
                }
                parse_commit_or_die(commit);
                cur_depth++;
                if ((depth != INFINITE_DEPTH && cur_depth >= depth) ||
-                   (is_repository_shallow() && !commit->parents &&
-                    (graft = lookup_commit_graft(&commit->object.oid)) != NULL &&
+                   (is_repository_shallow(the_repository) && !commit->parents &&
+                    (graft = lookup_commit_graft(the_repository, &commit->object.oid)) != NULL &&
                     graft->nr_parent < 0)) {
                        commit_list_insert(commit, &result);
                        commit->object.flags |= shallow_flag;
@@ -116,25 +125,31 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                }
                commit->object.flags |= not_shallow_flag;
                for (p = commit->parents, commit = NULL; p; p = p->next) {
-                       if (!p->item->util) {
-                               int *pointer = xmalloc(sizeof(int));
-                               p->item->util = pointer;
-                               *pointer =  cur_depth;
+                       int **depth_slot = commit_depth_at(&depths, p->item);
+                       if (!*depth_slot) {
+                               *depth_slot = xmalloc(sizeof(int));
+                               **depth_slot = cur_depth;
                        } else {
-                               int *pointer = p->item->util;
-                               if (cur_depth >= *pointer)
+                               if (cur_depth >= **depth_slot)
                                        continue;
-                               *pointer = cur_depth;
+                               **depth_slot = cur_depth;
                        }
                        if (p->next)
                                add_object_array(&p->item->object,
                                                NULL, &stack);
                        else {
                                commit = p->item;
-                               cur_depth = *(int *)commit->util;
+                               cur_depth = **commit_depth_at(&depths, commit);
                        }
                }
        }
+       for (i = 0; i < depths.slab_count; i++) {
+               int j;
+
+               for (j = 0; j < depths.slab_size; j++)
+                       free(depths.slab[i][j]);
+       }
+       clear_commit_depth(&depths);
 
        return result;
 }
@@ -165,7 +180,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
         */
        clear_object_flags(both_flags);
 
-       is_repository_shallow(); /* make sure shallows are read */
+       is_repository_shallow(the_repository); /* make sure shallows are read */
 
        init_revisions(&revs, NULL);
        save_commit_buffer = 0;
@@ -175,6 +190,9 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
                die("revision walk setup failed");
        traverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);
 
+       if (!not_shallow_list)
+               die("no commits selected for shallow requests");
+
        /* Mark all reachable commits as NOT_SHALLOW */
        for (p = not_shallow_list; p; p = p->next)
                p->item->object.flags |= not_shallow_flag;
@@ -215,12 +233,12 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
        return result;
 }
 
-static void check_shallow_file_for_update(void)
+static void check_shallow_file_for_update(struct repository *r)
 {
-       if (is_shallow == -1)
+       if (r->parsed_objects->is_shallow == -1)
                BUG("shallow must be initialized by now");
 
-       if (!stat_validity_check(&shallow_stat, git_path_shallow()))
+       if (!stat_validity_check(r->parsed_objects->shallow_stat, git_path_shallow(the_repository)))
                die("shallow file has changed since we read it");
 }
 
@@ -315,9 +333,10 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
        struct strbuf sb = STRBUF_INIT;
        int fd;
 
-       fd = hold_lock_file_for_update(shallow_lock, git_path_shallow(),
+       fd = hold_lock_file_for_update(shallow_lock,
+                                      git_path_shallow(the_repository),
                                       LOCK_DIE_ON_ERROR);
-       check_shallow_file_for_update();
+       check_shallow_file_for_update(the_repository);
        if (write_shallow_commits(&sb, 0, extra)) {
                if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
@@ -342,7 +361,7 @@ static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *c
 
 void advertise_shallow_grafts(int fd)
 {
-       if (!is_repository_shallow())
+       if (!is_repository_shallow(the_repository))
                return;
        for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
 }
@@ -362,16 +381,17 @@ void prune_shallow(int show_only)
                strbuf_release(&sb);
                return;
        }
-       fd = hold_lock_file_for_update(&shallow_lock, git_path_shallow(),
+       fd = hold_lock_file_for_update(&shallow_lock,
+                                      git_path_shallow(the_repository),
                                       LOCK_DIE_ON_ERROR);
-       check_shallow_file_for_update();
+       check_shallow_file_for_update(the_repository);
        if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
                if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
                                  get_lock_file_path(&shallow_lock));
                commit_lock_file(&shallow_lock);
        } else {
-               unlink(git_path_shallow());
+               unlink(git_path_shallow(the_repository));
                rollback_lock_file(&shallow_lock);
        }
        strbuf_release(&sb);
@@ -396,7 +416,8 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
        for (i = 0; i < sa->nr; i++) {
                if (has_object_file(sa->oid + i)) {
                        struct commit_graft *graft;
-                       graft = lookup_commit_graft(&sa->oid[i]);
+                       graft = lookup_commit_graft(the_repository,
+                                                   &sa->oid[i]);
                        if (graft && graft->nr_parent < 0)
                                continue;
                        info->ours[info->nr_ours++] = i;
diff --git a/show-index.c b/show-index.c
deleted file mode 100644 (file)
index 1ead41e..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "cache.h"
-#include "pack.h"
-
-static const char show_index_usage[] =
-"git show-index";
-
-int cmd_main(int argc, const char **argv)
-{
-       int i;
-       unsigned nr;
-       unsigned int version;
-       static unsigned int top_index[256];
-
-       if (argc != 1)
-               usage(show_index_usage);
-       if (fread(top_index, 2 * 4, 1, stdin) != 1)
-               die("unable to read header");
-       if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
-               version = ntohl(top_index[1]);
-               if (version < 2 || version > 2)
-                       die("unknown index version");
-               if (fread(top_index, 256 * 4, 1, stdin) != 1)
-                       die("unable to read index");
-       } else {
-               version = 1;
-               if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
-                       die("unable to read index");
-       }
-       nr = 0;
-       for (i = 0; i < 256; i++) {
-               unsigned n = ntohl(top_index[i]);
-               if (n < nr)
-                       die("corrupt index file");
-               nr = n;
-       }
-       if (version == 1) {
-               for (i = 0; i < nr; i++) {
-                       unsigned int offset, entry[6];
-
-                       if (fread(entry, 4 + 20, 1, stdin) != 1)
-                               die("unable to read entry %u/%u", i, nr);
-                       offset = ntohl(entry[0]);
-                       printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
-               }
-       } else {
-               unsigned off64_nr = 0;
-               struct {
-                       unsigned char sha1[20];
-                       uint32_t crc;
-                       uint32_t off;
-               } *entries;
-               ALLOC_ARRAY(entries, nr);
-               for (i = 0; i < nr; i++)
-                       if (fread(entries[i].sha1, 20, 1, stdin) != 1)
-                               die("unable to read sha1 %u/%u", i, nr);
-               for (i = 0; i < nr; i++)
-                       if (fread(&entries[i].crc, 4, 1, stdin) != 1)
-                               die("unable to read crc %u/%u", i, nr);
-               for (i = 0; i < nr; i++)
-                       if (fread(&entries[i].off, 4, 1, stdin) != 1)
-                               die("unable to read 32b offset %u/%u", i, nr);
-               for (i = 0; i < nr; i++) {
-                       uint64_t offset;
-                       uint32_t off = ntohl(entries[i].off);
-                       if (!(off & 0x80000000)) {
-                               offset = off;
-                       } else {
-                               uint32_t off64[2];
-                               if ((off & 0x7fffffff) != off64_nr)
-                                       die("inconsistent 64b offset index");
-                               if (fread(off64, 8, 1, stdin) != 1)
-                                       die("unable to read 64b offset %u", off64_nr);
-                               offset = (((uint64_t)ntohl(off64[0])) << 32) |
-                                                    ntohl(off64[1]);
-                               off64_nr++;
-                       }
-                       printf("%" PRIuMAX " %s (%08"PRIx32")\n",
-                              (uintmax_t) offset,
-                              sha1_to_hex(entries[i].sha1),
-                              ntohl(entries[i].crc));
-               }
-               free(entries);
-       }
-       return 0;
-}
index 388ef1f892bdd63a485d9c93810a945fc3eab46f..ca324a9e37d8ce738db417e2cb689cbe0508cff0 100644 (file)
@@ -4,6 +4,7 @@
 #include "submodule-config.h"
 #include "submodule.h"
 #include "strbuf.h"
+#include "object-store.h"
 #include "parse-options.h"
 
 /*
index 939d6870ecd7cfbc62f9fa3fb867e5c7362ed9bd..d3a9aab83dc90b0c7797cc4b83d0a83bf6c196b7 100644 (file)
@@ -740,12 +740,14 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
                else {
                        name = default_name_or_path(p->two->path);
                        /* make sure name does not collide with existing one */
-                       submodule = submodule_from_name(the_repository, commit_oid, name);
+                       if (name)
+                               submodule = submodule_from_name(the_repository,
+                                                               commit_oid, name);
                        if (submodule) {
                                warning("Submodule in commit %s at path: "
                                        "'%s' collides with a submodule named "
                                        "the same. Skipping it.",
-                                       oid_to_hex(commit_oid), name);
+                                       oid_to_hex(commit_oid), p->two->path);
                                name = NULL;
                        }
                }
@@ -1532,6 +1534,18 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
        return ret;
 }
 
+void submodule_unset_core_worktree(const struct submodule *sub)
+{
+       char *config_path = xstrfmt("%s/modules/%s/config",
+                                   get_git_common_dir(), sub->name);
+
+       if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+               warning(_("Could not unset core.worktree setting in submodule '%s'"),
+                         sub->path);
+
+       free(config_path);
+}
+
 static const char *get_super_prefix_or_empty(void)
 {
        const char *s = get_super_prefix();
@@ -1697,6 +1711,8 @@ int submodule_move_head(const char *path,
 
                        if (is_empty_dir(path))
                                rmdir_or_warn(path);
+
+                       submodule_unset_core_worktree(sub);
                }
        }
 out:
index 7856b8a0b3d676cde20095f69a4d000d8cf38615..4644683e6cb39916a942e501eacb8663d89c3f6b 100644 (file)
@@ -121,6 +121,8 @@ extern int submodule_move_head(const char *path,
                               const char *new_head,
                               unsigned flags);
 
+void submodule_unset_core_worktree(const struct submodule *sub);
+
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
  * a submodule by clearing any repo-specific environment variables, but
index 1f38a85371ab49790d14d3d4b82a0ee6e52789dd..aa5ac03325a8c5038e9b568fceb1e953f4a0dcf4 100755 (executable)
@@ -235,7 +235,7 @@ reset_work_tree_to_interested () {
        then
                mkdir -p submodule_update/.git/modules/sub1/modules &&
                cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
-               GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+               # core.worktree is unset for sub2 as it is not checked out
        fi &&
        # indicate we are interested in the submodule:
        git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() {
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1
+                       ! test -e sub1 &&
+                       test_must_fail git config -f .git/modules/sub1/config core.worktree
                )
        '
        # ... absorbing a .git directory along the way.
index c413bff9cf1f3a79ef494b39844c42d3a8c877f1..4c865051e7dd3d27eee0e2666b4aaaaa46116d5c 100755 (executable)
@@ -287,6 +287,7 @@ test_expect_success 'init notices EEXIST (2)' '
 '
 
 test_expect_success POSIXPERM,SANITY 'init notices EPERM' '
+       test_when_finished "chmod +w newdir" &&
        rm -fr newdir &&
        mkdir newdir &&
        chmod -w newdir &&
index 71350e0657986d7b3e38801d82058c53f9fd23d0..5f056982a53b62810192fc8bbb5dbb6d784fb1a2 100755 (executable)
@@ -98,6 +98,16 @@ test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
 '
 
 
+test_expect_success 'safecrlf: no warning with safecrlf=false' '
+       git config core.autocrlf input &&
+       git config core.safecrlf false &&
+
+       for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+       git add allcrlf 2>err &&
+       test_must_be_empty err
+'
+
+
 test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
        git config core.autocrlf false &&
        git config core.safecrlf false &&
index 23fbe6434abd3f05057d4916f70e5d5b30115b68..7b111a56fdd3e13d34b538e52c2659a8f2e188a2 100755 (executable)
@@ -19,8 +19,8 @@ test_expect_success 'mktemp to nonexistent directory prints filename' '
 
 test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' '
        mkdir cannotwrite &&
-       chmod -w cannotwrite &&
        test_when_finished "chmod +w cannotwrite" &&
+       chmod -w cannotwrite &&
        test_must_fail test-tool mktemp cannotwrite/testXXXXXX 2>err &&
        grep "cannotwrite/test" err
 '
index cc18b75c03880eb9018b0d37aa9a26f7964dcee7..4984ca583d03a7cc0a6fb9323799e776eee4565b 100755 (executable)
@@ -23,7 +23,15 @@ promise_and_delete () {
        delete_object repo "$HASH"
 }
 
+test_expect_success 'extensions.partialclone without filter' '
+       test_create_repo server &&
+       git clone --filter="blob:none" "file://$(pwd)/server" client &&
+       git -C client config --unset core.partialclonefilter &&
+       git -C client fetch origin
+'
+
 test_expect_success 'missing reflog object, but promised by a commit, passes fsck' '
+       rm -rf repo &&
        test_create_repo repo &&
        test_commit -C repo my_commit &&
 
index c7ce5d8bb588215d528fad5dfa6aa094d2a5d468..826cd32e231607e3cd46f9c0314908583c01a093 100755 (executable)
@@ -179,6 +179,8 @@ test_expect_success 'funny symlink in work tree' '
 
 test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
 
+       test_when_finished "chmod u+w a 2>/dev/null; rm -fr a b" &&
+
        rm -fr a b &&
        git reset --hard &&
 
@@ -188,10 +190,6 @@ test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
 
 '
 
-# clean-up from the above test
-chmod a+w a 2>/dev/null
-rm -fr a b
-
 test_expect_success 'D/F setup' '
 
        git reset --hard &&
index 04d840a5448567bf438688c4aebd7e94d63da4ec..e7a400b4c77a822a49365e4049fbe68b059d23ee 100755 (executable)
@@ -70,8 +70,7 @@ test_expect_success 'i-t-a entry is simply ignored' '
        git commit -m second &&
        test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
        test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 &&
-       test $(git diff --name-only --ita-invisible-in-index HEAD -- nitfol | wc -l) = 0 &&
-       test $(git diff --name-only --ita-invisible-in-index -- nitfol | wc -l) = 1
+       test $(git diff --name-only -- nitfol | wc -l) = 1
 '
 
 test_expect_success 'can commit with an unrelated i-t-a entry in index' '
@@ -99,13 +98,13 @@ test_expect_success 'cache-tree invalidates i-t-a paths' '
 
        : >dir/bar &&
        git add -N dir/bar &&
-       git diff --cached --name-only >actual &&
+       git diff --name-only >actual &&
        echo dir/bar >expect &&
        test_cmp expect actual &&
 
        git write-tree >/dev/null &&
 
-       git diff --cached --name-only >actual &&
+       git diff --name-only >actual &&
        echo dir/bar >expect &&
        test_cmp expect actual
 '
@@ -186,7 +185,19 @@ test_expect_success 'rename detection finds the right names' '
                cat >expected.3 <<-EOF &&
                2 .R N... 100644 100644 100644 $hash $hash R100 third   first
                EOF
-               test_cmp expected.3 actual.3
+               test_cmp expected.3 actual.3 &&
+
+               git diff --stat >actual.4 &&
+               cat >expected.4 <<-EOF &&
+                first => third | 0
+                1 file changed, 0 insertions(+), 0 deletions(-)
+               EOF
+               test_cmp expected.4 actual.4 &&
+
+               git diff --cached --stat >actual.5 &&
+               : >expected.5 &&
+               test_cmp expected.5 actual.5
+
        )
 '
 
@@ -222,5 +233,46 @@ test_expect_success 'double rename detection in status' '
        )
 '
 
-test_done
+test_expect_success 'diff-files/diff-cached shows ita as new/not-new files' '
+       git reset --hard &&
+       echo new >new-ita &&
+       git add -N new-ita &&
+       git diff --summary >actual &&
+       echo " create mode 100644 new-ita" >expected &&
+       test_cmp expected actual &&
+       git diff --cached --summary >actual2 &&
+       : >expected2 &&
+       test_cmp expected2 actual2
+'
+
 
+test_expect_success '"diff HEAD" includes ita as new files' '
+       git reset --hard &&
+       echo new >new-ita &&
+       git add -N new-ita &&
+       git diff HEAD >actual &&
+       cat >expected <<-\EOF &&
+       diff --git a/new-ita b/new-ita
+       new file mode 100644
+       index 0000000..3e75765
+       --- /dev/null
+       +++ b/new-ita
+       @@ -0,0 +1 @@
+       +new
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'apply --intent-to-add' '
+       git reset --hard &&
+       echo new >new-ita &&
+       git add -N new-ita &&
+       git diff >expected &&
+       grep "new file" expected &&
+       git reset --hard &&
+       git apply --intent-to-add expected &&
+       git diff >actual &&
+       test_cmp expected actual
+'
+
+test_done
index 352a52e59d1a25d6fe50c5001ba3c84e2fc4a30c..d392160ba9957d9918f59cdfb031f66c624e90f3 100755 (executable)
@@ -981,7 +981,35 @@ test_expect_success 'rebase -i --root reword root commit' '
        test -z "$(git show -s --format=%p HEAD^)"
 '
 
+test_expect_success 'rebase -i --root when root has untracked file confilct' '
+       test_when_finished "reset_rebase" &&
+       git checkout -b failing-root-pick A &&
+       echo x >file2 &&
+       git rm file1 &&
+       git commit -m "remove file 1 add file 2" &&
+       echo z >file1 &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
+       rm file1 &&
+       git rebase --continue &&
+       test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
+       test "$(git rev-list --count HEAD)" = 2
+'
+
+test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
+       test_when_finished "reset_rebase" &&
+       echo z>file1 &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="reword 1 2" \
+               FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
+       rm file1 &&
+       FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
+       test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
+       test "$(git rev-list --count HEAD)" = 2
+'
+
 test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
+       git checkout reword-root-branch &&
        git reset --hard &&
        git checkout conflict-branch &&
        set_fake_editor &&
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
new file mode 100755 (executable)
index 0000000..6963750
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='git rebase interactive with rewording'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup' '
+       test_commit master file-1 test &&
+
+       git checkout -b stuff &&
+
+       test_commit feature_a file-2 aaa &&
+       test_commit feature_b file-2 ddd
+'
+
+test_expect_success 'reword without issues functions as intended' '
+       test_when_finished "reset_rebase" &&
+
+       git checkout stuff^0 &&
+
+       set_fake_editor &&
+       FAKE_LINES="pick 1 reword 2" FAKE_COMMIT_MESSAGE="feature_b_reworded" \
+               git rebase -i -v master &&
+
+       test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+       test $(git rev-list --count HEAD) = 3
+'
+
+test_expect_success 'reword after a conflict preserves commit' '
+       test_when_finished "reset_rebase" &&
+
+       git checkout stuff^0 &&
+
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="reword 2" \
+               git rebase -i -v master &&
+
+       git checkout --theirs file-2 &&
+       git add file-2 &&
+       FAKE_COMMIT_MESSAGE="feature_b_reworded" git rebase --continue &&
+
+       test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+       test $(git rev-list --count HEAD) = 2
+'
+
+test_done
index b170fb02b80356455d03dcf379636778149665a6..3e9139dca88e83165fc21e6dce06f3243bd49cd7 100755 (executable)
@@ -175,6 +175,49 @@ test_expect_success 'real edit works' '
        diff_cmp expected output
 '
 
+test_expect_success 'setup file' '
+       test_write_lines a "" b "" c >file &&
+       git add file &&
+       test_write_lines a "" d "" c >file
+'
+
+test_expect_success 'setup patch' '
+       SP=" " &&
+       NULL="" &&
+       cat >patch <<-EOF
+       @@ -1,4 +1,4 @@
+        a
+       $NULL
+       -b
+       +f
+       $SP
+       c
+       EOF
+'
+
+test_expect_success 'setup expected' '
+       cat >expected <<-EOF
+       diff --git a/file b/file
+       index b5dd6c9..f910ae9 100644
+       --- a/file
+       +++ b/file
+       @@ -1,5 +1,5 @@
+        a
+       $SP
+       -f
+       +d
+       $SP
+        c
+       EOF
+'
+
+test_expect_success 'edit can strip spaces from empty context lines' '
+       test_write_lines e n q | git add -p 2>error &&
+       test_must_be_empty error &&
+       git diff >output &&
+       diff_cmp expected output
+'
+
 test_expect_success 'skip files similarly as commit -a' '
        git reset &&
        echo file >.gitignore &&
index cf0f3a1ee75dd28c6b5ce2a5db7c3ad6afafd21f..108c012a3a66d900cbbfcda04ac2dfe5ffeb6362 100755 (executable)
@@ -139,11 +139,13 @@ test_expect_success SYMLINKS 'setup symlinks with attributes' '
 test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
        cat >expect <<-\EOF &&
        diff --git a/file.bin b/file.bin
-       index e69de29..d95f3ad 100644
-       Binary files a/file.bin and b/file.bin differ
+       new file mode 100644
+       index 0000000..d95f3ad
+       Binary files /dev/null and b/file.bin differ
        diff --git a/link.bin b/link.bin
-       index e69de29..dce41ec 120000
-       --- a/link.bin
+       new file mode 120000
+       index 0000000..dce41ec
+       --- /dev/null
        +++ b/link.bin
        @@ -0,0 +1 @@
        +file.bin
index 028d5507a6b684208ba82292379ec74bc6628574..53880da7bbe85521112ced3ff94ead05587c7e81 100755 (executable)
@@ -1554,13 +1554,15 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
 
 test_expect_success 'format-patch --base' '
        git checkout side &&
-       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual &&
+       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
+       git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
        echo >expected &&
        echo "base-commit: $(git rev-parse HEAD~3)" >>expected &&
        echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
        echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
        signature >> expected &&
-       test_cmp expected actual
+       test_cmp expected actual1 &&
+       test_cmp expected actual2
 '
 
 test_expect_success 'format-patch --base errors out when base commit is in revision list' '
index 168739c7214c764ef7f35a83063d369c1db22ec4..fd3bdbfe2c0a30a0b712b1a59c2bacf0efde167c 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success setup '
 #   fatal: unable to write file '(null)' mode 100644: Bad address
 # Also, it had the unwanted side-effect of deleting f.
 test_expect_success 'try to apply corrupted patch' '
-       test_must_fail git am bad-patch.diff 2>actual
+       test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual
 '
 
 test_expect_success 'compare diagnostic; ensure file is still here' '
index a380419b65bdf1ecace4c23831e76bc8a7d81962..77d85aefe7da18a0d7cc2db2baaedacead6b2e97 100755 (executable)
@@ -221,4 +221,13 @@ test_expect_success 'write graph in bare repo' '
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
 
+test_expect_success 'perform fast-forward merge in full repo' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git checkout -b merge-5-to-8 commits/5 &&
+       git merge commits/8 &&
+       git show-ref -s merge-5-to-8 >output &&
+       git show-ref -s commits/8 >expect &&
+       test_cmp expect output
+'
+
 test_done
index d4f435155f5c9350f196904b910e2b5466c3ffd6..ea6570e81990d73dc07d03c3bb6e6af5c8f029f4 100755 (executable)
@@ -518,6 +518,47 @@ test_expect_success 'test --all, --depth, and explicit tag' '
        ) >out-adt 2>error-adt
 '
 
+test_expect_success 'test --all with tag to non-tip' '
+       git commit --allow-empty -m non-tip &&
+       git commit --allow-empty -m tip &&
+       git tag -m "annotated" non-tip HEAD^ &&
+       (
+               cd client &&
+               git fetch-pack --all ..
+       )
+'
+
+test_expect_success 'test --all wrt tag to non-commits' '
+       # create tag-to-{blob,tree,commit,tag}, making sure all tagged objects
+       # are reachable only via created tag references.
+       blob=$(echo "hello blob" | git hash-object -t blob -w --stdin) &&
+       git tag -a -m "tag -> blob" tag-to-blob $blob &&
+ \
+       tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
+       git tag -a -m "tag -> tree" tag-to-tree $tree &&
+ \
+       tree2=$(printf "100644 blob $blob\tfile2" | git mktree) &&
+       commit=$(git commit-tree -m "hello commit" $tree) &&
+       git tag -a -m "tag -> commit" tag-to-commit $commit &&
+ \
+       blob2=$(echo "hello blob2" | git hash-object -t blob -w --stdin) &&
+       tag=$(printf "object $blob2\ntype blob\ntag tag-to-blob2\n\
+tagger author A U Thor <author@example.com> 0 +0000\n\nhello tag" | git mktag) &&
+       git tag -a -m "tag -> tag" tag-to-tag $tag &&
+ \
+       # `fetch-pack --all` should succeed fetching all those objects.
+       mkdir fetchall &&
+       (
+               cd fetchall &&
+               git init &&
+               git fetch-pack --all .. &&
+               git cat-file blob $blob >/dev/null &&
+               git cat-file tree $tree >/dev/null &&
+               git cat-file commit $commit >/dev/null &&
+               git cat-file tag $tag >/dev/null
+       )
+'
+
 test_expect_success 'shallow fetch with tags does not break the repository' '
        mkdir repo1 &&
        (
@@ -711,6 +752,17 @@ test_expect_success 'fetch shallow since ...' '
        test_cmp expected actual
 '
 
+test_expect_success 'clone shallow since selects no commits' '
+       test_create_repo shallow-since-the-future &&
+       (
+       cd shallow-since-the-future &&
+       GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one &&
+       GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two &&
+       GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three &&
+       test_must_fail git clone --shallow-since "900000000 +0700" "file://$(pwd)/." ../shallow111
+       )
+'
+
 test_expect_success 'shallow clone exclude tag two' '
        test_create_repo shallow-exclude &&
        (
index 9cc4b569c0566da2f5c2778f404e67e20b29ed06..359e03ff836cf6ac087c83c5b7abe7365ecb4447 100755 (executable)
@@ -574,11 +574,7 @@ test_expect_success "fetch new commits when submodule got renamed" '
        git clone . downstream_rename &&
        (
                cd downstream_rename &&
-               git submodule update --init &&
-# NEEDSWORK: we omitted --recursive for the submodule update here since
-# that does not work. See test 7001 for mv "moving nested submodules"
-# for details. Once that is fixed we should add the --recursive option
-# here.
+               git submodule update --init --recursive &&
                git checkout -b rename &&
                git mv submodule submodule_renamed &&
                (
index df8d2f095a40f524b8318b98a7c4fb085d8010f4..943231af9638d14641811ea8196f8afdafdf6871 100755 (executable)
@@ -175,8 +175,8 @@ EOF
 
 test_expect_success POSIXPERM,SANITY 'shallow fetch from a read-only repo' '
        cp -R .git read-only.git &&
-       find read-only.git -print | xargs chmod -w &&
        test_when_finished "find read-only.git -type d -print | xargs chmod +w" &&
+       find read-only.git -print | xargs chmod -w &&
        git clone --no-local --depth=2 read-only.git from-read-only &&
        git --git-dir=from-read-only/.git log --format=%s >actual &&
        cat >expect <<EOF &&
index 9594e891f45d4c0d29b73190c1b5345b4c4d54b6..747775c147612f3b09a599661fe3dc7d3b1d34bd 100755 (executable)
@@ -29,7 +29,7 @@ test_expect_success GPG 'create repositories with signed commits' '
                echo 4 >d && git add d &&
                test_tick && git commit -S -m "bad" &&
                git cat-file commit HEAD >raw &&
-               sed -e "s/bad/forged bad/" raw >forged &&
+               sed -e "s/^bad/forged bad/" raw >forged &&
                git hash-object -w -t commit forged >forged.commit &&
                git checkout $(cat forged.commit)
        ) &&
index 18aa88b5c096c14f2f8a3a8d8c6488127fddf055..b5621303d67d47146b4af8ee79fe55c338674071 100755 (executable)
@@ -4,12 +4,6 @@ test_description='recursive merge corner cases involving criss-cross merges'
 
 . ./test-lib.sh
 
-get_clean_checkout () {
-       git reset --hard &&
-       git clean -fdqx &&
-       git checkout "$1"
-}
-
 #
 #  L1  L2
 #   o---o
@@ -21,51 +15,66 @@ get_clean_checkout () {
 #
 
 test_expect_success 'setup basic criss-cross + rename with no modifications' '
-       ten="0 1 2 3 4 5 6 7 8 9" &&
-       for i in $ten
-       do
-               echo line $i in a sample file
-       done >one &&
-       for i in $ten
-       do
-               echo line $i in another sample file
-       done >two &&
-       git add one two &&
-       test_tick && git commit -m initial &&
-
-       git branch L1 &&
-       git checkout -b R1 &&
-       git mv one three &&
-       test_tick && git commit -m R1 &&
-
-       git checkout L1 &&
-       git mv two three &&
-       test_tick && git commit -m L1 &&
-
-       git checkout L1^0 &&
-       test_tick && git merge -s ours R1 &&
-       git tag L2 &&
-
-       git checkout R1^0 &&
-       test_tick && git merge -s ours L1 &&
-       git tag R2
+       test_create_repo basic-rename &&
+       (
+               cd basic-rename &&
+
+               ten="0 1 2 3 4 5 6 7 8 9" &&
+               for i in $ten
+               do
+                       echo line $i in a sample file
+               done >one &&
+               for i in $ten
+               do
+                       echo line $i in another sample file
+               done >two &&
+               git add one two &&
+               test_tick && git commit -m initial &&
+
+               git branch L1 &&
+               git checkout -b R1 &&
+               git mv one three &&
+               test_tick && git commit -m R1 &&
+
+               git checkout L1 &&
+               git mv two three &&
+               test_tick && git commit -m L1 &&
+
+               git checkout L1^0 &&
+               test_tick && git merge -s ours R1 &&
+               git tag L2 &&
+
+               git checkout R1^0 &&
+               test_tick && git merge -s ours L1 &&
+               git tag R2
+       )
 '
 
 test_expect_success 'merge simple rename+criss-cross with no modifications' '
-       git reset --hard &&
-       git checkout L2^0 &&
-
-       test_must_fail git merge -s recursive R2^0 &&
-
-       test 2 = $(git ls-files -s | wc -l) &&
-       test 2 = $(git ls-files -u | wc -l) &&
-       test 2 = $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
-       test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
-
-       test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
-       test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+       (
+               cd basic-rename &&
+
+               git reset --hard &&
+               git checkout L2^0 &&
+
+               test_must_fail git merge -s recursive R2^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 3 out &&
+
+               git rev-parse >expect       \
+                       L2:three   R2:three \
+                       L2:three   R2:three &&
+               git rev-parse   >actual     \
+                       :2:three   :3:three &&
+               git hash-object >>actual    \
+                       three~HEAD three~R2^0
+               test_cmp expect actual
+       )
 '
 
 #
@@ -81,58 +90,67 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
 #
 
 test_expect_success 'setup criss-cross + rename merges with basic modification' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       ten="0 1 2 3 4 5 6 7 8 9" &&
-       for i in $ten
-       do
-               echo line $i in a sample file
-       done >one &&
-       for i in $ten
-       do
-               echo line $i in another sample file
-       done >two &&
-       git add one two &&
-       test_tick && git commit -m initial &&
-
-       git branch L1 &&
-       git checkout -b R1 &&
-       git mv one three &&
-       echo more >>two &&
-       git add two &&
-       test_tick && git commit -m R1 &&
-
-       git checkout L1 &&
-       git mv two three &&
-       test_tick && git commit -m L1 &&
-
-       git checkout L1^0 &&
-       test_tick && git merge -s ours R1 &&
-       git tag L2 &&
-
-       git checkout R1^0 &&
-       test_tick && git merge -s ours L1 &&
-       git tag R2
+       test_create_repo rename-modify &&
+       (
+               cd rename-modify &&
+
+               ten="0 1 2 3 4 5 6 7 8 9" &&
+               for i in $ten
+               do
+                       echo line $i in a sample file
+               done >one &&
+               for i in $ten
+               do
+                       echo line $i in another sample file
+               done >two &&
+               git add one two &&
+               test_tick && git commit -m initial &&
+
+               git branch L1 &&
+               git checkout -b R1 &&
+               git mv one three &&
+               echo more >>two &&
+               git add two &&
+               test_tick && git commit -m R1 &&
+
+               git checkout L1 &&
+               git mv two three &&
+               test_tick && git commit -m L1 &&
+
+               git checkout L1^0 &&
+               test_tick && git merge -s ours R1 &&
+               git tag L2 &&
+
+               git checkout R1^0 &&
+               test_tick && git merge -s ours L1 &&
+               git tag R2
+       )
 '
 
 test_expect_success 'merge criss-cross + rename merges with basic modification' '
-       git reset --hard &&
-       git checkout L2^0 &&
-
-       test_must_fail git merge -s recursive R2^0 &&
-
-       test 2 = $(git ls-files -s | wc -l) &&
-       test 2 = $(git ls-files -u | wc -l) &&
-       test 2 = $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:three) = $(git rev-parse L2:three) &&
-       test $(git rev-parse :3:three) = $(git rev-parse R2:three) &&
-
-       test $(git rev-parse L2:three) = $(git hash-object three~HEAD) &&
-       test $(git rev-parse R2:three) = $(git hash-object three~R2^0)
+       (
+               cd rename-modify &&
+
+               git checkout L2^0 &&
+
+               test_must_fail git merge -s recursive R2^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 3 out &&
+
+               git rev-parse >expect       \
+                       L2:three   R2:three \
+                       L2:three   R2:three &&
+               git rev-parse   >actual     \
+                       :2:three   :3:three &&
+               git hash-object >>actual    \
+                       three~HEAD three~R2^0
+               test_cmp expect actual
+       )
 '
 
 #
@@ -156,64 +174,74 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
 #
 
 test_expect_success 'setup differently handled merges of rename/add conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
-       git add a &&
-       test_tick && git commit -m A &&
-
-       git branch B &&
-       git checkout -b C &&
-       echo 10 >>a &&
-       echo "other content" >>new_a &&
-       git add a new_a &&
-       test_tick && git commit -m C &&
-
-       git checkout B &&
-       git mv a new_a &&
-       test_tick && git commit -m B &&
-
-       git checkout B^0 &&
-       test_must_fail git merge C &&
-       git clean -f &&
-       test_tick && git commit -m D &&
-       git tag D &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       rm new_a~HEAD new_a &&
-       printf "Incorrectly merged content" >>new_a &&
-       git add -u &&
-       test_tick && git commit -m E &&
-       git tag E
+       test_create_repo rename-add &&
+       (
+               cd rename-add &&
+
+               printf "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" >a &&
+               git add a &&
+               test_tick && git commit -m A &&
+
+               git branch B &&
+               git checkout -b C &&
+               echo 10 >>a &&
+               echo "other content" >>new_a &&
+               git add a new_a &&
+               test_tick && git commit -m C &&
+
+               git checkout B &&
+               git mv a new_a &&
+               test_tick && git commit -m B &&
+
+               git checkout B^0 &&
+               test_must_fail git merge C &&
+               git clean -f &&
+               test_tick && git commit -m D &&
+               git tag D &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B &&
+               rm new_a~HEAD new_a &&
+               printf "Incorrectly merged content" >>new_a &&
+               git add -u &&
+               test_tick && git commit -m E &&
+               git tag E
+       )
 '
 
 test_expect_success 'git detects differently handled merges conflict' '
-       git reset --hard &&
-       git checkout D^0 &&
-
-       test_must_fail git merge -s recursive E^0 &&
-
-       test 3 = $(git ls-files -s | wc -l) &&
-       test 3 = $(git ls-files -u | wc -l) &&
-       test 0 = $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:new_a) = $(git rev-parse D:new_a) &&
-       test $(git rev-parse :3:new_a) = $(git rev-parse E:new_a) &&
-
-       git cat-file -p B:new_a >>merged &&
-       git cat-file -p C:new_a >>merge-me &&
-       >empty &&
-       test_must_fail git merge-file \
-               -L "Temporary merge branch 2" \
-               -L "" \
-               -L "Temporary merge branch 1" \
-               merged empty merge-me &&
-       sed -e "s/^\([<=>]\)/\1\1\1/" merged >merged-internal &&
-       test $(git rev-parse :1:new_a) = $(git hash-object merged-internal)
+       (
+               cd rename-add &&
+
+               git checkout D^0 &&
+
+               test_must_fail git merge -s recursive E^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect       \
+                       D:new_a  E:new_a &&
+               git rev-parse   >actual     \
+                       :2:new_a :3:new_a &&
+               test_cmp expect actual
+
+               git cat-file -p B:new_a >ours &&
+               git cat-file -p C:new_a >theirs &&
+               >empty &&
+               test_must_fail git merge-file \
+                       -L "Temporary merge branch 2" \
+                       -L "" \
+                       -L "Temporary merge branch 1" \
+                       ours empty theirs &&
+               sed -e "s/^\([<=>]\)/\1\1\1/" ours >expect &&
+               git cat-file -p :1:new_a >actual &&
+               test_cmp expect actual
+       )
 '
 
 #
@@ -236,67 +264,85 @@ test_expect_success 'git detects differently handled merges conflict' '
 # Merging commits D & E should result in modify/delete conflict.
 
 test_expect_success 'setup criss-cross + modify/delete resolved differently' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       echo A >file &&
-       git add file &&
-       test_tick &&
-       git commit -m A &&
-
-       git branch B &&
-       git checkout -b C &&
-       git rm file &&
-       test_tick &&
-       git commit -m C &&
-
-       git checkout B &&
-       echo B >file &&
-       git add file &&
-       test_tick &&
-       git commit -m B &&
-
-       git checkout B^0 &&
-       test_must_fail git merge C &&
-       echo B >file &&
-       git add file &&
-       test_tick &&
-       git commit -m D &&
-       git tag D &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       git rm file &&
-       test_tick &&
-       git commit -m E &&
-       git tag E
+       test_create_repo modify-delete &&
+       (
+               cd modify-delete &&
+
+               echo A >file &&
+               git add file &&
+               test_tick &&
+               git commit -m A &&
+
+               git branch B &&
+               git checkout -b C &&
+               git rm file &&
+               test_tick &&
+               git commit -m C &&
+
+               git checkout B &&
+               echo B >file &&
+               git add file &&
+               test_tick &&
+               git commit -m B &&
+
+               git checkout B^0 &&
+               test_must_fail git merge C &&
+               echo B >file &&
+               git add file &&
+               test_tick &&
+               git commit -m D &&
+               git tag D &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B &&
+               git rm file &&
+               test_tick &&
+               git commit -m E &&
+               git tag E
+       )
 '
 
 test_expect_success 'git detects conflict merging criss-cross+modify/delete' '
-       git checkout D^0 &&
+       (
+               cd modify-delete &&
+
+               git checkout D^0 &&
 
-       test_must_fail git merge -s recursive E^0 &&
+               test_must_fail git merge -s recursive E^0 &&
 
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
 
-       test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
-       test $(git rev-parse :2:file) = $(git rev-parse B:file)
+               git rev-parse >expect       \
+                       master:file  B:file &&
+               git rev-parse   >actual      \
+                       :1:file      :2:file &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'git detects conflict merging criss-cross+modify/delete, reverse direction' '
-       git reset --hard &&
-       git checkout E^0 &&
+       (
+               cd modify-delete &&
 
-       test_must_fail git merge -s recursive D^0 &&
+               git reset --hard &&
+               git checkout E^0 &&
 
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u | wc -l) &&
+               test_must_fail git merge -s recursive D^0 &&
 
-       test $(git rev-parse :1:file) = $(git rev-parse master:file) &&
-       test $(git rev-parse :3:file) = $(git rev-parse B:file)
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+
+               git rev-parse >expect       \
+                       master:file  B:file &&
+               git rev-parse   >actual      \
+                       :1:file      :3:file &&
+               test_cmp expect actual
+       )
 '
 
 #
@@ -336,120 +382,164 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
 #
 
 test_expect_success 'setup differently handled merges of directory/file conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       >ignore-me &&
-       git add ignore-me &&
-       test_tick &&
-       git commit -m A &&
-       git tag A &&
-
-       git branch B &&
-       git checkout -b C &&
-       mkdir a &&
-       echo 10 >a/file &&
-       git add a/file &&
-       test_tick &&
-       git commit -m C &&
-
-       git checkout B &&
-       echo 5 >a &&
-       git add a &&
-       test_tick &&
-       git commit -m B &&
-
-       git checkout B^0 &&
-       test_must_fail git merge C &&
-       git clean -f &&
-       rm -rf a/ &&
-       echo 5 >a &&
-       git add a &&
-       test_tick &&
-       git commit -m D &&
-       git tag D &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       git clean -f &&
-       git rm --cached a &&
-       echo 10 >a/file &&
-       git add a/file &&
-       test_tick &&
-       git commit -m E1 &&
-       git tag E1 &&
-
-       git checkout C^0 &&
-       test_must_fail git merge B &&
-       git clean -f &&
-       git rm --cached a &&
-       printf "10\n11\n" >a/file &&
-       git add a/file &&
-       test_tick &&
-       git commit -m E2 &&
-       git tag E2
+       test_create_repo directory-file &&
+       (
+               cd directory-file &&
+
+               >ignore-me &&
+               git add ignore-me &&
+               test_tick &&
+               git commit -m A &&
+               git tag A &&
+
+               git branch B &&
+               git checkout -b C &&
+               mkdir a &&
+               echo 10 >a/file &&
+               git add a/file &&
+               test_tick &&
+               git commit -m C &&
+
+               git checkout B &&
+               echo 5 >a &&
+               git add a &&
+               test_tick &&
+               git commit -m B &&
+
+               git checkout B^0 &&
+               test_must_fail git merge C &&
+               git clean -f &&
+               rm -rf a/ &&
+               echo 5 >a &&
+               git add a &&
+               test_tick &&
+               git commit -m D &&
+               git tag D &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B &&
+               git clean -f &&
+               git rm --cached a &&
+               echo 10 >a/file &&
+               git add a/file &&
+               test_tick &&
+               git commit -m E1 &&
+               git tag E1 &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B &&
+               git clean -f &&
+               git rm --cached a &&
+               printf "10\n11\n" >a/file &&
+               git add a/file &&
+               test_tick &&
+               git commit -m E2 &&
+               git tag E2
+       )
 '
 
 test_expect_success 'merge of D & E1 fails but has appropriate contents' '
-       get_clean_checkout D^0 &&
-
-       test_must_fail git merge -s recursive E1^0 &&
-
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 1 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
-       test $(git rev-parse :2:a) = $(git rev-parse B:a)
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout D^0 &&
+
+               test_must_fail git merge -s recursive E1^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 1 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect    \
+                       A:ignore-me  B:a &&
+               git rev-parse   >actual   \
+                       :0:ignore-me :2:a &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'merge of E1 & D fails but has appropriate contents' '
-       get_clean_checkout E1^0 &&
-
-       test_must_fail git merge -s recursive D^0 &&
-
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 1 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
-       test $(git rev-parse :3:a) = $(git rev-parse B:a)
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout E1^0 &&
+
+               test_must_fail git merge -s recursive D^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 1 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect    \
+                       A:ignore-me  B:a &&
+               git rev-parse   >actual   \
+                       :0:ignore-me :3:a &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'merge of D & E2 fails but has appropriate contents' '
-       get_clean_checkout D^0 &&
-
-       test_must_fail git merge -s recursive E2^0 &&
-
-       test 4 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :2:a) = $(git rev-parse B:a) &&
-       test $(git rev-parse :3:a/file) = $(git rev-parse E2:a/file) &&
-       test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
-       test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
-
-       test -f a~HEAD
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout D^0 &&
+
+               test_must_fail git merge -s recursive E2^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 2 out &&
+
+               git rev-parse >expect    \
+                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+               git rev-parse   >actual   \
+                       :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
+               test_cmp expect actual
+
+               test_path_is_file a~HEAD
+       )
 '
 
 test_expect_success 'merge of E2 & D fails but has appropriate contents' '
-       get_clean_checkout E2^0 &&
-
-       test_must_fail git merge -s recursive D^0 &&
-
-       test 4 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :3:a) = $(git rev-parse B:a) &&
-       test $(git rev-parse :2:a/file) = $(git rev-parse E2:a/file) &&
-       test $(git rev-parse :1:a/file) = $(git rev-parse C:a/file) &&
-       test $(git rev-parse :0:ignore-me) = $(git rev-parse A:ignore-me) &&
-
-       test -f a~D^0
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout E2^0 &&
+
+               test_must_fail git merge -s recursive D^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 2 out &&
+
+               git rev-parse >expect    \
+                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+               git rev-parse   >actual   \
+                       :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
+               test_cmp expect actual
+
+               test_path_is_file a~D^0
+       )
 '
 
 #
@@ -492,52 +582,58 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
 # but that may cancel out at the final merge stage".
 
 test_expect_success 'setup rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
-       git reset --hard &&
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n6\n" >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       echo 7 >>b &&
-       git add -u &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       git commit -m C &&
-
-       git checkout -q B^0 &&
-       git merge --no-commit -s ours C^0 &&
-       git mv b newname &&
-       git commit -m "Merge commit C^0 into HEAD" &&
-       git tag D &&
-
-       git checkout -q C^0 &&
-       git merge --no-commit -s ours B^0 &&
-       git mv c newname &&
-       printf "7\n8\n" >>newname &&
-       git add -u &&
-       git commit -m "Merge commit B^0 into HEAD" &&
-       git tag E
+       test_create_repo rename-squared-squared &&
+       (
+               cd rename-squared-squared &&
+
+               printf "1\n2\n3\n4\n5\n6\n" >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               echo 7 >>b &&
+               git add -u &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               git commit -m C &&
+
+               git checkout -q B^0 &&
+               git merge --no-commit -s ours C^0 &&
+               git mv b newname &&
+               git commit -m "Merge commit C^0 into HEAD" &&
+               git tag D &&
+
+               git checkout -q C^0 &&
+               git merge --no-commit -s ours B^0 &&
+               git mv c newname &&
+               printf "7\n8\n" >>newname &&
+               git add -u &&
+               git commit -m "Merge commit B^0 into HEAD" &&
+               git tag E
+       )
 '
 
 test_expect_success 'handle rename/rename(1to2)/modify followed by what looks like rename/rename(2to1)/modify' '
-       git checkout D^0 &&
+       (
+               cd rename-squared-squared &&
+
+               git checkout D^0 &&
 
-       git merge -s recursive E^0 &&
+               git merge -s recursive E^0 &&
 
-       test 1 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 1 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
 
-       test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+               test $(git rev-parse HEAD:newname) = $(git rev-parse E:newname)
+       )
 '
 
 #
@@ -562,59 +658,72 @@ test_expect_success 'handle rename/rename(1to2)/modify followed by what looks li
 # renaming carefully (both in the virtual merge base and later), and getting
 # content merge handled.
 
-test_expect_success 'setup criss-cross + rename/rename/add + modify/modify' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "lots\nof\nwords\nand\ncontent\n" >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       printf "2\n3\n4\n5\n6\n7\n" >a &&
-       git add a &&
-       git commit -m C &&
-
-       git checkout B^0 &&
-       git merge --no-commit -s ours C^0 &&
-       git checkout C -- a c &&
-       mv a old_a &&
-       echo 1 >a &&
-       cat old_a >>a &&
-       rm old_a &&
-       git add -u &&
-       git commit -m "Merge commit C^0 into HEAD" &&
-       git tag D &&
-
-       git checkout C^0 &&
-       git merge --no-commit -s ours B^0 &&
-       git checkout B -- b &&
-       echo 8 >>a &&
-       git add -u &&
-       git commit -m "Merge commit B^0 into HEAD" &&
-       git tag E
+test_expect_success 'setup criss-cross + rename/rename/add-source + modify/modify' '
+       test_create_repo rename-rename-add-source &&
+       (
+               cd rename-rename-add-source &&
+
+               printf "lots\nof\nwords\nand\ncontent\n" >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               printf "2\n3\n4\n5\n6\n7\n" >a &&
+               git add a &&
+               git commit -m C &&
+
+               git checkout B^0 &&
+               git merge --no-commit -s ours C^0 &&
+               git checkout C -- a c &&
+               mv a old_a &&
+               echo 1 >a &&
+               cat old_a >>a &&
+               rm old_a &&
+               git add -u &&
+               git commit -m "Merge commit C^0 into HEAD" &&
+               git tag D &&
+
+               git checkout C^0 &&
+               git merge --no-commit -s ours B^0 &&
+               git checkout B -- b &&
+               echo 8 >>a &&
+               git add -u &&
+               git commit -m "Merge commit B^0 into HEAD" &&
+               git tag E
+       )
 '
 
 test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
-       git checkout D^0 &&
-
-       git merge -s recursive E^0 &&
-
-       test 3 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
-       test $(git rev-parse HEAD:c) = $(git rev-parse A:a) &&
-       test "$(cat a)" = "$(printf "1\n2\n3\n4\n5\n6\n7\n8\n")"
+       (
+               cd rename-rename-add-source &&
+
+               git checkout D^0 &&
+
+               git merge -s recursive E^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               printf "1\n2\n3\n4\n5\n6\n7\n8\n" >correct &&
+               git rev-parse >expect \
+                       A:a   A:a     \
+                       correct       &&
+               git rev-parse   >actual  \
+                       :0:b  :0:c       &&
+               git hash-object >>actual \
+                       a                &&
+               test_cmp expect actual
+       )
 '
 
 #
@@ -638,52 +747,62 @@ test_expect_failure 'detect rename/rename/add-source for virtual merge-base' '
 # base of B & C needs to not delete B:c for that to work, though...
 
 test_expect_success 'setup criss-cross+rename/rename/add-dest + simple modify' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
-       git add c &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       git commit -m C &&
-
-       git checkout B^0 &&
-       git merge --no-commit -s ours C^0 &&
-       git mv b a &&
-       git commit -m "D is like B but renames b back to a" &&
-       git tag D &&
-
-       git checkout B^0 &&
-       git merge --no-commit -s ours C^0 &&
-       git mv b a &&
-       echo 8 >>c &&
-       git add c &&
-       git commit -m "E like D but has mod in c" &&
-       git tag E
+       test_create_repo rename-rename-add-dest &&
+       (
+               cd rename-rename-add-dest &&
+
+               >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               printf "1\n2\n3\n4\n5\n6\n7\n" >c &&
+               git add c &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               git commit -m C &&
+
+               git checkout B^0 &&
+               git merge --no-commit -s ours C^0 &&
+               git mv b a &&
+               git commit -m "D is like B but renames b back to a" &&
+               git tag D &&
+
+               git checkout B^0 &&
+               git merge --no-commit -s ours C^0 &&
+               git mv b a &&
+               echo 8 >>c &&
+               git add c &&
+               git commit -m "E like D but has mod in c" &&
+               git tag E
+       )
 '
 
 test_expect_success 'virtual merge base handles rename/rename(1to2)/add-dest' '
-       git checkout D^0 &&
-
-       git merge -s recursive E^0 &&
-
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse HEAD:a) = $(git rev-parse A:a) &&
-       test $(git rev-parse HEAD:c) = $(git rev-parse E:c)
+       (
+               cd rename-rename-add-dest &&
+
+               git checkout D^0 &&
+
+               git merge -s recursive E^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect \
+                       A:a   E:c     &&
+               git rev-parse   >actual \
+                       :0:a  :0:c      &&
+               test_cmp expect actual
+       )
 '
 
 test_done
index 411550d2b6e9085c5e7f93c25effec7b130f0401..1cbd946fc2eb9ab3dcad83c811abb6d18448fc4e 100755 (executable)
@@ -6,31 +6,40 @@ test_description="recursive merge corner cases w/ renames but not criss-crosses"
 . ./test-lib.sh
 
 test_expect_success 'setup rename/delete + untracked file' '
-       echo "A pretty inscription" >ring &&
-       git add ring &&
-       test_tick &&
-       git commit -m beginning &&
-
-       git branch people &&
-       git checkout -b rename-the-ring &&
-       git mv ring one-ring-to-rule-them-all &&
-       test_tick &&
-       git commit -m fullname &&
-
-       git checkout people &&
-       git rm ring &&
-       echo gollum >owner &&
-       git add owner &&
-       test_tick &&
-       git commit -m track-people-instead-of-objects &&
-       echo "Myyy PRECIOUSSS" >ring
+       test_create_repo rename-delete-untracked &&
+       (
+               cd rename-delete-untracked &&
+
+               echo "A pretty inscription" >ring &&
+               git add ring &&
+               test_tick &&
+               git commit -m beginning &&
+
+               git branch people &&
+               git checkout -b rename-the-ring &&
+               git mv ring one-ring-to-rule-them-all &&
+               test_tick &&
+               git commit -m fullname &&
+
+               git checkout people &&
+               git rm ring &&
+               echo gollum >owner &&
+               git add owner &&
+               test_tick &&
+               git commit -m track-people-instead-of-objects &&
+               echo "Myyy PRECIOUSSS" >ring
+       )
 '
 
 test_expect_success "Does git preserve Gollum's precious artifact?" '
-       test_must_fail git merge -s recursive rename-the-ring &&
+       (
+               cd rename-delete-untracked &&
 
-       # Make sure git did not delete an untracked file
-       test -f ring
+               test_must_fail git merge -s recursive rename-the-ring &&
+
+               # Make sure git did not delete an untracked file
+               test_path_is_file ring
+       )
 '
 
 # Testcase setup for rename/modify/add-source:
@@ -41,96 +50,125 @@ test_expect_success "Does git preserve Gollum's precious artifact?" '
 # We should be able to merge B & C cleanly
 
 test_expect_success 'setup rename/modify/add-source conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       echo 8 >>a &&
-       git add a &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a b &&
-       echo something completely different >a &&
-       git add a &&
-       git commit -m C
+       test_create_repo rename-modify-add-source &&
+       (
+               cd rename-modify-add-source &&
+
+               printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               echo 8 >>a &&
+               git add a &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a b &&
+               echo something completely different >a &&
+               git add a &&
+               git commit -m C
+       )
 '
 
 test_expect_failure 'rename/modify/add-source conflict resolvable' '
-       git checkout B^0 &&
+       (
+               cd rename-modify-add-source &&
+
+               git checkout B^0 &&
 
-       git merge -s recursive C^0 &&
+               git merge -s recursive C^0 &&
 
-       test $(git rev-parse B:a) = $(git rev-parse b) &&
-       test $(git rev-parse C:a) = $(git rev-parse a)
+               git rev-parse >expect \
+                       B:a   C:a     &&
+               git rev-parse >actual \
+                       b     c       &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'setup resolvable conflict missed if rename missed' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n" >a &&
-       echo foo >b &&
-       git add a b &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a c &&
-       echo "Completely different content" >a &&
-       git add a &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       echo 6 >>a &&
-       git add a &&
-       git commit -m C
+       test_create_repo break-detection-1 &&
+       (
+               cd break-detection-1 &&
+
+               printf "1\n2\n3\n4\n5\n" >a &&
+               echo foo >b &&
+               git add a b &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a c &&
+               echo "Completely different content" >a &&
+               git add a &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               echo 6 >>a &&
+               git add a &&
+               git commit -m C
+       )
 '
 
 test_expect_failure 'conflict caused if rename not detected' '
-       git checkout -q C^0 &&
-       git merge -s recursive B^0 &&
-
-       test 3 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test_line_count = 6 c &&
-       test $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&
-       test $(git rev-parse HEAD:b) = $(git rev-parse A:b)
+       (
+               cd break-detection-1 &&
+
+               git checkout -q C^0 &&
+               git merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               test_line_count = 6 c &&
+               git rev-parse >expect \
+                       B:a   A:b     &&
+               git rev-parse >actual \
+                       :0:a  :0:b    &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'setup conflict resolved wrong if rename missed' '
-       git reset --hard &&
-       git clean -f &&
-
-       git checkout -b D A &&
-       echo 7 >>a &&
-       git add a &&
-       git mv a c &&
-       echo "Completely different content" >a &&
-       git add a &&
-       git commit -m D &&
-
-       git checkout -b E A &&
-       git rm a &&
-       echo "Completely different content" >>a &&
-       git add a &&
-       git commit -m E
+       test_create_repo break-detection-2 &&
+       (
+               cd break-detection-2 &&
+
+               printf "1\n2\n3\n4\n5\n" >a &&
+               echo foo >b &&
+               git add a b &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b D A &&
+               echo 7 >>a &&
+               git add a &&
+               git mv a c &&
+               echo "Completely different content" >a &&
+               git add a &&
+               git commit -m D &&
+
+               git checkout -b E A &&
+               git rm a &&
+               echo "Completely different content" >>a &&
+               git add a &&
+               git commit -m E
+       )
 '
 
 test_expect_failure 'missed conflict if rename not detected' '
-       git checkout -q E^0 &&
-       test_must_fail git merge -s recursive D^0
+       (
+               cd break-detection-2 &&
+
+               git checkout -q E^0 &&
+               test_must_fail git merge -s recursive D^0
+       )
 '
 
 # Tests for undetected rename/add-source causing a file to erroneously be
@@ -145,198 +183,233 @@ test_expect_failure 'missed conflict if rename not detected' '
 #   Commit C: rename a->b, add unrelated a
 
 test_expect_success 'setup undetected rename/add-source causes data loss' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n" >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a b &&
-       echo foobar >a &&
-       git add a &&
-       git commit -m C
+       test_create_repo break-detection-3 &&
+       (
+               cd break-detection-3 &&
+
+               printf "1\n2\n3\n4\n5\n" >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a b &&
+               echo foobar >a &&
+               git add a &&
+               git commit -m C
+       )
 '
 
 test_expect_failure 'detect rename/add-source and preserve all data' '
-       git checkout B^0 &&
+       (
+               cd break-detection-3 &&
 
-       git merge -s recursive C^0 &&
+               git checkout B^0 &&
 
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
+               git merge -s recursive C^0 &&
 
-       test -f a &&
-       test -f b &&
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
 
-       test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
-       test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+               test_path_is_file a &&
+               test_path_is_file b &&
+
+               git rev-parse >expect \
+                       A:a   C:a     &&
+               git rev-parse >actual \
+                       :0:b  :0:a    &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_failure 'detect rename/add-source and preserve all data, merge other way' '
-       git checkout C^0 &&
+       (
+               cd break-detection-3 &&
+
+               git checkout C^0 &&
 
-       git merge -s recursive B^0 &&
+               git merge -s recursive B^0 &&
 
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
 
-       test -f a &&
-       test -f b &&
+               test_path_is_file a &&
+               test_path_is_file b &&
 
-       test $(git rev-parse HEAD:b) = $(git rev-parse A:a) &&
-       test $(git rev-parse HEAD:a) = $(git rev-parse C:a)
+               git rev-parse >expect \
+                       A:a   C:a     &&
+               git rev-parse >actual \
+                       :0:b  :0:a    &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'setup content merge + rename/directory conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n6\n" >file &&
-       git add file &&
-       test_tick &&
-       git commit -m base &&
-       git tag base &&
-
-       git checkout -b right &&
-       echo 7 >>file &&
-       mkdir newfile &&
-       echo junk >newfile/realfile &&
-       git add file newfile/realfile &&
-       test_tick &&
-       git commit -m right &&
-
-       git checkout -b left-conflict base &&
-       echo 8 >>file &&
-       git add file &&
-       git mv file newfile &&
-       test_tick &&
-       git commit -m left &&
-
-       git checkout -b left-clean base &&
-       echo 0 >newfile &&
-       cat file >>newfile &&
-       git add newfile &&
-       git rm file &&
-       test_tick &&
-       git commit -m left
+       test_create_repo rename-directory-1 &&
+       (
+               cd rename-directory-1 &&
+
+               printf "1\n2\n3\n4\n5\n6\n" >file &&
+               git add file &&
+               test_tick &&
+               git commit -m base &&
+               git tag base &&
+
+               git checkout -b right &&
+               echo 7 >>file &&
+               mkdir newfile &&
+               echo junk >newfile/realfile &&
+               git add file newfile/realfile &&
+               test_tick &&
+               git commit -m right &&
+
+               git checkout -b left-conflict base &&
+               echo 8 >>file &&
+               git add file &&
+               git mv file newfile &&
+               test_tick &&
+               git commit -m left &&
+
+               git checkout -b left-clean base &&
+               echo 0 >newfile &&
+               cat file >>newfile &&
+               git add newfile &&
+               git rm file &&
+               test_tick &&
+               git commit -m left
+       )
 '
 
 test_expect_success 'rename/directory conflict + clean content merge' '
-       git reset --hard &&
-       git reset --hard &&
-       git clean -fdqx &&
+       (
+               cd rename-directory-1 &&
 
-       git checkout left-clean^0 &&
+               git checkout left-clean^0 &&
 
-       test_must_fail git merge -s recursive right^0 &&
+               test_must_fail git merge -s recursive right^0 &&
 
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 1 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 1 out &&
+               git ls-files -o >out &&
+               test_line_count = 2 out &&
 
-       echo 0 >expect &&
-       git cat-file -p base:file >>expect &&
-       echo 7 >>expect &&
-       test_cmp expect newfile~HEAD &&
+               echo 0 >expect &&
+               git cat-file -p base:file >>expect &&
+               echo 7 >>expect &&
+               test_cmp expect newfile~HEAD &&
 
-       test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
+               test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
 
-       test -f newfile/realfile &&
-       test -f newfile~HEAD
+               test_path_is_file newfile/realfile &&
+               test_path_is_file newfile~HEAD
+       )
 '
 
 test_expect_success 'rename/directory conflict + content merge conflict' '
-       git reset --hard &&
-       git reset --hard &&
-       git clean -fdqx &&
-
-       git checkout left-conflict^0 &&
-
-       test_must_fail git merge -s recursive right^0 &&
-
-       test 4 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
-
-       git cat-file -p left-conflict:newfile >left &&
-       git cat-file -p base:file    >base &&
-       git cat-file -p right:file   >right &&
-       test_must_fail git merge-file \
-               -L "HEAD:newfile" \
-               -L "" \
-               -L "right^0:file" \
-               left base right &&
-       test_cmp left newfile~HEAD &&
-
-       test $(git rev-parse :1:newfile) = $(git rev-parse base:file) &&
-       test $(git rev-parse :2:newfile) = $(git rev-parse left-conflict:newfile) &&
-       test $(git rev-parse :3:newfile) = $(git rev-parse right:file) &&
-
-       test -f newfile/realfile &&
-       test -f newfile~HEAD
+       (
+               cd rename-directory-1 &&
+
+               git reset --hard &&
+               git reset --hard &&
+               git clean -fdqx &&
+
+               git checkout left-conflict^0 &&
+
+               test_must_fail git merge -s recursive right^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 2 out &&
+
+               git cat-file -p left-conflict:newfile >left &&
+               git cat-file -p base:file    >base &&
+               git cat-file -p right:file   >right &&
+               test_must_fail git merge-file \
+                       -L "HEAD:newfile" \
+                       -L "" \
+                       -L "right^0:file" \
+                       left base right &&
+               test_cmp left newfile~HEAD &&
+
+               git rev-parse >expect                                 \
+                       base:file   left-conflict:newfile  right:file &&
+               git rev-parse >actual                                 \
+                       :1:newfile  :2:newfile             :3:newfile &&
+               test_cmp expect actual
+
+               test_path_is_file newfile/realfile &&
+               test_path_is_file newfile~HEAD
+       )
 '
 
 test_expect_success 'setup content merge + rename/directory conflict w/ disappearing dir' '
-       git reset --hard &&
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       mkdir sub &&
-       printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
-       git add sub/file &&
-       test_tick &&
-       git commit -m base &&
-       git tag base &&
-
-       git checkout -b right &&
-       echo 7 >>sub/file &&
-       git add sub/file &&
-       test_tick &&
-       git commit -m right &&
-
-       git checkout -b left base &&
-       echo 0 >newfile &&
-       cat sub/file >>newfile &&
-       git rm sub/file &&
-       mv newfile sub &&
-       git add sub &&
-       test_tick &&
-       git commit -m left
+       test_create_repo rename-directory-2 &&
+       (
+               cd rename-directory-2 &&
+
+               mkdir sub &&
+               printf "1\n2\n3\n4\n5\n6\n" >sub/file &&
+               git add sub/file &&
+               test_tick &&
+               git commit -m base &&
+               git tag base &&
+
+               git checkout -b right &&
+               echo 7 >>sub/file &&
+               git add sub/file &&
+               test_tick &&
+               git commit -m right &&
+
+               git checkout -b left base &&
+               echo 0 >newfile &&
+               cat sub/file >>newfile &&
+               git rm sub/file &&
+               mv newfile sub &&
+               git add sub &&
+               test_tick &&
+               git commit -m left
+       )
 '
 
 test_expect_success 'disappearing dir in rename/directory conflict handled' '
-       git reset --hard &&
-       git clean -fdqx &&
+       (
+               cd rename-directory-2 &&
 
-       git checkout left^0 &&
+               git checkout left^0 &&
 
-       git merge -s recursive right^0 &&
+               git merge -s recursive right^0 &&
 
-       test 1 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 1 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
 
-       echo 0 >expect &&
-       git cat-file -p base:sub/file >>expect &&
-       echo 7 >>expect &&
-       test_cmp expect sub &&
+               echo 0 >expect &&
+               git cat-file -p base:sub/file >>expect &&
+               echo 7 >>expect &&
+               test_cmp expect sub &&
 
-       test -f sub
+               test_path_is_file sub
+       )
 '
 
 # Test for all kinds of things that can go wrong with rename/rename (2to1):
@@ -352,48 +425,59 @@ test_expect_success 'disappearing dir in rename/directory conflict handled' '
 #   * Nothing else should be present.  Is anything?
 
 test_expect_success 'setup rename/rename (2to1) + modify/modify' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n" >a &&
-       printf "5\n4\n3\n2\n1\n" >b &&
-       git add a b &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a c &&
-       echo 0 >>b &&
-       git add b &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv b c &&
-       echo 6 >>a &&
-       git add a &&
-       git commit -m C
+       test_create_repo rename-rename-2to1 &&
+       (
+               cd rename-rename-2to1 &&
+
+               printf "1\n2\n3\n4\n5\n" >a &&
+               printf "5\n4\n3\n2\n1\n" >b &&
+               git add a b &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a c &&
+               echo 0 >>b &&
+               git add b &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv b c &&
+               echo 6 >>a &&
+               git add a &&
+               git commit -m C
+       )
 '
 
 test_expect_success 'handle rename/rename (2to1) conflict correctly' '
-       git checkout B^0 &&
-
-       test_must_fail git merge -s recursive C^0 >out &&
-       test_i18ngrep "CONFLICT (rename/rename)" out &&
-
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u | wc -l) &&
-       test 2 -eq $(git ls-files -u c | wc -l) &&
-       test 3 -eq $(git ls-files -o | wc -l) &&
-
-       test ! -f a &&
-       test ! -f b &&
-       test -f c~HEAD &&
-       test -f c~C^0 &&
-
-       test $(git hash-object c~HEAD) = $(git rev-parse C:a) &&
-       test $(git hash-object c~C^0) = $(git rev-parse B:b)
+       (
+               cd rename-rename-2to1 &&
+
+               git checkout B^0 &&
+
+               test_must_fail git merge -s recursive C^0 >out &&
+               test_i18ngrep "CONFLICT (rename/rename)" out &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 2 out &&
+               git ls-files -u c >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 3 out &&
+
+               test_path_is_missing a &&
+               test_path_is_missing b &&
+               test_path_is_file c~HEAD &&
+               test_path_is_file c~C^0 &&
+
+               git rev-parse >expect   \
+                       C:a     B:b     &&
+               git hash-object >actual \
+                       c~HEAD  c~C^0   &&
+               test_cmp expect actual
+       )
 '
 
 # Testcase setup for simple rename/rename (1to2) conflict:
@@ -401,44 +485,53 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' '
 #   Commit B: rename a->b
 #   Commit C: rename a->c
 test_expect_success 'setup simple rename/rename (1to2) conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       echo stuff >a &&
-       git add a &&
-       test_tick &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       test_tick &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       test_tick &&
-       git commit -m C
+       test_create_repo rename-rename-1to2 &&
+       (
+               cd rename-rename-1to2 &&
+
+               echo stuff >a &&
+               git add a &&
+               test_tick &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               test_tick &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               test_tick &&
+               git commit -m C
+       )
 '
 
 test_expect_success 'merge has correct working tree contents' '
-       git checkout C^0 &&
-
-       test_must_fail git merge -s recursive B^0 &&
-
-       test 3 -eq $(git ls-files -s | wc -l) &&
-       test 3 -eq $(git ls-files -u | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
-       test $(git rev-parse :3:b) = $(git rev-parse A:a) &&
-       test $(git rev-parse :2:c) = $(git rev-parse A:a) &&
-
-       test ! -f a &&
-       test $(git hash-object b) = $(git rev-parse A:a) &&
-       test $(git hash-object c) = $(git rev-parse A:a)
+       (
+               cd rename-rename-1to2 &&
+
+               git checkout C^0 &&
+
+               test_must_fail git merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               test_path_is_missing a &&
+               git rev-parse >expect   \
+                       A:a   A:a   A:a \
+                       A:a   A:a       &&
+               git rev-parse >actual    \
+                       :1:a  :3:b  :2:c &&
+               git hash-object >>actual \
+                       b     c          &&
+               test_cmp expect actual
+       )
 '
 
 # Testcase setup for rename/rename(1to2)/add-source conflict:
@@ -449,130 +542,155 @@ test_expect_success 'merge has correct working tree contents' '
 # Merging of B & C should NOT be clean; there's a rename/rename conflict
 
 test_expect_success 'setup rename/rename(1to2)/add-source conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
-       git add a &&
-       git commit -m A &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       git commit -m B &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       echo something completely different >a &&
-       git add a &&
-       git commit -m C
+       test_create_repo rename-rename-1to2-add-source-1 &&
+       (
+               cd rename-rename-1to2-add-source-1 &&
+
+               printf "1\n2\n3\n4\n5\n6\n7\n" >a &&
+               git add a &&
+               git commit -m A &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               git commit -m B &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               echo something completely different >a &&
+               git add a &&
+               git commit -m C
+       )
 '
 
 test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge' '
-       git checkout B^0 &&
+       (
+               cd rename-rename-1to2-add-source-1 &&
+
+               git checkout B^0 &&
 
-       test_must_fail git merge -s recursive C^0 &&
+               test_must_fail git merge -s recursive C^0 &&
 
-       test 4 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
 
-       test $(git rev-parse 3:a) = $(git rev-parse C:a) &&
-       test $(git rev-parse 1:a) = $(git rev-parse A:a) &&
-       test $(git rev-parse 2:b) = $(git rev-parse B:b) &&
-       test $(git rev-parse 3:c) = $(git rev-parse C:c) &&
+               git rev-parse >expect         \
+                       C:a   A:a   B:b   C:C &&
+               git rev-parse >actual          \
+                       :3:a  :1:a  :2:b  :3:c &&
+               test_cmp expect actual
 
-       test -f a &&
-       test -f b &&
-       test -f c
+               test_path_is_file a &&
+               test_path_is_file b &&
+               test_path_is_file c
+       )
 '
 
 test_expect_success 'setup rename/rename(1to2)/add-source resolvable conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       >a &&
-       git add a &&
-       test_tick &&
-       git commit -m base &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       test_tick &&
-       git commit -m one &&
-
-       git checkout -b C A &&
-       git mv a b &&
-       echo important-info >a &&
-       git add a &&
-       test_tick &&
-       git commit -m two
+       test_create_repo rename-rename-1to2-add-source-2 &&
+       (
+               cd rename-rename-1to2-add-source-2 &&
+
+               >a &&
+               git add a &&
+               test_tick &&
+               git commit -m base &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               test_tick &&
+               git commit -m one &&
+
+               git checkout -b C A &&
+               git mv a b &&
+               echo important-info >a &&
+               git add a &&
+               test_tick &&
+               git commit -m two
+       )
 '
 
 test_expect_failure 'rename/rename/add-source still tracks new a file' '
-       git checkout C^0 &&
-       git merge -s recursive B^0 &&
-
-       test 2 -eq $(git ls-files -s | wc -l) &&
-       test 0 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse HEAD:a) = $(git rev-parse C:a) &&
-       test $(git rev-parse HEAD:b) = $(git rev-parse A:a)
+       (
+               cd rename-rename-1to2-add-source-2 &&
+
+               git checkout C^0 &&
+               git merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect \
+                       C:a   A:a     &&
+               git rev-parse >actual \
+                       :0:a  :0:b    &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'setup rename/rename(1to2)/add-dest conflict' '
-       git rm -rf . &&
-       git clean -fdqx &&
-       rm -rf .git &&
-       git init &&
-
-       echo stuff >a &&
-       git add a &&
-       test_tick &&
-       git commit -m base &&
-       git tag A &&
-
-       git checkout -b B A &&
-       git mv a b &&
-       echo precious-data >c &&
-       git add c &&
-       test_tick &&
-       git commit -m one &&
-
-       git checkout -b C A &&
-       git mv a c &&
-       echo important-info >b &&
-       git add b &&
-       test_tick &&
-       git commit -m two
+       test_create_repo rename-rename-1to2-add-dest &&
+       (
+               cd rename-rename-1to2-add-dest &&
+
+               echo stuff >a &&
+               git add a &&
+               test_tick &&
+               git commit -m base &&
+               git tag A &&
+
+               git checkout -b B A &&
+               git mv a b &&
+               echo precious-data >c &&
+               git add c &&
+               test_tick &&
+               git commit -m one &&
+
+               git checkout -b C A &&
+               git mv a c &&
+               echo important-info >b &&
+               git add b &&
+               test_tick &&
+               git commit -m two
+       )
 '
 
 test_expect_success 'rename/rename/add-dest merge still knows about conflicting file versions' '
-       git checkout C^0 &&
-       test_must_fail git merge -s recursive B^0 &&
-
-       test 5 -eq $(git ls-files -s | wc -l) &&
-       test 2 -eq $(git ls-files -u b | wc -l) &&
-       test 2 -eq $(git ls-files -u c | wc -l) &&
-       test 4 -eq $(git ls-files -o | wc -l) &&
-
-       test $(git rev-parse :1:a) = $(git rev-parse A:a) &&
-       test $(git rev-parse :2:b) = $(git rev-parse C:b) &&
-       test $(git rev-parse :3:b) = $(git rev-parse B:b) &&
-       test $(git rev-parse :2:c) = $(git rev-parse C:c) &&
-       test $(git rev-parse :3:c) = $(git rev-parse B:c) &&
-
-       test $(git hash-object c~HEAD) = $(git rev-parse C:c) &&
-       test $(git hash-object c~B\^0) = $(git rev-parse B:c) &&
-       test $(git hash-object b~HEAD) = $(git rev-parse C:b) &&
-       test $(git hash-object b~B\^0) = $(git rev-parse B:b) &&
-
-       test ! -f b &&
-       test ! -f c
+       (
+               cd rename-rename-1to2-add-dest &&
+
+               git checkout C^0 &&
+               test_must_fail git merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+               git ls-files -u b >out &&
+               test_line_count = 2 out &&
+               git ls-files -u c >out &&
+               test_line_count = 2 out &&
+               git ls-files -o >out &&
+               test_line_count = 5 out &&
+
+               git rev-parse >expect               \
+                       A:a   C:b   B:b   C:c   B:c &&
+               git rev-parse >actual                \
+                       :1:a  :2:b  :3:b  :2:c  :3:c &&
+               test_cmp expect actual
+
+               git rev-parse >expect               \
+                       C:c     B:c     C:b     B:b &&
+               git hash-object >actual                \
+                       c~HEAD  c~B\^0  b~HEAD  b~B\^0 &&
+               test_cmp expect actual
+
+               test_path_is_missing b &&
+               test_path_is_missing c
+       )
 '
 
 test_done
index b4b49eeb08313df8d260a17414a19e7ac2fdc901..291a1e2b07417559d3f3defd6a60043b505eb3c7 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success GPG 'verify and show signatures' '
 
 test_expect_success GPG 'detect fudged signature' '
        git cat-file tag seventh-signed >raw &&
-       sed -e "s/seventh/7th forged/" raw >forged1 &&
+       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t tag forged1 >forged1.tag &&
        test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
        grep "BAD signature from" actual1 &&
index 812db137b8db2f6f8457ccb5d6ab27e0215dbe1a..48fd14fae6e9ac04f2edac3592bfb3ee4504cab1 100755 (executable)
@@ -993,6 +993,11 @@ test_expect_success 'submodule deinit should remove the whole submodule section
        rmdir init
 '
 
+test_expect_success 'submodule deinit should unset core.worktree' '
+       test_path_is_file .git/modules/example/config &&
+       test_must_fail git config -f .git/modules/example/config core.worktree
+'
+
 test_expect_success 'submodule deinit from subdirectory' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
index 6ba5daf42ee870ce3a30c4fdb5a7cf84c18a2c84..77729ac4aa1873a7e74b59975910ca33a00899d8 100755 (executable)
@@ -82,16 +82,16 @@ test_expect_success 'test basic "submodule foreach" usage' '
 
 cat >expect <<EOF
 Entering '../sub1'
-$pwd/clone-foo1-../sub1-$sub1sha1
+$pwd/clone-foo1-sub1-../sub1-$sub1sha1
 Entering '../sub3'
-$pwd/clone-foo3-../sub3-$sub3sha1
+$pwd/clone-foo3-sub3-../sub3-$sub3sha1
 EOF
 
 test_expect_success 'test "submodule foreach" from subdirectory' '
        mkdir clone/sub &&
        (
                cd clone/sub &&
-               git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+               git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual
        ) &&
        test_i18ncmp expect actual
 '
@@ -196,6 +196,38 @@ test_expect_success 'test messages from "foreach --recursive" from subdirectory'
        ) &&
        test_i18ncmp expect actual
 '
+sub1sha1=$(cd clone2/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone2/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone2/sub3 && git rev-parse HEAD)
+nested1sha1=$(cd clone2/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone2/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone2/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone2/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+
+cat >expect <<EOF
+Entering '../nested1'
+toplevel: $pwd/clone2 name: nested1 path: nested1 displaypath: ../nested1 hash: $nested1sha1
+Entering '../nested1/nested2'
+toplevel: $pwd/clone2/nested1 name: nested2 path: nested2 displaypath: ../nested1/nested2 hash: $nested2sha1
+Entering '../nested1/nested2/nested3'
+toplevel: $pwd/clone2/nested1/nested2 name: nested3 path: nested3 displaypath: ../nested1/nested2/nested3 hash: $nested3sha1
+Entering '../nested1/nested2/nested3/submodule'
+toplevel: $pwd/clone2/nested1/nested2/nested3 name: submodule path: submodule displaypath: ../nested1/nested2/nested3/submodule hash: $submodulesha1
+Entering '../sub1'
+toplevel: $pwd/clone2 name: foo1 path: sub1 displaypath: ../sub1 hash: $sub1sha1
+Entering '../sub2'
+toplevel: $pwd/clone2 name: foo2 path: sub2 displaypath: ../sub2 hash: $sub2sha1
+Entering '../sub3'
+toplevel: $pwd/clone2 name: foo3 path: sub3 displaypath: ../sub3 hash: $sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach --recursive" from subdirectory' '
+       (
+               cd clone2/untracked &&
+               git submodule foreach --recursive "echo toplevel: \$toplevel name: \$name path: \$sm_path displaypath: \$displaypath hash: \$sha1" >../../actual
+       ) &&
+       test_i18ncmp expect actual
+'
 
 cat > expect <<EOF
 nested1-nested1
index 18a40257fbb3226a328fdd520231972c111e6265..e1f11293e2299079d59ebbaf9fa6570e2ff9190c 100755 (executable)
@@ -1099,6 +1099,7 @@ EOF
 '
 
 test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
+       test_when_finished "chmod 775 .git" &&
        (
                chmod a-w .git &&
                # make dir1/tracked stat-dirty
@@ -1108,9 +1109,6 @@ test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository'
                # make sure "status" succeeded without writing index out
                git diff-files | grep dir1/tracked
        )
-       status=$?
-       chmod 775 .git
-       (exit $status)
 '
 
 (cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm
index 762135adea6d4e8594a077f1b52dcb50d49adc4e..6e2015ed9a199d6c6240a5fee850e72fd2fa65c3 100755 (executable)
@@ -142,10 +142,9 @@ test_expect_success GPG 'show signed commit with signature' '
 
 test_expect_success GPG 'detect fudged signature' '
        git cat-file commit seventh-signed >raw &&
-
-       sed -e "s/seventh/7th forged/" raw >forged1 &&
+       sed -e "s/^seventh/7th forged/" raw >forged1 &&
        git hash-object -w -t commit forged1 >forged1.commit &&
-       ! git verify-commit $(cat forged1.commit) &&
+       test_must_fail git verify-commit $(cat forged1.commit) &&
        git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
        grep "BAD signature from" actual1 &&
        ! grep "Good signature from" actual1
@@ -156,7 +155,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
        cat raw >forged2 &&
        echo Qwik | tr "Q" "\000" >>forged2 &&
        git hash-object -w -t commit forged2 >forged2.commit &&
-       ! git verify-commit $(cat forged2.commit) &&
+       test_must_fail git verify-commit $(cat forged2.commit) &&
        git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
        grep "BAD signature from" actual2 &&
        ! grep "Good signature from" actual2
index e797c741124ef86eae0e3a8d6a5372b0a84c91e2..e2b1df817a50a48132964124d359c7dc29a89e23 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success GPG 'create signed commits' '
        echo 3 >bar && git add bar &&
        test_tick && git commit -S -m "bad on side" &&
        git cat-file commit side-bad >raw &&
-       sed -e "s/bad/forged bad/" raw >forged &&
+       sed -e "s/^bad/forged bad/" raw >forged &&
        git hash-object -w -t commit forged >forged.commit &&
        git checkout initial &&
 
index 9c49b6c1fe24335c6bcae777d60e14cdf96fd306..5e0ad1917736591b50e65169216bd5fe80193989 100755 (executable)
@@ -215,7 +215,9 @@ test_expect_success "multi-fetch continues to work" "
        "
 
 test_expect_success "multi-fetch works off a 'clean' repository" '
-       rm -rf "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" &&
+       rm -rf "$GIT_DIR/svn" &&
+       git for-each-ref --format="option no-deref%0adelete %(refname)" refs/remotes |
+       git update-ref --stdin &&
        git reflog expire --all --expire=all &&
        mkdir "$GIT_DIR/svn" &&
        git svn multi-fetch
index a28640ce1accd604bd32fdc95fd172119f6c3741..3b3a7b66e41024b5a9b939f1c33a9996b8874a12 100755 (executable)
@@ -501,6 +501,42 @@ test_expect_success '__gitcomp - suffix' '
        EOF
 '
 
+test_expect_success '__gitcomp - ignore optional negative options' '
+       test_gitcomp "--" "--abc --def --no-one -- --no-two" <<-\EOF
+       --abc Z
+       --def Z
+       --no-one Z
+       --no-... Z
+       EOF
+'
+
+test_expect_success '__gitcomp - ignore/narrow optional negative options' '
+       test_gitcomp "--a" "--abc --abcdef --no-one -- --no-two" <<-\EOF
+       --abc Z
+       --abcdef Z
+       EOF
+'
+
+test_expect_success '__gitcomp - ignore/narrow optional negative options' '
+       test_gitcomp "--n" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       --no-... Z
+       EOF
+'
+
+test_expect_success '__gitcomp - expand all negative options' '
+       test_gitcomp "--no-" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       --no-two Z
+       EOF
+'
+
+test_expect_success '__gitcomp - expand/narrow all negative options' '
+       test_gitcomp "--no-o" "--abc --def --no-one -- --no-two" <<-\EOF
+       --no-one Z
+       EOF
+'
+
 test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
        __gitcomp "$invalid_variable_name"
 '
@@ -1398,8 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
        --ignore-other-worktrees Z
        --recurse-submodules Z
        --progress Z
-       --no-track Z
-       --no-recurse-submodules Z
+       --no-quiet Z
+       --no-... Z
        EOF
 '
 
@@ -1607,6 +1643,7 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
 test_expect_success 'completion without explicit _git_xxx function' '
        test_completion "git version --" <<-\EOF
        --build-options Z
+       --no-build-options Z
        EOF
 '
 
diff --git a/tag.c b/tag.c
index 3d37c1bd251c5f8c5eb06ede72ab57b323888709..3be7206e92063d4f4f561488e2203a5edd18760d 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -1,8 +1,10 @@
 #include "cache.h"
 #include "tag.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
+#include "alloc.h"
 #include "gpg-interface.h"
 
 const char *tag_type = "tag";
@@ -93,7 +95,8 @@ struct tag *lookup_tag(const struct object_id *oid)
 {
        struct object *obj = lookup_object(oid->hash);
        if (!obj)
-               return create_object(oid->hash, alloc_tag_node());
+               return create_object(the_repository, oid->hash,
+                                    alloc_tag_node(the_repository));
        return object_as_type(obj, OBJ_TAG, 0);
 }
 
@@ -114,6 +117,14 @@ static timestamp_t parse_tag_date(const char *buf, const char *tail)
        return parse_timestamp(dateptr, NULL, 10);
 }
 
+void release_tag_memory(struct tag *t)
+{
+       free(t->tag);
+       t->tagged = NULL;
+       t->object.parsed = 0;
+       t->date = 0;
+}
+
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
 {
        struct object_id oid;
diff --git a/tag.h b/tag.h
index d469534e82a87b651abe752469d0547c2b560e10..9057d76a50639d65aae1340b752d2daa7dc67aa9 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -15,6 +15,7 @@ struct tag {
 extern struct tag *lookup_tag(const struct object_id *oid);
 extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size);
 extern int parse_tag(struct tag *item);
+extern void release_tag_memory(struct tag *t);
 extern struct object *deref_tag(struct object *, const char *, int);
 extern struct object *deref_tag_noverify(struct object *);
 extern int gpg_verify_tag(const struct object_id *oid,
index 8f5090862b5193db135f18854974b21e75eb8ae4..ecd6e53b8541408add0de076b01932a1ffd631e9 100644 (file)
@@ -2,6 +2,7 @@
 #include "tree-walk.h"
 #include "unpack-trees.h"
 #include "dir.h"
+#include "object-store.h"
 #include "tree.h"
 #include "pathspec.h"
 
diff --git a/tree.c b/tree.c
index 244eb5e665e931a6b735366d74b5ed2bcbce4c9b..bc7e99020d96eee95b099e3f276aee9ca852c57b 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -2,9 +2,11 @@
 #include "cache.h"
 #include "cache-tree.h"
 #include "tree.h"
+#include "object-store.h"
 #include "blob.h"
 #include "commit.h"
 #include "tag.h"
+#include "alloc.h"
 #include "tree-walk.h"
 
 const char *tree_type = "tree";
@@ -196,7 +198,8 @@ struct tree *lookup_tree(const struct object_id *oid)
 {
        struct object *obj = lookup_object(oid->hash);
        if (!obj)
-               return create_object(oid->hash, alloc_tree_node());
+               return create_object(the_repository, oid->hash,
+                                    alloc_tree_node(the_repository));
        return object_as_type(obj, OBJ_TREE, 0);
 }
 
index 3a85a02a7733aeeee86da1a324319d026c32d787..66741130aeddaec7a913264fb41ef82d87d9b504 100644 (file)
@@ -16,6 +16,7 @@
 #include "submodule.h"
 #include "submodule-config.h"
 #include "fsmonitor.h"
+#include "object-store.h"
 #include "fetch-object.h"
 
 /*
index 87c6722ea58207a9b369e75f7cf7ac16a705330b..936acabbdd1b171e5183e3f25a3624d0ca74049e 100644 (file)
@@ -3,6 +3,7 @@
 #include "refs.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "object-store.h"
 #include "tag.h"
 #include "object.h"
 #include "commit.h"
@@ -658,7 +659,7 @@ static void send_shallow(struct commit_list *result)
                if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
                        packet_write_fmt(1, "shallow %s",
                                         oid_to_hex(&object->oid));
-                       register_shallow(&object->oid);
+                       register_shallow(the_repository, &object->oid);
                        shallow_nr++;
                }
                result = result->next;
@@ -695,14 +696,14 @@ static void send_unshallow(const struct object_array *shallows)
                        add_object_array(object, NULL, &extra_edge_obj);
                }
                /* make sure commit traversal conforms to client */
-               register_shallow(&object->oid);
+               register_shallow(the_repository, &object->oid);
        }
 }
 
 static void deepen(int depth, int deepen_relative,
                   struct object_array *shallows)
 {
-       if (depth == INFINITE_DEPTH && !is_repository_shallow()) {
+       if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
                int i;
 
                for (i = 0; i < shallows->nr; i++) {
@@ -782,7 +783,8 @@ static int send_shallow_list(int depth, int deepen_rev_list,
                if (shallows->nr > 0) {
                        int i;
                        for (i = 0; i < shallows->nr; i++)
-                               register_shallow(&shallows->objects[i].item->oid);
+                               register_shallow(the_repository,
+                                                &shallows->objects[i].item->oid);
                }
        }
 
@@ -1356,14 +1358,15 @@ static void send_shallow_info(struct upload_pack_data *data)
 {
        /* No shallow info needs to be sent */
        if (!data->depth && !data->deepen_rev_list && !data->shallows.nr &&
-           !is_repository_shallow())
+           !is_repository_shallow(the_repository))
                return;
 
        packet_write_fmt(1, "shallow-info\n");
 
        if (!send_shallow_list(data->depth, data->deepen_rev_list,
                               data->deepen_since, &data->deepen_not,
-                              &data->shallows) && is_repository_shallow())
+                              &data->shallows) &&
+           is_repository_shallow(the_repository))
                deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows);
 
        packet_delim(1);
index 0b162a09b95a3eadeef47f3e6d314d386215cc9f..86359ab0ab62990cf7097b0d59940f1f0b04fec5 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "walker.h"
+#include "object-store.h"
 #include "commit.h"
 #include "tree.h"
 #include "tree-walk.h"
index d1c05145a4bded06a7fccc60543193d2f5181953..8827a256d32925ea24d51ccf00f3108154d01757 100644 (file)
@@ -1317,7 +1317,7 @@ static void show_rebase_in_progress(struct wt_status *s,
                        status_printf_ln(s, color,
                                _("  (use \"git rebase --abort\" to check out the original branch)"));
                }
-       } else if (state->rebase_in_progress || !stat(git_path_merge_msg(), &st)) {
+       } else if (state->rebase_in_progress || !stat(git_path_merge_msg(the_repository), &st)) {
                print_rebase_state(s, state, color);
                if (s->hints)
                        status_printf_ln(s, color,
@@ -1552,17 +1552,17 @@ void wt_status_get_state(struct wt_status_state *state,
        struct stat st;
        struct object_id oid;
 
-       if (!stat(git_path_merge_head(), &st)) {
+       if (!stat(git_path_merge_head(the_repository), &st)) {
                state->merge_in_progress = 1;
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
-       } else if (!stat(git_path_cherry_pick_head(), &st) &&
+       } else if (!stat(git_path_cherry_pick_head(the_repository), &st) &&
                        !get_oid("CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
                oidcpy(&state->cherry_pick_head_oid, &oid);
        }
        wt_status_check_bisect(NULL, state);
-       if (!stat(git_path_revert_head(), &st) &&
+       if (!stat(git_path_revert_head(the_repository), &st) &&
            !get_oid("REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
                oidcpy(&state->revert_head_oid, &oid);
index 9315bc0ede11ba0377e27d711e37b6a0ae555c43..ec6e574e4aa07414b9a17bb99ddee26fd44497de 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "config.h"
+#include "object-store.h"
 #include "xdiff-interface.h"
 #include "xdiff/xtypes.h"
 #include "xdiff/xdiffi.h"